CDS view with parameters and aggregate function - cds

I'm doing a self-learning on CDS views and I'm having trouble with aggregate function.
Here is my code:
define view ZACT02B_MAR
as select from cosp as vp
left outer join prps as vd
on vp.objnr = vd.pspnr {
key vp.objnr as Object_No,
lednr as Ledger,
gjahr as Fiscal_Year,
wrttp as Value_Type,
versn as Version,
kstar as Cost_Element,
hrkft as CO_Key_SubNo,
coalesce( wtg001 , 0.00 ) as Total_Value,
coalesce( wtg002 , 0.00 ) as Total_Value_2
}
Now, I'm trying to apply an aggregate function which is aggregate sum of WTG and parameters GJAHR and VERSN but encountering errors.
define view ZACT03_MAR
with parameters p_gjahr : gjahr,
p_versn : versn
as select from cosp {
key gjahr,
key versn,
lednr as Ledger,
gjahr as Fiscal_Year,
wrttp as Value_Type,
versn as Version,
kstar as Cost_Element,
hrkft as CO_Key_SubNo,
coalesce( wtg001 , 0.00 ) as Total_Value,
sum( wtg001 ) as Sum_WTG001,
sum( wtg002 ) as Sum_WTG002
}
group by gjahr
//where gjahr = $parameters.p_gjahr and versn = $parameters.p_versn
Is is possible to implement parameters and aggregate at the same time? thank you

Using parameters and aggregations is possible in ABAP CDS.
I think you just have to change the order of the group by and the where clause. Also, in ABAP CDS, the group by statement must contain all selected fields in the cds-view except from the aggregations.
I made a simple example on the Table spfli that counts all entries depending on a parameter for the filed carrid. Note that I would get the same result with group by carrid having p_carrid = :carrid.
define view Z_Tester
with parameters p_carrid: abap.char(2)
as select from spfli
{
carrid,
count(*) as count_carrid
}
where carrid = :p_carrid
group by carrid

Related

How to use RelBuilder.pivot() method for a query without an alias passed for Pivot expression values?

https://github.com/apache/calcite/blob/master/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
Here in RelBuilder class documentation for pivot() method provided example is below.
for SQL ::
SELECT * FROM (SELECT mgr, deptno, job, sal FROM emp) PIVOT (SUM(sal) AS ss, COUNT(*) AS c FOR (job, deptno) IN (('CLERK', 10) AS c10, ('MANAGER', 20) AS m20))
RelBuilder.pivot(groupKey, aggCalls, axes, valueMap.build().entrySet())
can be used to create a RelNode object. Here we pass a Map(alias, pivot_column_values) object as "values" parameter.
final Function<RelBuilder, RelNode> f = b -> b.scan("emp")
.pivot(b.groupKey("MGR"), Arrays.asList(b.sum(b.field("SAL")).as("SS"), b.count().as("C")),
b.fields(Arrays.asList("JOB", "DEPTNO")),
ImmutableMap.<String, List<RexNode>>builder()
.put("C10", Arrays.asList(b.literal("CLERK"), b.literal(10)))
.put("M20", Arrays.asList(b.literal("MANAGER"), b.literal(20))).build().entrySet())
.build();
How to pass "values" parameter when we don't have alias for pivot column values.
e.g. how to pass "values" parameter of RelBuilder.pivot() method for below query
SELECT * FROM (SELECT mgr, deptno, job, sal FROM emp) PIVOT (SUM(sal) AS ss, COUNT(*) AS c FOR (job, deptno) IN (('CLERK', 10), ('MANAGER', 20)))
Also is it compulsory to have at least one entry in values Map?

Power Bi Compare two tables and get values that do not matched criteria

