DB2 Union causes time out? - sql

First time working with DB2. I have a stored proc that performs a union, well it trys to anyhow. The procedure times out. The select statements on either side of the union execute quickly with no issue when done individually. Why would a union do this?
Here is some sudo code for the proc, assume data types are either int or string, and they match when compared or unioned unless otherwise noted in the SQL:
DECLARE Foo CURSOR WITH RETURN FOR
Select STRIP(A.Name) as my_Name,
Case A.Number
when 2 then '(' || strip(char(A.Number)) || ')' strip(B.num)
when 3 then '(' || strip(char(A.Number)) || ')' strip(C.num)
when 4 then '(' || strip(char(A.Number)) || ')' strip(D.num)
when 5 then '(' || strip(char(A.Number)) || ')' strip(E.num)
when 6 then '(' || strip(char(A.Number)) || ')' strip(F.num)
end as my_number
FROM A
left outer join B on A.Number= 2 and A.Name = B.Name
left outer join C on A.Number= 3 and C.Name = B.Name
left outer join D on A.Number= 4 and D.Name = B.Name
left outer join E on A.Number= 5 and E.Name = B.Name
left outer join F on A.Number= 6 and F.Name = B.Name
,session.Temp_Result X
WHERE X.ID = A.ID
GROUP BY A.Number, A.Name, B.Name, C.Name, D.Name, E.Name, F.Name
for fetch only ur;
DROP TABLE Session.Temp_Result;
DECLARE GLOBAL TEMPORARY TABLE session.Temp_Result
( ID DECIMAL(18,0)
);
INSERT INTO session.Temp_Result
select X.ID
from Z, Y, X, Q
where Z.num = 6
and Z.ID = Y.ID2
and Y.GROUPA = 'ABC'
and Y.GROUPB = 'DEF'
and Y.ID = X.ID2
and X.ID = Q.ID
union
select W.ID
from Z, Y, W
where Z.num = 6
and Z.ID = Y.ID2
and Y.GROUPA = 'ABC'
and Y.GROUPB = 'DEF'
and Y.ID = W.ID2
group by ID;
OPEN C_HIERARCHIES;

Try to query using "with" statement.
DECLARE GLOBAL TEMPORARY TABLE session.Temp_Result (
ID DECIMAL(18,0))
WITH REPLACE;
INSERT INTO session.Temp_Result
(ID)
WITH Q1(Y_ID)
AS (
SELECT Y.ID
FROM Z
INNER JOIN Y ON Z.ID = Y.ID2
WHERE Z.NUM = 6 AND Y.GROUPA = 'ABC' AND Y.GROUPB = 'DEF')
SELECT X.ID
FROM X
INNER JOIN Q1 ON X.ID2 = Q1.Y_ID
WHERE EXISTS(SELECT 1 FROM Q WHERE Q.ID = X.ID)
UNION
SELECT DISTINCT W.ID
FROM W
INNER JOIN Q1 ON W.ID2 = Q1.Y_ID
If it does not help, try to use "Explain SQL" for the select statement. Maybe you should create some indexes...

My guess is that the original code is missing a join condition that is present in the pseudocode. You should rewrite the query using proper join syntax to verify the query.
In any case, if each subquery runs quickly and there is some perverse consequence of a union, can you try splitting it into two inserts:
INSERT INTO session.Temp_Result
select X.ID
from Z, Y, X, Q
where Z.num = 6
and Z.ID = Y.ID2
and Y.GROUPA = 'ABC'
and Y.GROUPB = 'DEF'
and Y.ID = X.ID2
and X.ID = Q.ID;
INSERT INTO session.Temp_Result
select W.ID
from Z, Y, W
where Z.num = 6
and Z.ID = Y.ID2
and Y.GROUPA = 'ABC'
and Y.GROUPB = 'DEF'
and Y.ID = W.ID2
group by ID;
Then you can query Temp_Result as:
select distinct id from TempResult
Removing duplicates seems unnecessary, since the data is so small, but you could also do that directly.

Related

Change the SELECT in WHERE clause to another solution

