SQL query - db2/400 iseries - sql

My SQL group statement results in the below table. From this table, I need to subtract Qty with Code ='S' from Qty with code ='B' when Price and Date are matching.
For example, in the below table I need the value to be stored in a work variable.
1) 100-50 = 50 for the first 2 rows
2) 60 -30 = 30 for the 3rd and 4 th row
3) The last row since it does not have code 'S' it should return just 20
Table
Price Date Code Sum(Qty)
9.0 201512 B 100
9.0 201512 S 50
8.0 201506 B 60
6.0 201506 S 30
5.0 201508 B 20
SQL query used to get the above table
select Price, Date, Code,sum(Qty) from Table
where Type = 'A' and Acct = 'CLOSED'
group by Price,Date,Code
order by Price,Date
Can I modify my existing SQL statement typed above using CASE statements to get my required output. I tried it but Cursor returns one by one row and CASE does not seem to work
exec sql
declare c1 cursor for
select Price, Date, Code,
Case when Code ='B' then ifnull(sum(Qty),0)
when Code ='S' then (-1 * ifnull(sum(Qty),0)) end
from Table
where Type = 'A' and Acct = 'CLOSED'
group by Price,Date,Code
order by Price,Date
exec sql
open c1
exec sql
fetch c1 into :var_price, :var_date, :var_code, :var_Bqty, :VarSqty
Using SQLRPGLE on iseries system.

You can use conditional aggregation for this:
select Price, Date,
sum(case when code = 'B' then Qty when code = 'S' then -QTY end) as diff
from Table
where Type = 'A' and Acct = 'CLOSED'
group by Price, Date
order by Price, Date;

Related

How to pivot a table in sql and sum the amounts?

I have a table called test_table. This table looks like below
id
type
value
1
tax
10
1
premium
21
1
tax
3
1
correction
4.5
2
premium
15
I would like to "pivot" this table and make it look like below
id
premium
tax
correction
1
21
13 (=10+3)
4.5
2
15
NULL
NULL
create columns by type (premium, tax and correction)
sum the amounts by type and by id
With my basic sql knowledge, I have no idea how to build this query. Can you help me with this?
You may try the following pivot query:
SELECT
id,
SUM(CASE WHEN type = 'premium' THEN value ELSE 0 END) AS premium,
SUM(CASE WHEN type = 'tax' THEN value ELSE 0 END) AS tax
SUM(CASE WHEN type = 'correction' THEN value ELSE 0 END) AS correction
FROM yourTable
GROUP BY id
ORDER BY id;
Note that the above will report zero for those cells having entry in the source table.
In MS Sql Server, the PIVOT syntax should be sufficiant for this.
select *
from (
select id, [type], value
from test_table
) src
pivot (
sum(value)
for [type] in ([premium], [tax], [correction])
) pvt
order by id

How to Count Distinct on Case When?