i have 2 tables and, i would like to check if table 1 (Type_Sorting) == (CCSClassCode_Type) is matched with table 2 (_Type Sorting) == (_CCS Class Type):
for example, you can see vi got the wrong value in table 1 (CCSClassCode_Type)
and, the right value is XLBas you can see in table 2 (_CCS Class Type) not ULM,
the idea of table 2 to check if people type the right values, Please not that table 2 (_CCS Class Type) have duplicate values
thank you in advance :)
You can calculate this like that:
Table 2 =
Var trt =
SELECTCOLUMNS(Table_2, "XX"
, COMBINEVALUES(",",Table_2[_CCS Class Type],Table_2[_Type Sorting]))
return
SUMMARIZECOLUMNS(Table_1[Column1]
, Table_1[CCSClassCode_Type]
, Table_1[Type_Sorting]
, FILTER(ALL(Table_1[CCSClassCode_Type],Table_1[Type_Sorting]), not( COMBINEVALUES(",",Table_1[CCSClassCode_Type],Table_1[Type_Sorting])
in trt )
))

Conditional itab grouping alike HAVING clause in SQL

This is a follow-up question for this one but instead of aggregation I want to process groups based on some condition and cannot figure out the proper syntax for this. I need to exclude groups which contain documents with status "deleted", if at least one of the members of the group has this status.
I tried so far GROUP...WITHOUT MEMBERS, LOOP...FOR GROUPS, REDUCE and this is the solution I end up with
DATA(lt_valid_doc) = VALUE tt_struct(
FOR ls_valid IN VALUE tt_struct(
FOR GROUPS <group_key> OF <wa> IN lt_ilot
GROUP BY ( guid = <wa>-guid guid2 = <wa>-guid2 ) ASCENDING
LET not_deleted = REDUCE #( INIT valid TYPE t_ref_s_struct
FOR <m> IN GROUP <group_key>
NEXT valid = COND #(
WHEN valid IS NOT BOUND OR <m>-stat = 'I1040'
THEN REF #( <m> ) ELSE valid ) )
IN ( not_deleted->* ) )
WHERE ( status NE 'I1040' )
( ls_valid ) ).
 
