db2 query: replacement for 'with' - db2

Our code structure is something like this:
select
sampleA.columnA,
(select sampleB.columnA
from sampleB where sampleB.ColumnB = 'test1'),
(select sampleB.columnA
from sampleB where sampleB.ColumnC = 'test2')
from sampleA
join sampleB on sampleA.id = sampleB.id
where sampleA.columnB = 'test3'
What happens is.. if we run only the
select sampleB.columnA from sampleB where sampleB.ColumnB = 'test1'
then that returns us 27 results.
Similarly it returns some other say 35 rows for
select sampleB.columnA from sampleB where sampleB.ColumnC = 'test2'
But when we run the entire query, we get scalar error....
We are using DB2 and ultimately want to run this on Jasper.. and Jasper does not support 'with' any other way?

A subquery must return only one row, try this :
select sampleA.columnA,
(select sampleB.columnA from sampleB where sampleB.ColumnB = 'test1' FETCH FIRST ROWS ONLY) as columnAtest1 ,
(select sampleB.columnA from sampleB where sampleB.ColumnC = 'test2' FETCH FIRST ROWS ONLY) as columnAtest2
from sampleA
join sampleB on sampleA.id = sampleB.id
where sampleA.columnB = 'test3'
or this
select sampleA.columnA,
Samplec.columnA as columnAtest1, Sampled.columnA as columnAtest2
from sampleA
join sampleB on sampleA.id = sampleB.id
cross join (select sampleB.columnA from sampleB where sampleB.ColumnB = 'test1' FETCH FIRST ROWS ONLY) Samplec
cross join (select sampleB.columnA from sampleB where sampleB.ColumnB = 'test2' FETCH FIRST ROWS ONLY) Sampled
where sampleA.columnB = 'test3'

Related

Recursive select in Postgres SQL

I'm trying to make my first recursive query in SQL and get an unxpected syntax error with inner join clause. I don't understand what causes this error. The query is simple it is just to select all employee attached to their boss with simple rule: BOSS_POS_ID = POS_ID
Here is example of my data:
POS_ID;POS_NAME;BOSS_POS_ID
32520602;CEO;
32809988;Manager;32520602
35244656;Vice;32520602
35244652;CEO assistant;32520602
35042934;Manager;32520602
35255704;Manager;32520602
35342468;Director;32520602
34091164;Director;32520602
35236439;Excecutive;32520602
32809978;Director;32520602
Here is my query:
with recursive subordinates as
(
select POS_ID, POS_NAME, BOSS_POS_ID
from gdm.hr_oss
where
POS_ID = 32520602
and CAL_DAY = (select max(CAL_DAY) from gdm.hr_oss)
union select
e.POS_ID,
e.POS_NAME,
e.BOSS_POS_ID
from gdm.hr_oss e
where CAL_DAY = (select max(CAL_DAY) from gdm.hr_oss)
inner join subordinates s on s.POS_ID = e.BOSS_POS_ID
)
select * from subordinates;
Here is error:
ERROR. Execution failed on sql '
with recursive subordinates as
(
select POS_ID, POS_NAME, BOSS_POS_ID
from gdm.hr_oss
where
POS_ID = 32520602
and CAL_DAY = (select max(CAL_DAY) from gdm.hr_oss)
union select
e.POS_ID,
e.POS_NAME,
e.BOSS_POS_ID
from gdm.hr_oss e
where CAL_DAY = (select max(CAL_DAY) from gdm.hr_oss)
inner join subordinates s on s.POS_ID = e.BOSS_POS_ID
)
select * from subordinates;
': syntax error at or near "inner"
LINE 15: inner join subordinates s on s.POS_ID = e.BOSS_POS_ID

Converting select statement directly into json array

