Conditional itab grouping alike HAVING clause in SQL - grouping

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.

Related

How to toggle condition in where clause

How can I implement below logic in where clause ?
below is the scenario.
IF #include_prime = 'YES'
Then result set should contain Promo_records along with non_prime_records
IF #include_prime = 'NO'
Then result set should contain only non_prime_records
I have tried as below, for both "Yes" and "No" selection I am getting same number of records
`DECLARE
#weeks INT = 2,
#week_ending DATE = '12/03/2022',
#include_prime CHAR(3)='YES'
BEGIN
SELECT
[PRType],
[PRName],
[PRNumber],
[PROffice],
[PRStudio],
[Manager],
[WeekEndDate]
FROM Source_table
WHERE
Source_table.[WeekEndDate] BETWEEN #week_ending AND DATEADD("WEEK",CAST(#weeks AS INT),#week_ending)
AND
( ( #include_prime = 'Yes'
OR Source_table.[PRType] = 'Prime'
)
OR
( #include_prime = 'No'
AND Source_table.[PRType] <> 'Prime'
)
END`

django find in field type json with value type int or str

I have a field type Json in a model, but I can't be sure if it's an integer or a string, the long version that works is:
cars = Car.objects.filter(
user_id=self.user_id,
car_type=self.car_type,
facture__facture_id=1,
)
if len(cars) == 0:
cars = Car.objects.filter(
user_id=self.user_id,
car_type=self.car_type,
facture__facture_id=1,
)
But I want to do not repeat all the block, and I want to know if there is another way like:
Car.objects.filter(
user_id=self.user_id,
car_type=self.car_type,
facture__facture_id=1 | facture__facture_id='1',
)
Use Q objects for "OR" logic ( with | operator) (also here)
Something like
cars = Car.objects.filter(
user_id=self.user_id,
car_type=self.car_type,
).filter(
Q( facture__facture_id=1 ) | Q( facture__facture_id='1' )
)
(except I am unsure how this uncertainty can arise. Is it a CharField or an IntegerField? I can understand needing to use Q to deal with variants of a CharField).
You can also greatly reduce duplicated code by generating one queryset from another:
cars_base_qs = Car.objects.filter(
user_id=self.user_id,
car_type=self.car_type )
...
cars = cars_base_qs.filter( ...)

Django - Using a string variable in a Q filter

I have a very similar query that works for two different types of inputs and will match on the correct column. Both queries are essentially the same except for one word, i.e. the column name.
def trade_company_group(company_group):
# can filter both name or alias as name will always contain 'Companies'
if "COMPANIES" in company_group.upper():
return (
// Same query as below except for "name", below is "alias"
Q(buy_book__entity__company_groups__name__iexact=company_group) &
Q(sell_book__entity__company_groups__name__iexact=company_group) &
(
~Q(buy_book__entity__type=ENTITY.INTERNAL) |
(
Q(buy_book__entity__primary_company_group__name__iexact=company_group) |
Q(sell_book__entity__primary_company_group__name__iexact=company_group)
)
))
return (
Q(buy_book__entity__company_groups__alias__iexact=company_group) &
Q(sell_book__entity__company_groups__alias__iexact=company_group) &
(
~Q(buy_book__entity__type=ENTITY.INTERNAL) |
(
Q(buy_book__entity__primary_company_group__alias__iexact=company_group) |
Q(sell_book__entity__primary_company_group__alias__iexact=company_group)
)
))
I don't want to duplicate code so I was hoping there was a way to substitute the column name in the query depending on my if statement.
Is this possible?
You can use a dict instead
.e.g This:
Q(buy_book__entity__type=ENTITY.INTERNAL)
Is equivalent to this:
q_filter = {"buy_book__entity__type": ENTITY.INTERNAL}
Q(**q_filter)
You can apply operations like & and | on Q objects. Here is a simple example for you to simplify the query.
query = Q(buy_book__entity__company_groups__name__iexact=company_group) & Q(sell_book__entity__company_groups__name__iexact=company_group)
if "COMPANIES" in company_group.upper():
query &= (
~Q(buy_book__entity__type=ENTITY.INTERNAL) |
(
Q(buy_book__entity__primary_company_group__name__iexact=company_group) |
Q(sell_book__entity__primary_company_group__name__iexact=company_group)
)
)
else:
query &= (
~Q(buy_book__entity__type=ENTITY.INTERNAL) |
(
Q(buy_book__entity__primary_company_group__alias__iexact=company_group) |
Q(sell_book__entity__primary_company_group__alias__iexact=company_group)
)
)

Sum of array elements in Bigquery

I have to calculate the total num of positive and (negative+null or empty values) from the table basically 2 values . I have the below query to list the negative and null and positive values .. but i want the entire count . please assist.
SELECT
ARRAY(
SELECT count(value),
FROM UNNEST(event_data_results) where REGEXP_CONTAINS(name, r'data.result.result') and ((REGEXP_CONTAINS(value, r'^-?\d+$') and SAFE_CAST(value AS INT64) <= 0 ))) AS negative_attributes,
ARRAY(
SELECT count(value) as neg_val,
FROM UNNEST(event_data_results) where value = 'null' or value='' ) AS null_attributes,
ARRAY(
SELECT count(value),
FROM UNNEST(event_data_results) where REGEXP_CONTAINS(name, r'data.result.result') and (REGEXP_CONTAINS(value, r'^-?\d+$') and SAFE_CAST(value AS INT64) > 0 )) AS positive_attributes
FROM `table` where EXISTS (SELECT 1 FROM UNNEST(event_keys) as keys , UNNEST(event_data_results) as results WHERE keys.value = "attribute")
event_keys,event_data_results , data_metrics all are repeatable struct
result should be postive : 4 negative+null :4
Below is for BigQuery Standard SQL
#standardSQL
SELECT
COUNTIF(result.value > 0) positive_attributes,
COUNTIF(result.value < 0) negative_attributes,
COUNTIF(IFNULL(result.value, 0) = 0) null_or_zero_attributes
FROM `project.dataset.table`,
UNNEST(event_data_results) AS result
WHERE EXISTS (
SELECT 1
FROM UNNEST(event_keys) AS key
WHERE key.value = "attribute"
)
you can add here whatever conditions you need
Also, if result.value is a string - you can use SAFE_CAST(result.value AS INT64) as you already do so i was not focusing on this aspect of your case

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.