However, this solution seems redundant to me (I1040 filter indicated twice). Is there any syntax that allows doing this in one statement (REDUCE, GROUP or whatever) without constructing nested table on-the-fly and filtering it like I am doing now?
If I use WHERE condition on all of the above statements (GROUP...WITHOUT MEMBERS, LOOP...FOR GROUPS and REDUCE) it only filters base lines for grouping not the groups itself. I need somewhat similar to HAVING in SQL.
UPDATE OK, here is real-life compilable example based on BSEG table. The task is find only unreveresed docs, i.e.to exclude all docs with reversed (XNEGP = true) lines.
TYPES: BEGIN OF t_s_bseg,
bukrs TYPE bseg-bukrs,
belnr TYPE bseg-belnr,
gjahr TYPE bseg-gjahr,
buzei TYPE bseg-buzei,
xnegp TYPE bseg-xnegp,
END OF t_s_bseg,
tt_bseg TYPE SORTED TABLE OF t_s_bseg WITH EMPTY KEY.
TYPES: t_ref_s_bseg TYPE REF TO t_s_bseg.
DATA(lt_valid_fi_doc) = VALUE tt_bseg(
FOR ls_valid IN VALUE tt_bseg(
FOR GROUPS <group_key> OF <wa> IN lt_bseg
GROUP BY ( bukrs = <wa>-bukrs belnr = <wa>-belnr gjahr = <wa>-belnr ) ASCENDING
LET not_reversed = REDUCE #( INIT valid TYPE t_ref_s_bseg
FOR <m> IN GROUP <group_key>
NEXT valid = COND #(
WHEN valid IS NOT BOUND OR <m>-xnegp = abap_true
THEN REF #( <m> ) ELSE valid ) )
IN ( not_reversed->* ) )
WHERE ( xnegp NE abap_true )
( ls_valid ) ).
Input lines
bukrs belnr gjahr buzei xnegp
1000 0100000001 2019 1
1000 0100000001 2019 2
1000 0100000003 2019 1
1000 0100000003 2019 2
1000 0100000004 2019 1
1000 0100000004 2019 2 X
Doc 0100000004 has reversed line so result should be
bukrs belnr gjahr buzei xnegp
1000 0100000001 2019
1000 0100000003 2019
Here's a solution which doesn't repeat the selection, but one question remains, is that really "better"?
The solution is based on generating an empty line if the group of lines contains one line with status 'I1040', instead of keeping the unwanted line. I'm not sure, but maybe another similar solution could keep the reference to the line (not_deleted), plus adding an auxiliary variable to know whether the reference is to keep or not. I found it more intuitive to use table indexes (INDEX INTO), but that might not work if tt_struct is a hashed table type.
I provide the code with an ABAP Unit Test so that you can quickly try it yourself.
CLASS ltc_main DEFINITION FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS test FOR TESTING.
METHODS cut.
TYPES : BEGIN OF ty_struct,
guid TYPE string,
stat TYPE string,
END OF ty_struct,
tt_struct TYPE STANDARD TABLE OF ty_struct WITH EMPTY KEY,
t_ref_s_struct TYPE REF TO ty_struct.
DATA: lt_ilot TYPE tt_struct,
lt_valid_doc TYPE tt_struct.
ENDCLASS.
CLASS ltc_main IMPLEMENTATION.
METHOD cut.
lt_valid_doc = VALUE #(
FOR ls_valid IN VALUE tt_struct(
FOR GROUPS <group_key> OF <wa> IN lt_ilot
GROUP BY ( guid = <wa>-guid ) ASCENDING
LET x1 = REDUCE #(
INIT x2 = 0
FOR <m> IN GROUP <group_key> INDEX INTO x3
NEXT x2 = COND #(
WHEN <m>-stat = 'I1040' THEN -1
ELSE COND #( WHEN x2 <> 0 THEN x2 ELSE x3 ) ) )
IN ( COND #( WHEN x1 <> -1 THEN lt_ilot[ x1 ] ) ) )
WHERE ( table_line IS NOT INITIAL )
( ls_valid ) ).
ENDMETHOD.
METHOD test.
lt_ilot = VALUE #(
( guid = 'A' stat = 'I1000' )
( guid = 'A' stat = 'I1040' )
( guid = 'B' stat = 'I1020' )
( guid = 'C' stat = 'I1040' )
( guid = 'D' stat = 'I1040' )
( guid = 'D' stat = 'I1000' ) ).
cut( ).
cl_abap_unit_assert=>assert_equals( act = lt_valid_doc
exp = VALUE tt_struct( ( guid = 'B' stat = 'I1020' ) ) ).
ENDMETHOD.
ENDCLASS.
I really hope that was some kind of personal test case, and you do not use this coding in a productive environment. If I would have to understand what you want to achieve looking only at the coding, I would hate you ;).
The key to easily solve this problem is sorting the table so that the delete condition is always in the first row of the group you want to process:
Solution1
with output of unique list:
DATA: lt_bseg TYPE STANDARD TABLE OF t_s_bseg.
SORT lt_bseg BY belnr xnegp DESCENDING.
DELETE ADJACENT DUPLICATES FROM lt_bseg COMPARING belnr.
DELETE lt_bseg WHERE xnegp = abap_true.
Solution2
with output of a non-unique list:
DATA: lt_bseg TYPE STANDARD TABLE OF t_s_bseg,
lf_prev_belnr TYPE belnr,
lf_delete TYPE char1.
SORT lt_bseg BY belnr xnegp DESCENDING.
LOOP AT lt_bseg ASSIGNING FIELD-SYMBOL(<ls_bseg>).
IF <ls_bseg>-belnr <> lf_prev_belnr.
lf_delete = <ls_bseg>-xnegp.
lf_prev_belnr = <ls_bseg>-belnr.
ENDIF.
IF lf_delete = abap_true.
DELETE lt_bseg.
ENDIF.
ENDLOOP.
the following solution might not be the prettiest one, but its simple. I's easier to remove a whole group if one member meets a condition than to add a whole group if all of them fail the condtition. Just an idea.
TYPES : BEGIN OF ty_struct,
guid TYPE string,
stat TYPE string,
END OF ty_struct,
tt_struct TYPE STANDARD TABLE OF ty_struct WITH EMPTY KEY.
DATA(lt_ilot) = VALUE tt_struct(
( guid = 'A' stat = 'I1000' )
( guid = 'A' stat = 'I1040' )
( guid = 'B' stat = 'I1020' )
( guid = 'C' stat = 'I1040' )
( guid = 'D' stat = 'I1040' )
( guid = 'D' stat = 'I1000' )).
LOOP AT lt_ilot INTO DATA(ls_ilot) WHERE stat = 'I1040'.
DELETE lt_ilot WHERE guid = ls_ilot-guid.
ENDLOOP.

