Is it possible to GROUP multiple columns in END AT statement? - abap

I have internal table lt_stock with following rows:
WERKS
LGORT
MATNR
QUANTITY
I want to group WERKS LGORT and MATNR and add the QUANTITY.
I have done this using 2 loops:
LOOP AT lt_stock INTO ls_stock.
MOVE-CORRESPONDING ls_stock TO ls_stock_key.
CONCATENATE ls_stock-werks ls_stock-lgort ls_stock-matnr INTO ls_stock_key-key.
APPEND ls_stock_key TO lt_stock_key.
ENDLOOP.
LOOP AT lt_stock_key INTO ls_stock_key.
AT END OF key.
SUM.
APPEND ls_stock_key TO lt_stock_calculated.
ENDAT.
ENDLOOP.
Is it possible to do this using a single LOOP?
(Example: AT END OF werks, lgort, matnr)

Simple example based on LOOP...GROUP BY:
TYPES: BEGIN OF ty_stock,
werks TYPE werks_d,
lgort TYPE lgort_d,
matnr TYPE matnr,
qty TYPE volum,
END OF ty_stock,
tty_stock TYPE STANDARD TABLE OF ty_stock WITH NON-UNIQUE KEY primary_key COMPONENTS werks lgort matnr.
DATA: lt_input TYPE tty_stock.
DATA(out) = cl_demo_output=>new( ).
lt_input = VALUE #( ( werks = 1000 lgort = 100 matnr = '10130101' qty = 40 )
( werks = 1000 lgort = 120 matnr = '10140101' qty = 150 )
( werks = 1000 lgort = 130 matnr = '10150101' qty = 300 )
( werks = 1000 lgort = 130 matnr = '10150101' qty = 100 )
( werks = 1000 lgort = 140 matnr = '10140101' qty = 200 )
( werks = 1000 lgort = 140 matnr = '10140101' qty = 180 )
( werks = 1000 lgort = 150 matnr = '10190101' qty = 120 )
( werks = 1000 lgort = 130 matnr = '10190101' qty = 200 )
( werks = 1000 lgort = 120 matnr = '10140101' qty = 300 )
( werks = 1000 lgort = 200 matnr = '10170101' qty = 500 )
).
DATA: qty TYPE volum.
LOOP AT lt_input ASSIGNING FIELD-SYMBOL(<fs_inp>) USING KEY primary_key GROUP BY ( werks = <fs_inp>-werks lgort = <fs_inp>-lgort matnr = <fs_inp>-matnr ) REFERENCE INTO DATA(stock).
LOOP AT GROUP stock ASSIGNING FIELD-SYMBOL(<fs_member>).
qty = qty + <fs_member>-qty.
ENDLOOP.
out->write( stock->lgort && '/' && stock->matnr && ` qty: ` && qty ).
CLEAR qty.
ENDLOOP.
out->display( ).
By replacing out->write( ) with APPEND you can construct the new totals internal table instead of displaying it.

It is actually possible, but the standard way it to use the three fields (from your example) directly:
AT END OF matnr.
SUM.
...
ENDAT.
The prerequisite is that werks, lgort and matnr are the three first fields of the internal table and the table is SORTed.
If you want to do it with a separate field (like in your example), the newly defined field (key) has to be the very first field in the internal table (this is a prerequisite for LOOP ... AT ... ENDAT actions) and the table also has to be SORTed.

Related

SQL Server query to filter data by different values in the same column