I have been building up a query today and I have got stuck. I have two unique Ids that identify if and order is Internal or Web. I have been able to split this out so it does the count of how many times they appear but unfortunately it is not providing me with the intended result. From research I have tried creating a Count Distinct Case When statement to provide me with the results.
Please see below where I have broken down what it is doing and how I expect it to be.
Original data looks like:
Company Name Order Date Order Items Orders Value REF
-------------------------------------------------------------------------------
CompanyA 03/01/2019 Item1 Order1 170 INT1
CompanyA 03/01/2019 Item2 Order1 0 INT1
CompanyA 03/01/2019 Item3 Order2 160 WEB2
CompanyA 03/01/2019 Item4 Order2 0 WEB2
How I expect it to be:
Company Name Order Date Order Items Orders Value WEB INT
-----------------------------------------------------------------------------------------
CompanyA 03/01/2019 4 2 330 1 1
What currently comes out
Company Name Order Date Order Items Orders Value WEB INT
-----------------------------------------------------------------------------------------
CompanyA 03/01/2019 4 2 330 2 2
As you can see from my current result it is counting every line even though it is the same reference. Now it is not a hard and fast rule that it is always doubled up. This is why I think I need a Count Distinct Case When. Below is my query I am currently using. This pull from a Progress V10 ODBC that I connect through Excel. Unfortunately I do not have SSMS and Microsoft Query is just useless.
My Current SQL:
SELECT
Company_0.CoaCompanyName
, SopOrder_0.SooOrderDate
, Count(DISTINCT SopOrder_0.SooOrderNumber) AS 'Orders'
, SUM(CASE WHEN SopOrder_0.SooOrderNumber IS NOT NULL THEN 1 ELSE 0 END) AS 'Order Items'
, SUM(SopOrderItem_0.SoiValue) AS 'Order Value'
, SUM(CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN 1 ELSE 0 END) AS 'INT'
, SUM(CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'WEB%' THEN 1 ELSE 0 END) AS 'WEB'
FROM
SBS.PUB.Company Company_0
, SBS.PUB.SopOrder SopOrder_0
, SBS.PUB.SopOrderItem SopOrderItem_0
WHERE
SopOrder_0.SopOrderID = SopOrderItem_0.SopOrderID
AND Company_0.CompanyID = SopOrder_0.CompanyID
AND SopOrder_0.SooOrderDate > '2019-01-01'
GROUP BY
Company_0.CoaCompanyName
, SopOrder_0.SooOrderDate
I have tried using the following line but it errors on me when importing:
, Count(DISTINCT CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN SopOrder_0.SooParentOrderReference ELSE 0 END) AS 'INT'
Just so know the error I get when importing at the moment is syntax error at or about "CASE WHEN sopOrder_0.SooParentOrderRefer" (10713)
Try removing the ELSE:
COUNT(DISTINCT CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN SopOrder_0.SooParentOrderReference END) AS num_int
You don't specify the error, but the problem is probably that the THEN is returning a string and the ELSE a number -- so there is an attempt to convert the string values to a number.
Also, learn to use proper, explicit, standard JOIN syntax. Simple rule: Never use commas in the FROM clause.
count distinct on the SooOrderNumber or the SooParentOrderReference, whichever makes more sense for you.
If you are COUNTing, you need to make NULL the thing that your are not counting. I prefer to include an else in the case because it is more consistent and complete.
, Count(DISTINCT CASE WHEN SopOrder_0.SooParentOrderReference LIKE 'INT%' THEN SopOrder_0.SooParentOrderReference ELSE null END) AS 'INT'
Gordon Linoff is correct regarding the source of your error, i.e. datatype mismatch between the case then value else value end. null removes (should remove) this ambiguity - I'd need to double check.
Editing my earlier answer...
Even though it looks, as you say, like count distinct is not supported in Pervasive PSQL, CTEs are supported. So you can do something like...
This is what you are trying to do but it is not supported...
with
dups as
(
select 1 as id, 'A' as col1 union all select 1, 'A' union all select 1, 'B' union all select 2, 'B'
)
select id
,count(distinct col1) as col_count
from dups
group by id;
Stick another CTE in the query to de-duplicate the data first. Then count as normal. That should work...
with
dups as
(
select 1 as id, 'A' as col1 union all select 1, 'A' union all select 1, 'B' union all select 2, 'B'
)
,de_dup as
(
select id
,col1
from dups
group by id
,col1
)
select id
,count(col1) as col_count
from de_dup
group by id;
These 2 versions should give the same result set.
There is always a way!!
I cannot explain the error you are getting. You are mistakenly using single quotes for alias names, but I don't actually think this is causing the error.
Anyway, I suggest you aggregate your order items per order first and only join then:
SELECT
c.coacompanyname
, so.sooorderdate
, COUNT(*) AS orders
, SUM(soi.itemcount) AS order_items
, SUM(soi.ordervalue) AS order_value
, COUNT(CASE WHEN so.sooparentorderreference LIKE 'INT%' THEN 1 END) AS int
, COUNT(CASE WHEN so.sooparentorderreference LIKE 'WEB%' THEN 1 END) AS web
FROM sbs.pub.company c
JOIN sbs.pub.soporder so ON so.companyid = c.companyid
JOIN
(
SELECT soporderid, COUNT(*) AS itemcount, SUM(soivalue) AS ordervalue
FROM sbs.pub.soporderitem
GROUP BY soporderid
) soi ON soi.soporderid = so.soporderid
GROUP BY c.coacompanyname, so.sooorderdate
ORDER BY c.coacompanyname, so.sooorderdate;

