This question already has an answer here:
LOOP AT... GROUP BY with dynamic group key
(1 answer)
Closed 2 years ago.
I want to aggregate some specific data in a RFC module. The module takes several parameters like timestamps but also dimensions that specify the kind of aggregation.
For example I want to aggregate all contracts in a specific timerange for the several sales orgs in my system.
For this I use the GROUP BY functionality for internal tables like here:
LOOP AT ls_output-merged_data INTO ls_tmp
GROUP BY ( sales_org = ls_tmp-sales_org
group_quantity = GROUP SIZE )
ASCENDING ASSIGNING FIELD-SYMBOL(<fs_tmp_2>).
lt_tmp_dim1 = VALUE #( BASE lt_tmp_dim1 (
sales_org = <fs_tmp_2>-sales_org
group_quantity = <fs_tmp_2>-group_quantity
) ).
ENDLOOP.
The group-field may change for different cases also it should be possible to use two group-fields in order to e.g. aggregate all sales-orgs and the material they sold.
My question know is, how could I do the GROUP-BY dynamically without the need to programm every combination of group-fields manually?
Thank you very much in advance
Please check: Loop at group by dynamic group condition
I implemented a dynamic 'group by' clause by referring above link. It may also can be answer your question.
LOOP AT t_actual_data INTO DATA(actual)
GROUP BY cond string( when i_prodh_based eq abap_true THEN
|{ actual-prodh } { actual-vkorg } { actual-hl_kunnr } { actual-period }|
else
|{ actual-matnr } { actual-vkorg } { actual-hl_kunnr } { actual-period }| )
ASSIGNING FIELD-SYMBOL(<group>).
"following assignment does not give any result. So I did a loop to get group conditions
"ASSIGN COMPONENT 'PRODH' of STRUCTURE <group> to <prodh>. "no result
"get group condition values
LOOP AT GROUP <group> ASSIGNING FIELD-SYMBOL(<line_data>).
ASSIGN COMPONENT 'PRODH' of STRUCTURE <line_data> to <prodh>.
ASSIGN COMPONENT 'VKORG' of STRUCTURE <line_data> to <vkorg>.
ASSIGN COMPONENT 'MATNR' of STRUCTURE <line_data> to <matnr>.
ASSIGN COMPONENT 'HL_KUNNR' of STRUCTURE <line_data> to <tl_kunnr>.
ASSIGN COMPONENT 'PERIOD' of STRUCTURE <line_data> to <period>.
EXIT.
ENDLOOP.
ENDLOOP.
Related
Focused DB tables:
Task:
For given location ID and culture ID, get max(crop_yield.value) * culture_price.price (let's call this multiplication monetaryGain) grouped by year, so something like:
[
{
"year":2014,
"monetaryGain":...
},
{
"year":2015,
"monetaryGain":...
},
{
"year":2016,
"monetaryGain":...
},
...
]
Attempt:
SELECT cp.price * max(cy.value) AS monetaryGain, EXTRACT(YEAR FROM cy.date) AS year
FROM culture_price AS cp
JOIN culture AS c ON cp.id_culture = c.id
JOIN crop_yield AS cy ON cy.id_culture = c.id
WHERE c.id = :cultureId AND cy.id_location = :locationId AND cp.year = year
GROUP BY year
ORDER BY year
The problem:
"columns "cp.price", "cy.value" and "cy.date" must appear in the GROUP BY clause or be used in an aggregate function"
If I put these three columns in GROUP BY, I won't get expected result - It won't be grouped just by year obviously.
Does anyone have an idea on how to fix/write this query better in order to get task result?
Thanks in advance!
The fix
Rewrite monetaryGain to be:
max(cp.price * cy.value) AS monetaryGain
That way you will not be required to group by cp.price because it is not outputted as an group member, but used in aggregate.
Why?
When you write GROUP BY query you can output only columns that are in GROUP BY list and aggregate function values. Well this is expected - you expect single row per group, but you may have several distinct values for the field that is not in grouping column list.
For the same reason you can not use a non grouping column(-s) in arithmetic or any other (not aggregate) function because this would lead in several results for in single row - there would not be a way to display.
This is VERY loose explanation but I hope will help to grasp the concept.
Aliases in GROUP BY
Also you should not use aliases in GROUP BY. Use:
GROUP BY EXTRACT(YEAR FROM cy.date)
Using alias in GROUP BY is not allowed. This link might explain why: https://www.postgresql.org/message-id/7608.1259177709%40sss.pgh.pa.us
I have field Value in table finStatementTrans which is array.
How should I write select syntax with group by and sum by this field?
while select finStatementTable join DataClassParagraph,sum(Value) from finStatementTrans
group by finStatementTrans.DataClassParagraph
where finStatementTable.RecId == finStatementTrans.FinStatementTable_FK
&& finStatementTable.FinStatementTableParent_FK == 5637569094
{
info(strFmt(%1,%2",finStatementTrans.DataClassParagraph,finStatementTrans.Value[1]));
}
Is this correct?
sum(Value[1])
with this I can't compile.
As Aliaksandr Maksimau mentioned in his comment, aggregating array fields is not possible. Aggregations are only supported for integer and real data type fields.
See also X++ data selection and manipulation, paragraph select statements, last sentence.
I am currently getting a list of related field like so
List ( join_table::id_b;)'
and what i would like to do is filter that list by a second field in the same related table pseudo code as follows
List ( join_table::id_b;jointable:other="foo")
not really sure how to filter it down
The List() function will return a list of (non-empty) values from all related records.
To get a list filtered by a second field, you could do any one of the following:
Define a calculation field in the join table = If ( other = "foo" ; id_b ) and use this field in your List() function call instead of the id_b field;
Construct a relationship filtered by the other field;
Use the ExecuteSQL() function instead of List();
Write your own recursive custom function (requires the Advanced version to install).
I have two graphs that contain values for enrollment in clinical trials. Each graph has incomplete records for enrollment so I want to combine the graphs to get a more complete listing of the enrollment values.
The KMD graph should take precedence. If enrollment is present in both the KMD graph and the KCTGOV graph, use the value from KMD. If enrollment is missing in KMD, use the enrollment value from KCTGOV.
I am getting close with the query below: I bring in the enrollment values from each graph by successfully joining on the value of ?nctId. How do I then create a result for ?enrollment that is from KMD when present in that graph or comes from KCTGOV when the value is missing in KMD? The code below creates separate enrollment columns named enrollKMD and enrollKCT. I need a merge of those columns.
Suggestions greatly appreciated!
PREFIX kmd: <http://www.example.org/kmd/>
PREFIX lct: <http://data.linkedct.org/vocab/resource/>
SELECT *
FROM NAMED <http://localhost:8890/KMD>
FROM NAMED <http://localhost:8890/KCTGOV>
WHERE
{
GRAPH <http://localhost:8890/KMD>
{
?obs a kmd:Study ;
kmd:hasOrgId ?orgId .
OPTIONAL
{
?obs kmd:hasNctId ?nctIdURI .
}
OPTIONAL {?obs kmd:hasEnrollment ?enrollkmd.}
# Create STR of NCTID for merge
BIND(strafter(str(?nctIdURI), "kmd/") AS ?nctId )
}
OPTIONAL
{
GRAPH <http://localhost:8890/KCTGOV>
{
OPTIONAL{ ?govNctIdURI lct:enrollment ?enrollKCT.}
# Create STR of NCTID for merge
BIND(UCASE(strafter(str(?govNctIdURI), "trial/")) AS ?nctId )
}
}
}ORDER BY ?orgId
You can do this with an IF operation, like so:
select (if(bound(?enrollkmd), ?enrollkmd, ?enrollKCT) as ?enrollment)
where ...
The IF operator checks if ?enrollkmd is bound to a value, if so, it returns that value, otherwise it returns the value of ?enrollKCT. The outcome of the operator is then bound to the ?enrollment variable in your query result.
Of course, since you are no longer using the wildcard-select ('*'), you will now need to explicitly add all variables you want returned. So the full select-clause will become something like this:
select ?obs ?orgId ?nctId (if(bound(?enrollkmd), ?enrollkmd, ?enrollKCT) as ?enrollment)
adapt to taste.
I don't know if this is possible or not, or if my limited knowledge of MDX is pushing me in the wrong direction...
The cube I'm dealing with has two different dimensions for dates, [Statement Dates] and [Premium Dates]. Further in the hierarchy of each looks like this:
[Statement Dates].[Statement Year].[2008]
[Payment Dates].[Payment Year].[2008]
For the business logic I'm implementing I need to do something like:
select
({ [Measures].[Commission] }) on columns,
({[Products].[Product Category]}) on rows
from [Cube]
where
(
IIF( [Products].[Product Category].CurrentMember.Name = "Some Category",
[Statement Dates].[Statement Year].[2008],
[Payment Dates].[Payment Year].[2008] )
)
So I need it to discriminate what dimension to use for filtering the year based on what product category is being used.
This parses ok and the query runs, but the numbers seem to suggest that the IIF statement is always returning false.
Because the WHERE clause gets evaluated first the .CurrentMember function in the IIF will only be seeing "All Product Cateogories". In which case the [Products].[Product Category].CurrentMember.Name will never be equal to "Some Category" as the only product category in context is "All Product Cateogories".
One possible work around would be to do a calculation like the following:
WITH MEMBER Measures.Commission2 as
SUM(
{[Products].[Product Category].[Product Category].Members}
, IIF( [Products].[Product Category].CurrentMember.Name = "Some Category"
, ([Statement Dates].[Statement Year].[2008],[Measures].[Commission])
, ([Payment Dates].[Payment Year].[2008].[Measures].[Commission]) )
)
select
({ [Measures].[Commission2] }) on columns
, ({[Products].[Product Category]}) on rows
from [Cube]
You could also do a scoped assignement in the calculation script in your cube to do this sort of thing.