I have following table:
Query 1 works fine and returns both records
select *
from dbo.PersonalAttribute
where ProductId = '12345'
Query 2-3-4-5 works fine and filters on AttributeType and corresponding AttributeDescription.
select *
from dbo.PersonalAttribute
where ProductId = '12345'
and (AttributeType = 'test1' and AttributeDescription = 0) -- will return one row
select *
from dbo.PersonalAttribute
where ProductId = '12345'
and (AttributeType = 'test1' and AttributeDescription = 1) -- will return zero rows
select *
from dbo.PersonalAttribute
where ProductId = '12345'
and (AttributeType = 'test2' and AttributeDescription = 1) -- will return one row
select *
from dbo.PersonalAttribute
where ProductId = '12345'
and (AttributeType = 'test1' and AttributeDescription = 0) -- will return zero rows
Query 6 not working and returns 0 rows even all conditions are true:
select *
from dbo.PersonalAttribute
where ProductId = '12345'
and (AttributeType = 'test1' and AttributeDescription = 0)
and (AttributeType = 'test2' and AttributeDescription = 1)
How can I rewrite Query 6 to have an ability to filter data by combination of vales in Attricute Type and Attribute Description columns, basically I need to select only when both conditions are true and if both conditions are not true no rows should be returned.
Thank you!
The "and" means you are looking at the same row for two different things. You are allowed to pick up one fruit. You cannot pick up an apple and a pear. But you can pick up an apple or a pear. Change that "and" to "or" and wrap it in parenthesis:
select *
from dbo.PersonalAttribute
where ProductId = '12345'
and ((AttributeType = 'test1' and AttributeDescription = 0)
or (AttributeType = 'test2' and AttributeDescription = 1))
One option is via a JOIN. You can create a Temp table or even a Table Variable to hold the desired combinations
select P.*
From dbo.PersonalAttribute P
Join ( values ('test1',0)
,('test2',1)
) V(AttrT,AttrD)
on AttributeType = AttrT
and AttributeDescription = AttrD
and ProductId = '12345'

I want to know a similar function for Array_contains in BigQuery. I am trying to get results for multiple values present in Array of Struct field

I have a Array of Struct field containing values like- field_name= [1,2,3,4]
I want to identify rows where field_name NOT contains values 2 OR 3 in BigQuery
Sample Data: Assume I have 4 rows of data:
row_id = 1 , field_name = [1,2,3,4]
row_id = 2 , field_name = [1,2]
row_id = 3 , field_name = [3,4]
row_id = 4 , field_name = [4]
What I want- Expected Result: row_id = 4 , field_name = [4]
I tried with UNNEST and EXISTS, UNNEST gives output as row_id = 1, row_id = 2, row_id = 3 & row_id = 4
Consider below approach
select * from your_table t
where not exists (
select value
from t.field_name as value
where value in (2,3)
)
if applied to sample data in your question - output is
if you would use where value in (1,2) - output were

Retrieve the First True Condition in a SQL Query

