Inner join on multiple subqueries? - sql

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.

Related

How to convert Subquery to join?

Is this possible to convert the subquery by using JOIN ?
Select * from WB.Email WHERE CVALID = 'V' AND HSESID IN (
Select HSESID from WB.SDATA WHERE CSTART = 'Y' AND DPERIOD IN (select DPERIOD from WB.PERIOT WHERE CVALID = 'Y' )
AND DJOUR = (CURRENT DATE + 15 DAYS))
select e.*
from WB.Email e
join WB.SDATA s on e.HSESID = s.HSESID
join WB.PERIOT p on s.DPERIOD = p.DPERIOD
where e.CVALID = 'V'
AND s.CSTART = 'Y'
AND s.DJOUR = CURRENT_DATE + 15 DAYS
AND p.CVALID = 'Y'
Perhaps you need to do SELECT DISTINCT to remove duplicates.

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

SQL sum and subtract issue with two queries

OK, I need somehelp. I have the following two queries:
SELECT DA.OWNG_OCD AS OFFICE, 'FL' AS STATE, SUM(S.STK_END_SEQ_NUM -
S.STK_STRT_SEQ_NUM) + COUNT(*) AS TOTSTK FROM STKRNG S, DFACCT DA, CMPNT C
WHERE RANGE_USED_SW = 'N' AND S.DFTACCT_CANUM = DA.DFTACCT_CANUM
AND DA.OWNG_OCD = C.OCD AND C.ST = 'FL' AND S.STK_TYP = 'R' GROUP
BY DA.OWNG_OCD;
AND
SELECT C.OCD, COUNT(*) AS USED FROM DRAFT D
JOIN STKRNG S ON S.DFTACCT_CANUM = D.DFTACCT_CANUM
JOIN DFACCT DA ON S.DFTACCT_CANUM = DA.DFTACCT_CANUM
JOIN CMPNT C ON CMPNT.OCD = DA.OWNG_OCD
WHERE D.DRFT_SEQ_NUM >= (SELECT MIN(S.STK_STRT_SEQ_NUM) FROM STKRNG S
WHERE D.DFTACCT_CANUM = S.DFTACCT_CANUM AND S.RANGE_USED_SW = 'N')
AND D.DRFT_SEQ_NUM <= (SELECT MAX(S.STK_END_SEQ_NUM) FROM STKRNG S WHERE
D.DFTACCT_CANUM = S.DFTACCT_CANUM AND S.RANGE_USED_SW = 'N')
AND S.STK_TYP = 'R'
AND S.RANGE_USED_SW = 'N'
AND C.ST = 'FL'
GROUP BY C.OCD;
I am trying to write one query where the results of the COUNT in the second query are subtracted from the results of the COUNT in the first query. Any idea on how to do this?
Put your queries in the from clause of your final query:
select q1.totstk - q2.used
from ( <your first query here> ) q1
join ( <your second query here> ) q2 on q2.ocd = q1.office;
try Something like this:
with STKRNGMINMAX as (
SELECT S.DFTACCT_CANUM,
MIN(S.STK_STRT_SEQ_NUM) MINNUM, MAX(S.STK_END_SEQ_NUM) MAXNUM,
SUM(S.STK_END_SEQ_NUM - S.STK_STRT_SEQ_NUM) DIFFNUM
FROM STKRNG S
WHERE (S.RANGE_USED_SW, S.STK_TYP)=('N', 'R')
group by S.DFTACCT_CANUM
)
SELECT C.OCD, S.DIFFNUM - COUNT(*) AS TOTSTK,
FROM DRAFT D
INNER JOIN STKRNGMINMAX S ON S.DFTACCT_CANUM = D.DFTACCT_CANUM and D.DRFT_SEQ_NUM between S.MINNUM AND S.MAXNUM
INNER JOIN DFACCT DA ON S.DFTACCT_CANUM = DA.DFTACCT_CANUM
INNER JOIN CMPNT C ON C.OCD = DA.OWNG_OCD and C.ST='FL'
GROUP BY C.OCD;

SQL Query with Left Join slows down