I want to make my query better. What opportunities that I have to change this code for a faster one?
SELECT sn
FROM package p
WHERE StatusID = 1
AND ( NOT EXISTS( SELECT * FROM packagedetail pd
WHERE pd.packageID = p.ID AND packageDetailStatus = 12 )
OR ( EXISTS ( SELECT * FROM packagedetail pd
WHERE pd.packageID = p.ID AND packageDetailStatus = 12)
AND EXISTS ( SELECT * FROM unit u JOIN unitDetail ON u.ID = ud.unitID
WHERE (ud.InmostPackageID = p.ID OR ud.OutmostPackageID = p.ID)
AND u.UnitStateID in (8120, 8130, 8140)
)
)
)
( I know this is so ugly, but I want to know, how can I improve my skills )
One simplification that you can do is to not repeat the NOT EXISTS subquery again.
The below query is semantically equivalent to yours:
SELECT p.sn
FROM package p
WHERE p.StatusID = 1
AND (
NOT EXISTS(SELECT * FROM packagedetail pd
WHERE pd.packageID = p.ID AND packageDetailStatus = 12)
OR
EXISTS (SELECT * FROM unit u JOIN unitDetail ON u.ID = ud.unitID
WHERE (ud.InmostPackageID = p.ID OR ud.OutmostPackageID = p.ID) AND u.UnitStateID in (8120, 8130, 8140))
);
because:
(NOT X) OR (X AND Y) = (NOT X) OR Y
An alternative that might make it easier to read:
SELECT sn
FROM
package p
cross apply
(select
exists_packagedetail =case when exists (
SELECT *
FROM packagedetail pd
WHERE pd.packageID = p.ID AND packageDetailStatus = 12
) then 1 else 0 end
,exists_unitdetail =case when exists (
SELECT *
FROM
unit u
JOIN unitDetail ON u.ID = ud.unitID
WHERE (ud.InmostPackageID = p.ID OR ud.OutmostPackageID = p.ID) AND u.UnitStateID in (8120, 8130, 8140)
)then 1 else 0 end
) as q1
WHERE StatusID = 1 and (q1.exists_unitdetail=1 or q1.exists_packagedetail=0)

Better sql query for performace

I have a query that looks like :
select *
from Franvaro f
inner join Anvandare a on a.id = f.anvandare_id
where (
f.friskskriven = 'Y'
and not exists (
select *
from sscpost
where franvaro_id = f.id
and friskskrivenpost = 'Y'
)
)
or (
f.startdatum <= '2020-04-05'
and not exists (
select *
from sscpost
where franvaro_id = f.id
and friskskrivenpost = 'Y'
)
)
and a.anstallningtyp = 'A'
;
I wonder if this query could be written in another way with the same output/result in order to have a better performance or better structure.I have tested also with this query(see below), but the result is not the same compared to the other query.
select *
from Franvaro f
inner join Anvandare a on a.id = f.anvandare_id
where f.friskskriven = 'Y'
or f.startdatum <= '2020-04-05'
and not exists (
select *
from sscpost
where franvaro_id = f.id
and friskskrivenpost = 'Y'
)
and a.anstallningtyp = 'A'
;
This might not matter with modern optimizers anymore, but in your EXISTS clause, you don't actually need anything returned. Therefore you might return 1 or NULL or something. I'm showing my age.
At the end of the day, the best way to optimize queries is to learn how to read their EXPLAIN PLAN's. Every SQL system works differently, and there sometimes isn't 1 best way. It will also depend on the size of these tables and what the data looks like.
But there is nothing inherently wrong with your code, it looks fine.
select *
from Franvaro f
inner join Anvandare a on a.id = f.anvandare_id
where (
f.friskskriven = 'Y'
and not exists (
select 1
from sscpost
where franvaro_id = f.id
and friskskrivenpost = 'Y'
)
)
or (
f.startdatum <= '2020-04-05'
and not exists (
select 1
from sscpost
where franvaro_id = f.id
and friskskrivenpost = 'Y'
)
)
and a.anstallningtyp = 'A'
;
Try:
SELECT *
FROM Franvaro f
INNER JOIN Anvandare a ON a.id = f.anvandare_id
WHERE a.anstallningtyp = 'A'
AND ( f.friskskriven = 'Y' OR f.startdatum <= '2020-04-05' )
AND NOT EXISTS
(
SELECT null
FROM sscpost
WHERE franvaro_id = f.id
AND friskskrivenpost = 'Y'
)
Mainly we combine the NOT EXISTS statement into one call

Inner join on multiple subqueries?