I'm running into a problem wherein I need to get the changes of an Employee based on specific conditions.
I should retrieve rows only based on the changes below:
Assignment Category (only based on specific Categories shown in the table xxtest)
Pay Basis (only from Hourly to Salaried and Vice Versa, also shown in the table xxtest)
Both Pay Basis and Assignment Category (based on the same constraints above)
I have created a Table (XXTEST) to restrict the SQL to only reference this mapping (See DML and DDL at the end of the Question).
Basically, the Data I'm Expecting would be:
| --------|-------------------|------------------|---------------|------------- |-------------|
| Sample | FROM_PAY_BASIS_ID | TO_PAY_BASIS_ID | FROM_CATEGORY | TO_CATEGORY | CHANGE_TYPE |
| --------| ------------------| -----------------| --------------|------------- |-------------|
| 1 | 1 | 2 | FR | FR | PAY_BASIS |
| 2 | 1 | 1 | FT | FR | ASSIGN_CAT |
| 3 | 1 | 2 | FT | FR | BOTH |
the table above is based on the conditions below:
1. if BOTH, it should get "Assignment Category and Pay Basis" as its Change_Type
2. if Assignment Category, it should get "Assignment Category" as its Change_Type
3. if Pay Basis, it should get "Pay Basis" as its Change_Type
I want it to evaluate first the condition 1, and if its false, then evaluate the next until the last.
the problem occurs when both of these columns change, resulting into having 3+ rows with CHANGE_TYPE having "PAY_BASIS", "ASSIGN_CAT", "BOTH" as values.
I've tried a lot of methods but nothing seemed to fully address my need, such as the query below:
select *
from (select coalesce((select CHANGE_TYPE
from XXTEST
where FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND FROM_ID = pax.PAY_BASIS_ID
and TO_ID = pax2.PAY_BASIS_ID),
select CHANGE_TYPE
from XXTEST
WHERE FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND FROM_ID = 0
and TO_ID = 0),
select CHANGE_TYPE
from XXTEST
WHERE FROM_CATEGORY = 'N/A'
AND TO_CATEGORY = 'N/A'
AND FROM_ID = pax.PAY_BASIS_ID
and TO_ID = pax2.PAY_BASIS_ID),
NULL ) CHANGE_TYPE FROM DUAL ) CHANGE_TYPE
, PPX.FULL_NAME
, PPX.EMPLOYEE_NUMBER
from per_people_X ppx
, per_assignments_X pax
, per_assignments_X pax2
WHERE pax.assignment_id = pax2.assignment_id
AND PPX.PERSON_id = pax2.PERSON_id
AND PPX.PERSON_id = PAX.PERSON_id)
where CHANGE_TYPE is not null;
This kinda works but it retrieves all records, regardless if it has a match or not (due to the NULL "else" condition) and results into Change_Type = NULL.
Is it possible to just filter only those records that do not have Change_Type = NULL without encasing it in another SELECT statement?
I've also tried using CASE,
It works fine if the Employee's Pay Basis ID only changed (ex. from 1 to 2)
and if only the Employee's Category has changed (ex. from FT to FR),
but it evaluates all the cases (returning 3 rows) whenever "both" changes occur.
Any advise?
I didn't follow all the details of your post and the answers and ensuing comments, but it seems you may be after something like this. To get exactly one answer in all situations, you probably need a CASE expression. In the code below I use nested CASE expressions, but that is only to save typing (and a little bit of execution time); you could rewrite this using a single CASE expression.
The reason this works is that evaluation ends immediately as soon as the first TRUE condition in the when... then... pairs is found. You will have to figure out how to attach this to your existing query - I just put its outputs in a CTE for testing purposes below.
with
query_output ( empl_id, from_pay_basis_id, to_pay_basis_id, from_category, to_category ) as (
select 101, 1, 2, 'FR', 'FR' from dual union all
select 102, 1, 1, 'FT', 'FR' from dual union all
select 103, 1, 2, 'FT', 'FR' from dual union all
select 104, 1, 1, 'FR', 'FR' from dual
)
select empl_id, from_pay_basis_id, to_pay_basis_id, from_category, to_category,
case when from_category != to_category then
case when from_pay_basis_id != to_pay_basis_id then 'Assignment Category and Pay Basis'
else 'Assignment Category'
end
when from_pay_basis_id != to_pay_basis_id then 'Pay Basis'
end as change_type
from query_output;
EMPL_ID FROM_PAY_BASIS_ID TO_PAY_BASIS_ID FROM_CATEGORY TO_CATEGORY CHANGE_TYPE
------- ----------------- --------------- ------------- ----------- ---------------------------------
101 1 2 FR FR Pay Basis
102 1 1 FT FR Assignment Category
103 1 2 FT FR Assignment Category and Pay Basis
104 1 1 FR FR
You are probably after case statements in the where clause, something like:
SELECT PPX.FULL_NAME
, PPX.EMPLOYEE_NUMBER
, XT.CHANGE_TYPE
FROM per_people_X ppx
, XXTEST XT
, per_assignments_X pax
, per_assignments_X pax2
WHERE pax.assignment_id = pax2.assignment_id
AND PPX.PERSON_id = pax2.PERSON_id
AND PPX.PERSON_id = PAX.PERSON_id
AND (-- Both Employment Category and Pay Basis records records that were Changed
case when XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND XT.TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID
then 1
else 0
end = 1
OR
-- all Pay Basis records that were "Updated"
case when XT.FROM_CATEGORY = 'N/A'
AND XT.TO_CATEGORY = 'N/A'
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID
then 1
else 0
end = 1
OR
-- all Assignment Category records that were "Updated"
case when XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND XT.TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND XT.FROM_ID = 0
AND XT.TO_ID = 0
then 1
else 0
end = 1);
N.B. untested, since you didn't provide sample data for the per_people_x or per_assignments tables.
You may have to add extra conditions into the "both categories" case expression to exclude the other two cases; it's not immediately apparent from the data that you have supplied so far.
First of all if you use OR you should use brackets and (... or ... ) or you lose some predicates. Your first query should be rewritten to:
SELECT PPX.FULL_NAME
, PPX.EMPLOYEE_NUMBER
, XT.CHANGE_TYPE
FROM per_people_X ppx
, XXTEST XT
, per_assignments_X pax
, per_assignments_X pax2
WHERE pax.assignment_id = pax2.assignment_id
AND PPX.PERSON_id = pax2.PERSON_id
AND PPX.PERSON_id = PAX.PERSON_id
-- THIS IS THE SECTION OF THE QUERY WHERE I'M HAVING PROBLEMS --
-- Both Employment Category and Pay Basis records records that were Changed
AND (
(XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND XT.TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID)
-- all Pay Basis records that were "Updated"
or (XT.FROM_CATEGORY = 'N/A'
AND XT.TO_CATEGORY = 'N/A'
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID)
-- all Assignment Category records that were "Updated"
or (XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND XT.TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND XT.FROM_ID = 0
AND XT.TO_ID = 0)
);
EDIT: Add solution with ranking of condition
SELECT FULL_NAME
, EMPLOYEE_NUMBER
, CHANGE_TYPE
FROM (
SELECT PPX.FULL_NAME
, PPX.EMPLOYEE_NUMBER
, XT.CHANGE_TYPE
, DENSE_RANK() OVER (PRTITION BY PPX.PERSON_id,pax.assignment_id ORDER BY
CASE WHEN XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND XT.TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID
THEN 1
WHEN XT.FROM_CATEGORY = 'N/A'
AND XT.TO_CATEGORY = 'N/A'
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID
THEN 2
ELSE 3
END
) as RNK
FROM per_people_X ppx
, XXTEST XT
, per_assignments_X pax
, per_assignments_X pax2
WHERE pax.assignment_id = pax2.assignment_id
AND PPX.PERSON_id = pax2.PERSON_id
AND PPX.PERSON_id = PAX.PERSON_id
-- THIS IS THE SECTION OF THE QUERY WHERE I'M HAVING PROBLEMS --
-- Both Employment Category and Pay Basis records records that were Changed
AND (
(XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND XT.TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID)
-- all Pay Basis records that were "Updated"
or (XT.FROM_CATEGORY = 'N/A'
AND XT.TO_CATEGORY = 'N/A'
AND XT.FROM_ID = pax.PAY_BASIS_ID
AND XT.TO_ID = pax2.PAY_BASIS_ID)
-- all Assignment Category records that were "Updated"
or (XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY
AND XT.TO_CATEGORY = pax2.EMPLOYMENT_CATEGORY
AND XT.FROM_ID = 0
AND XT.TO_ID = 0)
)
) WHERE RNK = 1;