Cleaning up some mess in a query that was written couple of years ago and I noticed that the below query is the cause of slowing down the result. I tried to search for alternatives but was not too successful.
WITH cat
AS (SELECT main.category_id main_id,
main.description main_desc,
sub1.category_id sub1_id,
sub1.description sub1_desc,
sub2.category_id sub2_id,
sub2.description sub2_desc,
sub3.category_id sub3_id,
sub3.description sub3_desc,
NVL (
NVL (NVL (sub3.category_id, sub2.category_id),
sub1.category_id),
main.category_id)
lowest
FROM iod.sr_category main
-- We need to insert a temporary null row and include it in the join so the categories which are not leaves are also included
LEFT JOIN
(SELECT category_id, description, parent_id
FROM iod.sr_category
UNION
SELECT NULL, NULL, NULL
FROM DUAL) sub1
ON ( main.category_id = sub1.parent_id
OR sub1.category_id IS NULL)
LEFT JOIN
(SELECT category_id, description, parent_id
FROM iod.sr_category
UNION
SELECT NULL, NULL, NULL
FROM DUAL) sub2
ON ( sub1.category_id = sub2.parent_id
OR sub2.category_id IS NULL)
LEFT JOIN
(SELECT category_id, description, parent_id
FROM iod.sr_category
UNION
SELECT NULL, NULL, NULL
FROM DUAL) sub3
ON ( sub2.category_id = sub3.parent_id
OR sub3.category_id IS NULL)
WHERE main.parent_id IS NULL
AND (main.category_id IN ( :main_category))
AND ( NVL ( :sub_category1, 0) = 0
OR sub1.category_id = :sub_category1)
AND ( NVL ( :sub_category2, 0) = 0
OR sub2.category_id = :sub_category2)
AND ( NVL ( :sub_category3, 0) = 0
OR sub3.category_id = :sub_category3))
SELECT r.sector_report_id,
r.created_date report_created_date --, nvl(f.sch_dep_date_local_port, f.sch_dep_date_gmt) report_search_date
,
c.main_desc,
c.sub1_desc,
c.sub2_desc,
c.sub3_desc,
p.problem_type_code,
p.description,
e.first_name,
e.last_name,
e.employee_id,
cl.description class,
ROW_NUMBER ()
OVER (PARTITION BY r.sector_report_id, cl.class_id
ORDER BY q.question_id)
question_num,
q.label question,
q.data_type,
NVL (
pa.answer,
NVL (
a.VALUE,
NVL (a.free_text,
NVL (a.hours || ':' || a.minutes, 'NO ANSWER'))))
answer,
f.arln_code,
f.flight_number,
f.sch_dep_date_gmt,
f.sch_dep_date_local_port,
f.sch_dep_apt,
f.sch_arv_apt,
f.tail_number,
f.fleet_code,
LISTAGG (
e_cmt.first_name
|| ' '
|| e_cmt.last_name
|| ' '
|| cmt.created_date
|| ':'
|| cmt.comment_text,
' ' || CHR (10))
WITHIN GROUP (ORDER BY cmt.created_date)
comments,
(NVL ( (SELECT COUNT (*)
FROM iod.sr_photograph p
WHERE p.sector_report_id = r.sector_report_id),
0))
AS photo
FROM iod.sr_sector_report r
JOIN cat c ON c.lowest = r.category_id
LEFT JOIN iod.sr_problem_type p
ON p.problem_type_id = r.problem_type_id
LEFT JOIN iod.ops_flight_leg f
ON f.ops_flight_leg_id = r.ops_flight_leg_id
LEFT JOIN iod.employee e ON e.employee_id = r.employee_id
LEFT JOIN iod.sr_class_sector_report csr
ON csr.sector_report_id = r.sector_report_id
LEFT JOIN iod.sr_class cl ON cl.class_id = csr.class_id
LEFT JOIN iod.sr_answer a ON a.sector_report_id = r.sector_report_id
LEFT JOIN iod.sr_predefined_answer pa
ON pa.predefined_answer_id = a.predefined_answer_id
LEFT JOIN iod.sr_question q ON q.question_id = a.question_id
LEFT JOIN iod.sr_comment cmt
ON cmt.sector_report_id = r.sector_report_id
LEFT JOIN iod.employee e_cmt ON e_cmt.employee_id = cmt.employee_id
WHERE (NVL ( :problem_type, 0) = 0 OR p.problem_type_id = :problem_type)
AND TRUNC (f.sch_dep_date_local_port) BETWEEN :from_date AND :TO_DATE
--and cast(nvl(f.sch_dep_date_local_port, f.sch_dep_date_gmt) as date) between :from_date and :to_date
AND (--nvl(:station_from, ' ') = ' ' or
f.sch_dep_apt IN ( :station_from))
AND (--nvl(:station_to, ' ') = ' ' or
f.sch_arv_apt IN ( :station_to))
GROUP BY r.sector_report_id,
r.created_date,
c.main_desc,
c.sub1_desc,
c.sub2_desc,
c.sub3_desc,
p.problem_type_code,
p.description,
e.first_name,
e.last_name,
e.employee_id,
cl.description,
cl.class_id,
q.label,
q.data_type,
q.question_id,
NVL (
pa.answer,
NVL (
a.VALUE,
NVL (a.free_text,
NVL (a.hours || ':' || a.minutes, 'NO ANSWER')))),
f.arln_code,
f.flight_number,
f.sch_dep_date_gmt,
f.sch_dep_date_local_port,
f.sch_dep_apt,
f.sch_arv_apt,
f.tail_number,
f.fleet_code
ORDER BY TRUNC (f.sch_dep_date_local_port) ASC
so the part
subx.category_id = suby.parent_id OR suby.category_id is null
slows down the query. Any ideas?
Without an explain plan or an understanding of the business logic being implemented all we can do is guess. But this does seem like a scenario where sub-query factoring could help:
with sub as (
SELECT category_id, description, parent_id FROM sr_category
UNION
SELECT null, null, null FROM DUAL)
select
....
from
....
LEFT JOIN sub sub1
ON (main.category_id = sub1.parent_id OR sub1.category_id is null)
LEFT JOIN sub sub2
ON (sub1.category_id = sub2.parent_id OR sub2.category_id is null)
LEFT JOIN sub sub3
ON (sub2.category_id = sub3.parent_id OR sub3.category_id is null)
WHERE main.parent_id is null
AND (main.category_id IN (:main_category))
AND (nvl(:sub_category1, 0) = 0 OR sub1.category_id = :sub_category1)
AND (nvl(:sub_category2, 0) = 0 OR sub2.category_id = :sub_category2)
AND (nvl(:sub_category3, 0) = 0 OR sub3.category_id = :sub_category3))
The WITH clause is covered in the Oracle SQL documentation. Find out more

DB2 Union causes time out?

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.