I am writing a select that contains a few subqueries (only one in snippet of code below), however I am having a hard time only returning rows where there is 1 existing row in the AIRFI_TCD subquery. I only want rows to be displayed when there is a matching CLT_ID on TSUMM and TPPPRFL tables. I tried the HAVING COUNT clause (see below), but that didn't work. I am blanking on how to join TSUMM with my subquery.
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN
, ( SELECT B.XREF_NBR
FROM GHMTUO#1.TPPPRFL B
WHERE B.PARTIC_PRFL_TCD = '04' --AIRFI
AND B.CLT_ID = A.CLT_ID
AND B.BUS_PGM_ID = A.PGM_ID
AND B.CLT_ID_TCD = '01'
AND B.PARTIC_PRFL_EDT = (SELECT MAX(X.PARTIC_PRFL_EDT)
FROM GHMTUO#1.TPPPRFL X
WHERE X.CLT_ID = B.CLT_ID)
AND B.PARTIC_PRFL_CDTTM = (SELECT MAX(Z.PARTIC_PRFL_CDTTM)
FROM GHMTUO#1.TPPPRFL Z
WHERE Z.CLT_ID = B.CLT_ID)
) AS AIRFI_TCD
FROM RAMTUO#1.TSUMM A
WHERE A.PGM_ID = 'RT'
GROUP BY A.CLT_ID, A.PGM_ID, A.PGM_ACCT_SQNBR
HAVING COUNT(AIRFI_TCD) > 1
WITH UR;
Thank you!
I this an INNER JOIN solution?
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN,
B.XREF_NBR AS AIRFI_TCD
FROM RAMTUO#1.TSUMM A INNER JOIN GHMTUO#1.TPPPRFL B ON B.CLT_ID = A.CLT_ID AND B.BUS_PGM_ID = A.PGM_ID
WHERE A.PGM_ID = 'RT' AND B.PARTIC_PRFL_TCD = '04'
AND ...
GROUP BY A.CLT_ID, A.PGM_ID, A.PGM_ACCT_SQNBR
rows will only appear if there is a matching CLT_ID and a matching PGM_ID
If you need just existence, then try this:
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN
FROM RAMTUO#1.TSUMM A
WHERE A.PGM_ID = 'RT'
AND EXISTS
(
SELECT 1
FROM GHMTUO#1.TPPPRFL B
JOIN
(
SELECT CL
T_ID
, MAX(PARTIC_PRFL_EDT) PARTIC_PRFL_EDT
, MAX(PARTIC_PRFL_CDTTM) PARTIC_PRFL_CDTTM
FROM GHMTUO#1.TPPPRFL
GROUP BY CLT_ID
) X ON X.CLT_ID = B.CLT_ID
AND X.PARTIC_PRFL_EDT = B.PARTIC_PRFL_EDT
AND X.PARTIC_PRFL_CDTTM = B.PARTIC_PRFL_CDTTM
WHERE B.PARTIC_PRFL_TCD = '04' --AIRFI
AND B.CLT_ID = A.CLT_ID
AND B.BUS_PGM_ID = A.PGM_ID
AND B.CLT_ID_TCD = '01'
)
;
One thing I particularly like about DB2 is the ability to move complicated expressions or even subqueries into new virtual data fields thanks to JOIN TABLE (...), something like this:
SELECT RIGHT(DIGITS (A.CLT_ID),9) || A.PGM_ID ||
RIGHT(DIGITS (A.PGM_ACCT_SQNBR),4) AS BN
, subq.AIRFI_TCD
FROM RAMTUO#1.TSUMM A
INNER JOIN TABLE
( SELECT B.XREF_NBR,
COUNT(*)OVER() AS total_amount --number of matching entries
FROM GHMTUO#1.TPPPRFL B
WHERE B.PARTIC_PRFL_TCD = '04' --AIRFI
AND B.CLT_ID = A.CLT_ID
AND B.BUS_PGM_ID = A.PGM_ID
AND B.CLT_ID_TCD = '01'
AND B.PARTIC_PRFL_EDT = (SELECT MAX(X.PARTIC_PRFL_EDT)
FROM GHMTUO#1.TPPPRFL X
WHERE X.CLT_ID = B.CLT_ID)
AND B.PARTIC_PRFL_CDTTM = (SELECT MAX(Z.PARTIC_PRFL_CDTTM)
FROM GHMTUO#1.TPPPRFL Z
WHERE Z.CLT_ID = B.CLT_ID)
) AS subq (AIRFI_TCD, total_amount) ON subq.total_amount = 1 --instead of HAVING
WHERE A.PGM_ID = 'RT'
WITH UR;
Try it, I hope it works.

How to get all entities from a foreign key that points to an entity in the same table, reiterative?