SQL: Split a row into multiple rows based on certain rules

Here is my table in my Microsoft Access database:
Terr(Prim) Terr(Sec) Qty
---------- --------- -------
A A 0.5
A B 0.5
I want to change it to like this through SQL:
Type Terr Qty
----------- ---- ----
Original A 0.5
Original A 0.5
50-50 give A -0.5
50-50 take B 0.5
Rules:
Create two columns, [Type] and [Terr]
if [Terr(Prim)] = [Terr (Sec)], then keep the row and [Type] = "Original"
if [Terr (Prim)] <> [Terr (Sec)], then:
Row 1: [Type] = "Original", [Terr] = [Terr(Prim)], the other columns remain the same
Row 2: [Type] = "50-50 Give", [Terr] = [Terr (Prim)], [Qty] turns into negative
Row 3: [Type] = "50-50 take", [Terr] = [Terr (Sec)], the other columns remain the same
delete [Terr(Prim)] and [Terr (Sec)]
I think you can use a query like this:
SELECT "Original" As [Type], [TerrPrim] AS [Terr], Qty
FROM t
UNION ALL
SELECT "50-50 Give" As [Type], [TerrPrim] AS [Terr], -Qty
FROM t
WHERE [TerrPrim] <> [TerrSec]
UNION ALL
SELECT "50-50 take" As [Type], [TerrSec] AS Terr, Qty
FROM t
WHERE [TerrPrim] <> [TerrSec];

Using a earlier made Column

I'm making a stored procedure where I have declared a column with the AS "NameofColumn" method. Now I want to use that value again later in the stored procedure. Is there anyway to do this?
CASE
WHEN ((select top 1 stuksweergeven from componenten where componentid = componentlink.componentid) = 1) and ((select AmountKG from componenten where componentid = componentlink.componentid) <> 0) THEN
Amount * (select AmountKG from componenten where componentid = componentlink.componentid)
ELSE
Amount
END AS Amount
Now later I want to do the following
Amount * 10 AS TotalAmount
Use subquery where you count the "Amount" and then make select to from that subquery . Something like that:
select Amount * 10 AS TotalAmount from ( SELECT CASE
WHEN ((select top 1 stuksweergeven from componenten where componentid = componentlink.componentid) = 1) and ((select AmountKG from componenten where componentid = componentlink.componentid) <> 0) THEN
Amount * (select AmountKG from componenten where componentid = componentlink.componentid)
ELSE
Amount
END AS Amount FROM yourtable )e