From here, and here I have figured out that if I want to aggregate a set of related rows into an array of objects I have to use this syntax:
(select to_json(C) from ( /* subquery */ ) C)
So, if I have three tables: user, creature and their junction table user_creature:
And I want to retrieve each user, and each creature that belongs to this user, I would have to do something like this:
select to_json(T)
from (
select "user".id as user_id,
(select to_json(C) -- !!! There it is
from (
select name, height
from creature
inner join "user_creature" uc on creature.id = "uc".creature_id
inner join "user" u on "uc".user_id = u.id
where u.id = user_id
) C) as "creatures" -- !!! There it is
from "user"
) T;
This query successfully retrieves a list of users and their related creatures:
Is there a way to drop select and from keywords from the query, so that I can write my query like this:
select to_json(T)
from (
select "user".id as user_id,
to_json( -- !!! Calling to_json directly on select statement
select name, height
from creature
inner join "user_creature" uc on creature.id = "uc".creature_id
inner join "user" u on "uc".user_id = u.id
where u.id = user_id
) as "creatures"
from "user"
) T;
It is possible to use a subquery as the argument to to_json, but not practical:
You need to wrap the subquery in a grouping parenthesis: to_json( (SELECT … FROM …) )
The subquery must return exactly one row (but that's normal)
The subquery must return exactly one column. This is a bit harder - you can return a record, but if you build it dynamically (e.g. from a selection of columns, you can hardly control the field names)
(See a demo here).
Instead, use json_build_object if you want to write a single SELECT query only:
SELECT json_build_object(
'user_id', u.id,
'creatures', (
SELECT json_build_object(
'name', c.name,
'height', c.height
)
FROM creature c
INNER JOIN "user_creature" uc ON c.id = uc.creature_id
WHERE uc.user_id = u.id
)
)
FROM "user" u;
And, if you want to be able to retrieve multiple rows use SELECT json_agg(json_build_object(…)) FROM … or ARRAY(SELECT json_build_object(…) FROM …):
SELECT json_build_object(
'user_id', u.id,
'creatures', (
SELECT json_agg(json_build_object(
'name', c.name,
'height', c.height
))
FROM creature c
INNER JOIN "user_creature" uc ON c.id = uc.creature_id
WHERE uc.user_id = u.id
)
)
FROM "user" u;

Is there any better way to perform unpivoting without using Union All?

Please look into the below query
select
ca.cnsmr_accnt_id,
cab.cnsmr_accnt_bal_amnt,
[Balance Short Name] = 'LOAN_AMOUNT' --LOAN_AMOUNT
from cnsmr_accnt ca
inner join cnsmr_accnt_bal cab on ca.cnsmr_accnt_id = cab.cnsmr_accnt_id
inner join bal_nm b on cab.bal_nm_id = b.bal_nm_id and b.bal_shrt_nm='OriBal' and b.bal_nm_actv_flg ='Y' UNION ALL
select
ca.cnsmr_accnt_id,
cab.cnsmr_accnt_bal_amnt,
[Balance Short Name] = 'BOM_POS' --BOM_POS
from cnsmr_accnt ca
inner join cnsmr_accnt_bal cab on ca.cnsmr_accnt_id = cab.cnsmr_accnt_id
inner join bal_nm b on cab.bal_nm_id = b.bal_nm_id and b.bal_shrt_nm='CurBal' and b.bal_nm_actv_flg ='Y' UNION ALL
select
ca.cnsmr_accnt_id,
cab.cnsmr_accnt_bal_amnt,
[Balance Short Name] = 'CURRENT_POS' --CURRENT_POS
from cnsmr_accnt ca
inner join cnsmr_accnt_bal cab on ca.cnsmr_accnt_id = cab.cnsmr_accnt_id
inner join bal_nm b on cab.bal_nm_id = b.bal_nm_id and b.bal_shrt_nm='CurBal' and b.bal_nm_actv_flg ='Y'
UNION ALL
select
ca.cnsmr_accnt_id,
cab.cnsmr_accnt_bal_amnt,
[Balance Short Name] = 'Total_Amount_Paid_till_date' --Total Amount Paid till date
from cnsmr_accnt ca
inner join cnsmr_accnt_bal cab on ca.cnsmr_accnt_id = cab.cnsmr_accnt_id
inner join bal_nm b on cab.bal_nm_id = b.bal_nm_id and b.bal_shrt_nm='TotPay' and b.bal_nm_actv_flg ='Y'
Every thing is same except the bal_shrt_nm value. This is taking too long time. Any better way to perform the same operation.
Sample output
You can use
SELECT ca.cnsmr_accnt_id,
cab.cnsmr_accnt_bal_amnt,
CA2.[Balance Short Name]
FROM cnsmr_accnt ca
INNER JOIN cnsmr_accnt_bal cab
ON ca.cnsmr_accnt_id = cab.cnsmr_accnt_id
INNER JOIN bal_nm b
ON cab.bal_nm_id = b.bal_nm_id
CROSS APPLY (SELECT 'LOAN_AMOUNT'
WHERE b.bal_shrt_nm = 'OriBal'
UNION ALL
SELECT 'BOM_POS'
WHERE b.bal_shrt_nm = 'CurBal'
UNION ALL
SELECT 'CURRENT_POS'
WHERE b.bal_shrt_nm = 'CurBal'
UNION ALL
SELECT 'Total_Amount_Paid_till_date'
WHERE b.bal_shrt_nm = 'TotPay') CA2([Balance Short Name])
WHERE b.bal_nm_actv_flg = 'Y'
AND b.bal_shrt_nm IN ( 'OriBal', 'CurBal', 'TotPay' );
It still has UNION ALL but doesn't repeat the join four times and then UNION ALL the results.

Aggregate similar row

Suppose I've a table like this:
NAME REF1 REF2 DRCT
A (null) Ra D1
A Rb (null) D1
A (null) Rc D2
B Rd (null) D3
B (null) Re D3
I want aggregate this table in something like:
NAME REF1 REF2 DRCT
A Rb Ra D1
A (null) Rc D2
B Rd Re D3
As you can see, i want aggregate each row with same name. I've search through COALESCE and various aggregate functions but I haven't found what i was looking for. Any idea?
Assuming that what I ask in my previous comment is true, (only null or a given value for REF1 and REF2 for each NAME, DRCT pair), this seems to work:
select NAME, M_REF1, M_REF2, DRCT
from (
select A.NAME, coalesce(A.REF1, B.REF1) m_REF1,
coalesce(A.REF2, B.REF2) m_REF2, A.REF1 A_REF1, B.REF1 B_REF1,
A.REF2 A_REF2, B.REF2 B_REF2, A.DRCT
from Table1 A JOIN Table1 B on A.NAME = B.NAME AND A.DRCT = B.DRCT)
WHERE A_REF1 = m_REF1 AND B_REF2 = m_REF2
UNION
select A.NAME, A.REF1, A.REF2, A.DRCT
FROM Table1 A JOIN
(select NAME, DRCT, COUNT(*)
from Table1
group by NAME, DRCT
HAVING COUNT(*) = 1) B ON A.NAME = B.NAME AND A.DRCT = B.DRCT;
The union is used because the rows with only one record are not included in the first SELECT.
But this is somewhat simpler, and works too:
select A.NAME, coalesce(A.REF1, B.REF1) M_REF1, coalesce(A.REF2,B.REF2) M_REF2,A.DRCT
from Table1 A LEFT OUTER JOIN Table1 B ON A.DRCT = B.DRCT AND A.NAME = B.NAME
WHERE NVL2(A.REF1,0,1) = 1 AND NVL2(B.REF1,0,1) =0
AND NVL2(A.REF2,0,1) = 0 AND NVL2(B.REF2,0,1) = 1
UNION
select A.NAME, A.REF1, A.REF2, A.DRCT
FROM Table1 A JOIN
(select NAME, DRCT, COUNT(*)
from Table1
group by NAME, DRCT
HAVING COUNT(*) = 1) B ON A.NAME = B.NAME AND A.DRCT = B.DRCT;

Can you use a SELECT INTO statement with a CTE that contains a UDF?

Can I do this:
With ZipCodeCTE as
{
select nvl(HH.GeoUSZip5 , **ZipCodeKeyLookUp**(HH.[CityName],HH.[StateName])) as TotalZipCode
from ODSDataArchive.archive.HHJob_Data_201202 HH
}
/* This Is a SELECT INTO statement that inserts
data into [Jobs].[dbo].[FactRPP]*/
SELECT [dbo].[FactJobsDaily].jobdid,
[dbo].[FactJobsDaily].DateKey,
[dbo].[FactJobsDaily].YearMonth,
[dbo].[FactJobsDaily].AccountKey,
[dbo].[FactJobsDaily].BridgeSocKey,
[dbo].[FactJobsDaily].HostSiteKey,
[dbo].[FactJobsDaily].JobClickedCount,
[dbo].[FactJobsDaily].JobResultsPageCount,
(select DZ.ZipCodeKey
from dimensions.dbo.DimZipCode DZ
where DZ.ZipCodeKey IN
(Select CAST(TotalZipCode AS INT)
from ZipCodeCTE))
INTO [Jobs].[dbo].[FactRPP]
from dbo.FactJobsDaily
inner join ODSDataArchive.archive.HHJob_Data_201202
on dbo.FactJobsDaily.JobDID = ODSDataArchive.archive.HHJob_Data_201202.DID
and dbo.FactJobsDaily.datekey = ODSDataArchive.archive.HHJob_Data_201202.datekey
inner join dimensions.dbo.Dimzipcode dzc
on ODSDataArchive.archive.HHJob_Data_201202.geoUSZip5 = dimensions.dbo.Dimzipcode.ZipCode
where [dbo].[FactJobsDaily].yearmonth= 201202
and [dbo].[FactJobsDaily].isactivekey = 1
-- and ODSDataArchive.archive.HHJob_Data_201202.geoUSZip5 <> ''
-- and ODSDataArchive.archive.HHJob_Data_201202.geoUSZip5 IS NOT NULL
and ODSDataArchive.archive.HHJob_Data_201202.status = 0
and ODSDataArchive.archive.HHJob_Data_201202.CountryName = 'US'
order by [dbo].[FactJobsDaily].jobdid;
Because the CTE translates into a regular query the short answer is yes.