SQL query syntax errors when dealing with subpopulations

I'm a software tester using this SQL query to check some data in our db:
select * from (
select sum(case when qm.HEALTHY_TERM_NEWBORN='E' then 1 else 0 end) as num, -- choose measure status columns here as appropriate
sum(case when qm.HEALTHY_TERM_NEWBORN in ('D','E') then 1 else 0 end) as denom,
-- summary level columns
cms.FACILITY_GROUP_ID,
-- time period columns
dt.USA_FISCAL_YEAR_BEGIN_DT
from F_MU_QM_EH_2014_IP_ADMSN qm
inner join CMS_MU_INFO cms on qm.DISCHARGE_CMS_MU_ID=cms.CMS_MU_ID
inner join DATE_DIMENSION dt on dt.CALENDAR_DT=qm.DISCHARGE_DATE
-- do filtering here if any
where cms.FACILITY_GROUP_ID='130170' AND dt.USA_FISCAL_YEAR_BEGIN_DT='10/01/2013'
group by -- summary level grouping
cms.FACILITY_GROUP_ID,
-- time period grouping
dt.USA_FISCAL_YEAR_BEGIN_DT
) x
where denom is not null
The script returns aggregated data from one column of information; however, for some of my testing I need to filter the column based on another column that defines a subpopulation within the first column. I tried this:
select * from (
sum(case when qm.SCIPINF1='E' and qm.SCIPINF1_POPULATION_C=7 then 1 else 0 end) as num
sum(case when qm.SCIPINF1 in ('D','E') and qm.SCIPINF1_POPULATION_C=1 then 1 else 0 end) as denom
-- summary level columns
cms.FACILITY_GROUP_ID,
-- time period columns
dt.USA_FISCAL_YEAR_BEGIN_DT
from F_MU_QM_EH_2014_IP_ADMSN qm
inner join CMS_MU_INFO cms on qm.DISCHARGE_CMS_MU_ID=cms.CMS_MU_ID
inner join DATE_DIMENSION dt on dt.CALENDAR_DT=qm.DISCHARGE_DATE
-- do filtering here if any
where cms.FACILITY_GROUP_ID='130170' AND dt.USA_FISCAL_YEAR_BEGIN_DT='10/01/2013'
group by -- summary level grouping
cms.FACILITY_GROUP_ID,
-- time period grouping
dt.USA_FISCAL_YEAR_BEGIN_DT
) x
where denom is not null
But I'm getting a syntax error near 'sum' according to Oracle, and I'm missing a parenthesis according to MS SQL Server Management Studio. Do any of you beautiful internet people know what I'm doing wrong?
Missing comma between two columns and also select in sub-select
SELECT *
FROM (SELECT Sum(CASE -- select missing here
WHEN qm.SCIPINF1 = 'E'
AND qm.SCIPINF1_POPULATION_C = 7 THEN 1
ELSE 0
END) AS num, --comma missing here
Sum(CASE
WHEN qm.SCIPINF1 IN ( 'D', 'E' )
AND qm.SCIPINF1_POPULATION_C = 1 THEN 1
ELSE 0
END) AS denom, --comma missing here
-- summary level columns
cms.FACILITY_GROUP_ID,
-- time period columns
dt.USA_FISCAL_YEAR_BEGIN_DT

Oracle SQL to get min number from case statement

I have this query below (in Oracle PL/SQL) which I am trying to pull a price, it seems to give me 3 records but I only want one. I want to show the lowest rate if I run into this kind of scenario, can I possibly put a min function around the 'rate' field which is in the case statement below?
select /*+ rule */ a.skucode as skucode,a.sizecode as
sizecode,b.colourdescription as colourdesc, a.season as season,
(case when sp.eventnbr in (select eventnbr from event where sysdate
between eventbegints
and eventendts) then rate else sellprice end) as listprice
from sku a, colour b, skuprcevnt sp
where a.skucode = '00000000051361264-04'
--" and a.storecode = '00000' " +
and a.storecode = '00000'
and a.colourcode = b.colourcode
and a.skucode=sp.skucode(+)
order by a.skucode, a.sizecode, b.colourdescription;
This gives the following result (but I want to see the price of 76.99 only):
SKUCODE SIZECODE COLOURDESC SEASON LISTPRICE
00000000051361264-04 XL BLACK FA-13 155
00000000051361264-04 XL BLACK FA-13 155
00000000051361264-04 XL BLACK FA-13 76.99
You can use MIN in the column list if you group the query appropriately:
select a.skucode,
a.sizecode,
b.colourdescription as colourdesc,
a.season,
MIN(case
when sp.eventnbr in (select eventnbr
from event
where sysdate between eventbegints
and eventendts)
then rate
else sellprice
end) as listprice
from sku a
INNER JOIN colour b
ON b.colourcode = a.colourcode
LEFT OUTER JOIN skuprcevnt sp
ON sp.skucode = a.skucode
where a.skucode = '00000000051361264-04' and
a.storecode = '00000'
GROUP BY a.SKUCODE, a.SIZECODE, b.COLOURDESCRIPTION, a.SEASON
order by a.skucode, a.sizecode, b.colourdescription, a.SEASON;
Share and enjoy.
The contents of your select clause will never affect the number of rows returned by a query. You need a filter in the WHERE clause along the lines of WHERE rate = (select min(rate) sku where skucode=a.skucode ).
Your example is more complicated so the actual filter would be bigger but that's just an idea of what it would look like.

Not a GROUP BY Expression & aggregate functions

I was wondering why, for this query that I have right here, why I have to use the MAX() aggregate function for the case statements, and not just jump directly into the case statement:
select
bank_id,
tran_branch_code,
acct_sol_id,
acct_sol_name,
transaction_date,
gl_date,
transaction_id,
account_number,
max(case
when cast(substr(GLSH_Code,0,1) as int) >= 1
and cast(substr(GLSH_Code,0,1) as int) <= 5
and trans_type = 'D'
then (trans_amount)
--else 0
end ) Ind_Part_Tran_Dr_RBU,
max(case
when cast(substr(GLSH_Code,0,1) as int) >= 1
and cast(substr(GLSH_Code,0,1) as int) <= 5
and trans_type = 'C'
then (trans_amount)
--else 0
end) Ind_Part_Tran_Cr_RBU,
max(case
when cast(substr(GLSH_Code,0,1) as int) = 0
or (cast(substr(GLSH_Code,0,1) as int) >= 6
and cast(substr(GLSH_Code,0,1) as int) <= 9)
and trans_type = 'D'
then (trans_amount)
--else 0
end)Ind_Part_Tran_Dr_FCDU,
max(case
when cast(substr(GLSH_Code,0,1) as int) = 0
or (cast(substr(GLSH_Code,0,1) as int) >= 6
and cast(substr(GLSH_Code,0,1) as int) <= 9)
and trans_type = 'C'
then (trans_amount)
--else 0
end) Ind_Part_Tran_Cr_FCDU,
ccy_alias,
ccy_name,
acct_currency,
tran_currency
from
(
SELECT
DTD.BANK_ID,
DTD.SOL_ID Acct_Sol_ID, --Account Sol ID
dtd.br_code Tran_branch_code, -- branch code of the transacting branch
sol.sol_desc Acct_sol_name, -- name/description of SOL
DTD.TRAN_DATE Transaction_Date, --TransactionDate
DTD.GL_DATE GL_Date, --GL Date
TRIM(DTD.TRAN_ID) Transaction_ID, --Transaction ID
DTD.GL_SUB_HEAD_CODE GLSH_Code, --GLSH Code
dtd.tran_amt trans_amount,
GAM.ACCT_CRNCY_CODE Acct_Currency, --Account Currency
DTD.TRAN_CRNCY_CODE Tran_Currency, --Transaction Currency
cnc.crncy_alias_num ccy_alias,
cnc.crncy_name ccy_name,
GAM.FORACID Account_Number, --Account Number
DTD.TRAN_PARTICULAR Transaction_Particulars, --Transaction Particulars
DTD.CRNCY_CODE DTD_CCY,
--GSH.CRNCY_CODE GSH_CCY,
DTD.PART_TRAN_TYPE Transaction_Code,
--'Closing_Balance',
DTD.PSTD_USER_ID PostedBy,
CASE WHEN DTD.REVERSAL_DATE IS NOT NULL
THEN 'Y' ELSE 'N' END Reversal,
TRIM(DTD.TRAN_ID) REV_ORIG_TRAN_ID,
--OTT.REF_NUM OAP_REF_NUM,
'OAP_SETTLEMENT',
'RATE_CODE',
EAB.EOD_DATE
FROM TBAADM.DTD
LEFT OUTER JOIN TBAADM.GAM ON DTD.ACID = GAM.ACID AND DTD.BANK_ID = GAM.BANK_ID
LEFT OUTER JOIN TBAADM.EAB ON DTD.ACID = EAB.ACID AND DTD.BANK_ID = EAB.BANK_ID AND EAB.EOD_DATE = '24-MAR-2014'
left outer join tbaadm.sol on dtd.sol_id = sol.sol_id and dtd.bank_id = sol.bank_id
left outer join tbaadm.cnc on dtd.tran_crncy_code = cnc.crncy_code
WHERE DTD.BANK_ID = 'CBC01'
AND GAM.ACCT_OWNERSHIP = 'O'
AND GAM.DEL_FLG != 'Y'
--AND DTD.TRAN_DATE = '14-APR-2014'
AND DTD.TRAN_DATE between '01-APR-2014' and '21-APR-2014'
--and foracid in ('50010112441109','50010161635051')
--and DTD.SOL_ID = '5001'
and GAM.ACCT_CRNCY_CODE = 'USD'
)
group by
bank_id,
tran_branch_code,
acct_sol_id,
acct_sol_name,
transaction_date,
gl_date,
transaction_id,
account_number,
ccy_alias,
ccy_name,
Acct_Currency,
Tran_Currency
Because If I would remove the MAX(), I'd get the "Not a GROUP BY Expression", and Toad points me to the first occurrence of the GLSH_Code. Based from other websites, the cure for this is really adding the MAX() function. I would just like to understand why should I use that particular function, what it exactly does in the query, stuff like that.
EDIT: inserted the rest of the code.
I know for sure what MAX() does, it returns the largest value in an expression. But in this case, I can't seem to figure out exactly what that largest value is that the function is attempting to return.
The GROUP BY statement declares that all columns returned in the SELECT should be aggregated, but that you want to separate the results by those listed in the GROUP BY.
This means we have to use aggregate functions like MIN, MAX, AVG, SUM, etc. on any column that is NOT listed in the GROUP BY.
It's about telling the SQL engine what the expected results should be when there is more than one option.
In a simple example, we have a table with three columns:
PrimaryId SubId RowValue
1 1 1
2 1 2
3 2 4
4 2 8
And an SQL like the following (which is invalid):
SELECT SubId, RowValue
FROM SampleTable
GROUP BY SubId
We know we want the distinct SubId's (because of the GROUP BY), but we don't know what RowValue should be when we aggregate the results.
SubId RowValue
1 ?
2 ?
We have to be explicit in our query, and indicate what RowValue should be as the results can vary.
If we choose MIN(RowValue) we see:
SubId RowValue
1 1
2 4
If we choose MAX(RowValue) we see:
SubId RowValue
1 2
2 8
If we choose SUM(RowValue) we see:
SubId RowValue
1 3
2 12
Without being explicit there's a high likelihood that the results will be wrong, so our SQL engine of choice protects us from ourselves by enforcing the need for aggregate functions.
You have group by clause at the end on all the columns except for Ind_Part_Tran_Dr_RBU, Ind_Part_Tran_Cr_RBU, Ind_Part_Tran_Dr_FCDU, Ind_Part_Tran_Cr_FCDU. In this case oracle wants you to tell what to do with these columns, i.e. based on which function it has to aggregate them for every group it finds.