How to remove null rows from MDX query results

How can I remove the null row from my MDX query results?
Here is the query I'm currently working with
select
non empty
{
[Measures].[Average Trips Per Day]
,[Measures].[Calories Burned]
,[Measures].[Carbon Offset]
,[Measures].[Median Distance]
,[Measures].[Median Duration]
,[Measures].[Rider Trips]
,[Measures].[Rides Per Bike Per Day]
,[Measures].[Total Distance]
,[Measures].[Total Riders]
,[Measures].[Total Trip Duration in Minutes]
,[Measures].[Total Members]
} on columns
,
non empty
{
(
[Promotion].[Promotion Code Name].children
)
} on rows
from [BCycle]
where ([Program].[Program Name].&[Madison B-cycle])
;results
This is not a null value however it is one of the children of [Promotion].[Promotion Code Name].Children.
You can exclude that particular value from children using the EXCEPT keyword of MDx.
Example query:
//This query shows the number of orders for all products,
//with the exception of Components, which are not
//sold.
SELECT
[Date].[Month of Year].Children ON COLUMNS,
Except
([Product].[Product Categories].[All].Children ,
{[Product].[Product Categories].[Components]}
) ON ROWS
FROM
[Adventure Works]
WHERE
([Measures].[Order Quantity])
Reference -> https://learn.microsoft.com/en-us/sql/mdx/except-mdx-function?view=sql-server-2017

Access the previous record to compare the value in DAX POWER BI

I need to access the previous record of the DTH_REFER_PEDID column to make the IF comparison (DTH_REFER_PEDID-1 <> "A").
That is, I'm reading the index X, I need to compare with the index X-1
Addition_Stats = VAR Atendido_OV = PR_HIST_MOVIM_PEDID[OVITEM_Hist]
VAR linha_anterior2 = CALCULATE(values(PR_HIST_MOVIM_PEDID[STA_ITEM_PEDCL]);filter(PR_HIST_MOVIM_PEDID;EARLIER(PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID])))
Return
if(PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID].[Month]<PR_HIST_MOVIM_PEDID[DAT_MAIOR_PLANE].[Month];"Atraso mês ant";
if(PR_HIST_MOVIM_PEDID[STA_ITEM_PEDCL] = "A" && PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID].[Day]<=PR_HIST_MOVIM_PEDID[DAT_MAIOR_PLANE].[Day];"Atendido no Prazo";
if((PR_HIST_MOVIM_PEDID[STA_ITEM_PEDCL]="P"||PR_HIST_MOVIM_PEDID[STA_ITEM_PEDCL]="L") && PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID].[Day]<= PR_HIST_MOVIM_PEDID[DAT_MAIOR_PLANE].[Day];"Planejado no prazo";
if(PR_HIST_MOVIM_PEDID[STA_ITEM_PEDCL]<>"A" && PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID].[Day]>PR_HIST_MOVIM_PEDID[DAT_MAIOR_PLANE].[Day];"Em atraso";
if(PR_HIST_MOVIM_PEDID[STA_ITEM_PEDCL] = "A"
&& linha_anterior2 <>"A"
&& PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID].[Day]>PR_HIST_MOVIM_PEDID[DAT_MAIOR_PLANE].[Day];"Atend fora Prazo"
;IF((PR_HIST_MOVIM_PEDID[OVITEM_Hist]=Atendido_OV)&&(PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID]>FIRSTDATE(PR_HIST_MOVIM_PEDID[DTH_REFER_PEDID].[Date]));"A retido";"NA")
)
)
)
)
)
//)
The error displayed is: A circular dependency has been detected: PR_HIST_MOVIM_PEDID [Addition_Stats].
How do I compare DTH_REFER_PEDID-1 <> "A"?
An easy way to work with previous or next records is:
Make sure your data is in a table with a primary key (=ID)
Make a query with all the fields as in your table and add one colum with ID+1. (or ID-1)
Make another query with the table and the query mentioned above and make a join between ID and ID+1 (or ID-1). Place all the fields of the table and the 1st query and you end up with all the values in 1 record. This way you can work with the previous or next values.