I have a Movie table and a movie can have a previous_part. Now I would like to, for example, based on movie_id '412331' get all the movies as shown in the image with the Star Wars movies. Is this possible with SQL(MsSQL/AzureSql)?
I am sorry for the lack of information, but I have honestly no idea on how to even start making a SQL query with this problem.
Update:
Created a Recursive Query, but it only works if I give the movie that starts it all (see WHERE Prev.previous_part = 412332 in the code block). So in this example Episode V would return the other two movies and Episode VI would only return Episode VII.
With MovieList AS
(SELECT Prev.movie_id, Prev.title, Prev.description, Prev.previous_part, 1 as PrevLevel
FROM Movie as Prev
WHERE Prev.previous_part = 412332
UNION ALL
SELECT Mov.movie_id, Mov.title, Mov.description, Mov.previous_part, ML.PrevLevel + 1
FROM Movie as Mov
INNER JOIN MovieList AS ML
ON Mov.previous_part = ML.movie_id
WHERE Mov.previous_part IS NOT NULL)
SELECT * FROM MovieList
This is Recursive CTE you need:
;with ml as (
--this is Anckor Query
select movie_id, title, previous_part
from movie where movie_id = 412325
union all
--this is Recursive Query
select m.movie_id, m.title, m.previous_part
from movie m
inner join ml on ml.previous_part = m.movie_id --link current prev to parent id
--if you want sequels instead of prev's change to m.previous_part = ml.movie_id
)
select * from ml
this seems to solve your question.
select *
into #t
from (
select 123 id,'xxx'nm,1234 pid
union all
select 1234, 'xl',12345
union all
select 12345,'xlxl',123456
) x
declare #mid int = 123
select x.* from #t x
where x.id = #mid
union all
select y.* from #t x
left join #t y
on x.pid = y.id
where x.id = #mid
union all
select z.* from #t x
left join #t y
on x.pid = y.id
left join #t z
on y.pid = z.id
left join #t xx
on z.pid = xx.id
where x.id = #mid
union all
select xx.* from #t x
left join #t y
on x.pid = y.id
left join #t z
on y.pid = z.id
left join #t xx
on z.pid = xx.id
where x.id = #mid
this query pulls up to 4 movies, but you can add more following the logic.
also it only works if you search by the earliest movie id.
if you need it to search both ways this should work
declare #mid int = 12345
select distinct * from
(
select x.* from #t x
where x.id = #mid
union all
select y.* from #t x
left join #t y
on x.pid = y.id or x.id = y.pid
where x.id = #mid
union all
select z.* from #t x
left join #t y
on x.pid = y.id or x.id = y.pid
left join #t z
on y.pid = z.id or y.id = z.pid
where x.id = #mid
union all
select xx.* from #t x
left join #t y
on x.pid = y.id or x.id = y.pid
left join #t z
on y.pid = z.id or y.id = z.pid
left join #t xx
on z.pid = xx.id or z.id = xx.pid
where x.id = #mid
) x
#Igors comment is correct recursive queries if there are multiple levels of reference/heirarchy you can use a recursive common table expression. Microsoft has a good Manager/Employee example. https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
That may be way more complicated than what you are looking for it is a little unclear.
If it is just one level of reference meaning you don't have to match then match again and again to find everything you want you can just use a Self Referencing outer join.
SELECT *
FROM
dbo.movies m1
LEFT JOIN dbo.movies m2
ON m1.movied_id = m2.previous_part
WHERE
m1.title LIKE '%Star Wars%'
OR m2.movied_ID IS NOT NULL
OR m2.title LIKE '%Star Wars%'
Good first stab I think you are close. Without your dataset it is a little hard to try but here is an edit that should give you a full list of every movie and their level.
;With MovieList AS (
SELECT Prev.movie_id, Prev.title, Prev.description, Prev.previous_part, 1 as PrevLevel
FROM Movie as Prev
WHERE Prev.previous_part IS NULL
UNION ALL
SELECT Mov.movie_id, Mov.title, Mov.description, Mov.previous_part, ML.PrevLevel + 1
FROM Movie as Mov
INNER JOIN MovieList AS ML
ON Mov.previous_part = ML.movie_id
)
SELECT * FROM MovieList
Note you can add a where statement on your SELECT * FROM MovieList to limit the list however you want after you generate the hierarchy.

Oracle Table alias in join

is it possible to use the table alias in an ON-Statement?
SQL Statement:
SELECT XMLELEMENT("row", XMLATTRIBUTES(rownum as "order"),
(
SELECT
XMLAGG(XMLELEMENT("attribute",XMLATTRIBUTES(z as "identifier") ) )
FROM b
LEFT JOIN c
ON c.ID = b.cID
AND c.example = table_alias.example
)
)
FROM
(
SELECT example FROM x ORDER BY y
) table_alias
I'm getting the error that table_alias.example is an invalid identifier.
If I move the c.example = table_alias.example into a WHERE Statement it works, but of course I'll get the wrong result.
Anyone have an idea?
TY
frgtv10
try this
SELECT XMLELEMENT("row", XMLATTRIBUTES(rownum as "order"),
( SELECT XMLAGG(XMLELEMENT("attribute"),XMLATTRIBUTES(z as "identifier") ) )
FROM b, c, x
where c.ID = b.cID(+) AND c.example = x.example
)
) from dual