Sql subquery for DB2 - sql

The sql query requires 4 tables to be joined, which i did, and i have to display few columns out of them that satisfy a condition. Say this is the query in Where clause. Now how do I write a subquery.. to display another column (ORG_NAME,that is there in ORG_UNIT) whose contents is based on the rows that is satified by the query in Where clause.
I wrote this code, but it is not working for me:
SELECT T33.CONTRACT_NUM, T135.MINOR_ORG_NUM, T96.ORG_TYPE,T22.CFD_FLAG,
(SELECT T96.ORG_NAME
FROM ORG_UNIT T96, SUB_UNIT T135
WHERE T96.ORG_NUMBER IN (T135.MAJOR_ORG_NUMBER)) AS HEAD_ORG_NAME
FROM
ORG_UNIT T96, SUB_UNIT T135, CUST_CONTRACT T33, CONT_ASSIGNMT T22
WHERE
T96.ORG_NUMBER = T22.ORG_NUMBER
AND T22.CTR_SYS_NUM = T33.CTR_SYS_NUM
AND T96.ORG_NUMBER = T135.MINOR_ORG_NUMBER
AND T135.RELTN_TYPE = 'HOS'
AND T22.CFD_FLAG = 'Y';
For the record, T135 contains head offices numbers (MAJOR_ORG_NUMBER) and their sub - offices numbers (MINOR_ORG_NUMBER)

In SQL, use JOIN to "merge" together tables based on their a common columns.
Here is a simple guide that would give you the base idea: SQL JOIN
In SQL, it's always best to draw what you want to do, so refer to this link to see a "LEFT JOIN" picture example: LEFT JOIN
Using a "LEFT JOIN" to merge your tables (where : ORG_UNIT.ORG_NUMBER = SUB_UNIT.MAJOR_ORG_NUMBER), will look like this:
LEFT JOIN SUB_UNIT T135 ON T96.ORG_NUMBER = T135.MAJOR_ORG_NUMBER
In a query, you put a JOIN right after the "FROM", and BEFORE the "WHERE":
SELECT
T33.CONTRACT_NUM,
T135.MINOR_ORG_NUM,
T96.ORG_TYPE,
T22.CFD_FLAG,
T135.ORG_NAME AS HEAD_ORG_NAME
FROM
ORG_UNIT T96,
CUST_CONTRACT T33,
CONT_ASSIGNMT T22
LEFT JOIN SUB_UNIT T135 ON T96.ORG_NUMBER = T135.MAJOR_ORG_NUMBER
WHERE
T96.ORG_NUMBER = T22.ORG_NUMBER
AND T22.CTR_SYS_NUM = T33.CTR_SYS_NUM
AND T96.ORG_NUMBER = T135.MINOR_ORG_NUMBER
AND T135.RELTN_TYPE = 'HOS'
AND T22.CFD_FLAG = 'Y';
Notice, that you could (and SHOULD) use JOIN for merging all the tables (and avoiding using an expensive WHERE condition):
SELECT
T33.CONTRACT_NUM,
T135.MINOR_ORG_NUM,
T96.ORG_TYPE,
T22.CFD_FLAG,
T135.ORG_NAME AS HEAD_ORG_NAME
FROM
ORG_UNIT T96
LEFT JOIN SUB_UNIT T135 ON
T96.ORG_NUMBER = T135.MAJOR_ORG_NUMBER
AND T96.ORG_NUMBER = T135.MINOR_ORG_NUMBER
LEFT JOIN ON
CONT_ASSIGNMT T22 ON T96.ORG_NUMBER = T22.ORG_NUMBER
LEFT JOIN ON
CUST_CONTRACT T33 ON T22.CTR_SYS_NUM = T33.CTR_SYS_NUM
WHERE
T135.RELTN_TYPE = 'HOS'
AND T22.CFD_FLAG = 'Y';
There are several JOIN types (LEFT/RIGHT/INNER/OUTER), so see your using the one you need.

Related

Speed up SQL query performance with nested queries

Could anyone help me speed this query up? It currently take 17 minutes to run but does return the correct data and it populates a subform in MS Access. Functions in the rest of the VBA are declared as long to try to speed up more.
Here's the full query:
SELECT lots of things
FROM (((((((((((((((ngstest
INNER JOIN patients
ON ngstest.internalpatientid = patients.internalpatientid)
INNER JOIN referral
ON ngstest.referralid = referral.referralid)
INNER JOIN checker
ON ngstest.bookby = checker.check1id)
INNER JOIN ngspanel
ON ngstest.ngspanelid = ngspanel.ngspanelid)
LEFT JOIN ngspanel AS ngspanel_1
ON ngstest.ngspanelid_b = ngspanel_1.ngspanelid)
INNER JOIN status
ON ngstest.statusid = status.statusid)
INNER JOIN dbo_patient_table
ON patients.patientid = dbo_patient_table.patienttrustid)
LEFT JOIN dna
ON ngstest.dna = dna.dnanumber)
INNER JOIN status AS status_1
ON patients.s_statusoverall = status_1.statusid)
LEFT JOIN gw_gendertable
ON dbo_patient_table.genderid = gw_gendertable.genderid)
LEFT JOIN ngswesbatch
ON ngstest.wesbatch = ngswesbatch.ngswesbatchid)
LEFT JOIN checker AS checker_1
ON ngstest.check1id = checker_1.check1id)
LEFT JOIN checker AS checker_2
ON ngstest.check2id = checker_2.check1id)
LEFT JOIN checker AS checker_3
ON ngstest.check3id = checker_3.check1id)
LEFT JOIN ngspanel AS ngspanel_2
ON ngstest.ngspanelid_c = ngspanel_2.ngspanelid)
LEFT JOIN checker AS checker_4
ON ngstest.check4id = checker_4.check1id
WHERE ((ngstest.referralid IN
(SELECT referralid FROM referral
WHERE grouptypeid = 14)
AND ngstest.ngstestid IN
(SELECT ngstest.ngstestid
FROM ngsanalysis
INNER JOIN ngstest
ON ngsanalysis.ngstestid = ngstest.ngstestid
WHERE ngsanalysis.pedigree = 3302) )
AND status.statusid = 1202218800)
ORDER BY ngstest.priority,
ngstest.daterequested;
The two nested queries are strings from elsewhere in the code so are called in the vba as " & includereferralls & " And " & ParentsStatusesFilter & "
They are:
ParentsStatusesFilter = "NGSTest.NGSTestID in
(SELECT NGSTest.NGSTestID
FROM NGSAnalysis
INNER JOIN NGSTest
ON NGSAnalysis.NGSTestID = NGSTest.NGSTestID
WHERE NGSAnalysis.Pedigree IN (3302,3303,3304)"
And
includereferrals = "NGSTest.ReferralID
(SELECT referralid FROM referral WHERE referral.grouptypeid = 14)"
The query needs to remain readable (and therefore editable) so can't use things like Distinct, Group By or contain any Unions. Have tried Exists instead of In for the nested queries but that stops it from actually filtering the results.
WHERE EXISTS (SELECT NGSTest.NGSTestID
FROM NGSAnalysis
INNER JOIN NGSTest
ON NGSAnalysis.NGSTestID = NGSTest.NGSTestID
WHERE NGSAnalysis.Pedigree IN (3302,3303,3304)
So the exist clause you have there isn't tied to the outer query which would run similar to just added 1 = 1 to the where clause. I took your where clause and converted it. It should look something like this...
WHERE EXISTS (
SELECT referralid
FROM referral
WHERE grouptypeid = 14 AND ngstest.referralid = referral.referralid)
AND EXISTS (
SELECT ngsanalysis.ngstestid
FROM ngsanalysis
WHERE ngsanalysis.pedigree IN (3302,3303,3304) AND ngstest.ngstestid = ngsanalysis.ngstestid
)
AND status.statusid = 1202218800
Adding exists will speed it up a bit, but the the bulk of the slowness is the left joins. Access does not handle the left joins as well as SQL Server does. Change all your joins to inner joins and you will see the query runs very fast. This is obviously not ideal since some relationships are optional. What I have done to get around this is add a default record that replaces a null relationship.
Here is what that looks like for you: In the checker table you could add a record that represents a null value. So put a record into the checker table with check1id of -1 or 0. Then default check1id, check2id, check3id on ngstest to -1 or 0. You will need to do that type of thing for all tables you need to left join on.

Is there a way to make multiple UNION's more efficient when they all do the same joins?

I have 3 tables and I'd like to make a view that aggregates data vertically (using UNIONS) from the 3 tables. I already have a query that does what I want, but it uses a lot of repeated joins for each query, and I'm wondering if there is a way to only join up all these tables once and put the unions on top of that.
I want to join 3 tables that I will call desiredTable1, desiredTable2, and desiredTable3.
They all make use of joins on joinTable, joinTable2 and joinTable3 like so:
(select desiredTable1.id, desiredTable1.created_at, joinTable1.firstname, joinTable1.lastname, 'DESCRIPTOR_1' as "descriptor", desiredTable1.amount from desiredTable1
join joinTable3 on joinTable3.id = desiredTable1.joinTable3_id
join joinTable2 on joinTable2.id = joinTable3.joinTable2_id
join joinTable1 on joinTable1.id = joinTable2.joinTable1_id)
UNION
(select desiredTable2.id, desiredTable2.created_at, joinTable1.firstname, joinTable1.lastname, 'DESCRIPTOR_2' as "descriptor", desiredTable2.amount from desiredTable2
join joinTable3 on joinTable3.id = desiredTable2.joinTable3_id
join joinTable2 on joinTable2.id = joinTable3.joinTable2_id
join joinTable1 on joinTable1.id = joinTable2.joinTable1_id)
UNION
(select desiredTable3.id, desiredTable3.created_at, joinTable1.firstname, joinTable1.lastname, 'DESCRIPTOR_3' as "descriptor", desiredTable3.amount from desiredTable3
join joinTable3 on joinTable3.id = desiredTable3.joinTable3_id
join joinTable2 on joinTable2.id = joinTable3.joinTable2_id
join joinTable1 on joinTable1.id = joinTable2.joinTable1_id)
As you can see, I need the linked information from joinTable1 in each query, but if I can help it, I would prefer not to repeat all these joins. Is there a way I could "define" a subquery that does all those joins, then do all the unions on top of that?
You can use union all before joining:
select dt.id, dt.created_at, jt1.firstname, jt1.lastname, dt.descriptor, dt.amount
from ((select dt1.*, 'DESCRIPTOR_1' as descriptor
from desiredTable1 dt1
) union all
(select dt2.*, 'DESCRIPTOR_2' as descriptor
from desiredTable2 dt2
) union all
(select dt3.*, 'DESCRIPTOR_3' as descriptor
from desiredTable3 dt3
)
) dt join
joinTable3 jt3
on jt3.id = dt.joinTable3_id join
joinTable2 jt2
on jt2.id = jt3.joinTable2_id join
joinTable1 jt1
on jt1.id = jt2.joinTable1_id;
Note: This uses dt.* for the subqueries as a convenience. If the tables don't have the same columns, list only the ones needed for the outer query.

Ms Access - Missing operator (LEFT JOIN)

I have problem with LEFT JOIN query which is working in SQL, but when I try to query MDB database it doesn´t work.
Here it is:
Select PH.[ID], PH.[SText], PH.[Datum] ,PHpol.[SText], PHpol.[Mnozstvi], PHpol.[kcJedn], PHpol.[RelSzDPH], PHpol.[SDph], skst.[ID]
from PH LEFT JOIN PHpol ON PH.[ID] = PHpol.[RefAg]
LEFT JOIN Skz ON PHpol.[RefSKz] = Skz.[ID]
LEFT JOIN Skst ON Skz.[RefStruct] = Skst.[ID]
WHERE RelforUh = 2
AND RelCr=43
AND Datum BETWEEN #2015-01-01# AND #2015-09-01#
In MS Access, you need parentheses for multiple joins:
Select PH.[ID], PH.[SText], PH.[Datum] ,PHpol.[SText], PHpol.[Mnozstvi], PHpol.[kcJedn], PHpol.[RelSzDPH], PHpol.[SDph], skst.[ID]
from ((PH LEFT JOIN
PHpol
ON PH.[ID] = PHpol.[RefAg]
) LEFT JOIN
Skz
ON PHpol.[RefSKz] = Skz.[ID]
) LEFT JOIN
Skst
ON Skz.[RefStruct] = Skst.[ID]
WHERE RelforUh = 2 AND RelCr=43 AND
Datum BETWEEN #2015-01-01# AND #2015-09-01#
In MS-Access you need to add parentheses if you have to query on more than two tables. Structure goes like this..
Syntax for when you have two tables
Select <column list>
From Table1 Join Table2
on Table1.Col = Table2.col
where <your conditions>
Syntax for more than two tables.
Select <column list>
From (Table1 Join Table2
on Table1.Col = Table2.col)
Join Table3 on Table2.col = Table3.col
where <your conditions>
() make sure that whatever is written inside it, act as a table. If you have more tables then you can join them in the same fashion. You can read more about joining multiple tables in MS-Access here.
Well you can re-write your query as follows.
Select PH.[ID], PH.[SText], PH.[Datum] ,PHpol.[SText], PHpol.[Mnozstvi], PHpol.[kcJedn], PHpol.[RelSzDPH], PHpol.[SDph], skst.[ID]
from ((PH LEFT JOIN PHpol ON PH.[ID] = PHpol.[RefAg])
LEFT JOIN Skz ON PHpol.[RefSKz] = Skz.[ID])
LEFT JOIN Skst ON Skz.[RefStruct] = Skst.[ID]
WHERE RelforUh = 2
AND RelCr=43
AND Datum BETWEEN #2015-01-01# AND #2015-09-01#

LEFT JOIN ON COALESCE(a, b, c) - very strange behavior

I have encountered very strange behavior of my query and I wasted a lot of time to understand what causes it, in vane. So I am asking for your help.
SELECT count(*) FROM main_table
LEFT JOIN front_table ON front_table.pk = main_table.fk_front_table
LEFT JOIN info_table ON info_table.pk = front_table.fk_info_table
LEFT JOIN key_table ON key_table.pk = COALESCE(info_table.fk_key_table, front_table.fk_key_table_1, front_table.fk_key_table_2)
LEFT JOIN side_table ON side_table.fk_front_table = front_table.pk
WHERE side_table.pk = (SELECT MAX(pk) FROM side_table WHERE fk_front_table = front_table.pk)
OR side_table.pk IS NULL
Seems like a simple join query, with coalesce, I've used this technique before(not too many times) and it worked right.
In this query I don't ever get nulls for side_table.pk. If I remove coalesce or just don't use key_table, then the query returns rows with many null side_table.pk, but if I add coalesce join, I can't get those nulls.
It seems key_table and side_table don't have anything in common, but the result is so weird.
Also, when I don't use side_table and WHERE clause, the count(*) result with coalesce and without differs, but I can't see any pattern in rows missing, it seems random!
Real query:
SELECT ECHANGE.EXC_AUTO_KEY, STOCK_RESERVATIONS.STR_AUTO_KEY FROM EXCHANGE
LEFT JOIN WO_BOM ON WO_BOM.WOB_AUTO_KEY = EXCHANGE.WOB_AUTO_KEY
LEFT JOIN VIEW_WO_SUB ON VIEW_WO_SUB.WOO_AUTO_KEY = WO_BOM.WOO_AUTO_KEY
LEFT JOIN STOCK stock3 ON stock3.STM_AUTO_KEY = EXCHANGE.STM_AUTO_KEY
LEFT JOIN STOCK stock2 ON stock2.STM_AUTO_KEY = EXCHANGE.ORIG_STM
LEFT JOIN CONSIGNMENT_CODES con2 ON con2.CNC_AUTO_KEY = stock2.CNC_AUTO_KEY
LEFT JOIN CONSIGNMENT_CODES con3 ON con3.CNC_AUTO_KEY = stock3.CNC_AUTO_KEY
LEFT JOIN CI_UTL ON CI_UTL.CUT_AUTO_KEY = EXCHANGE.CUT_AUTO_KEY
LEFT JOIN PART_CONDITION_CODES pcc2 ON pcc2.PCC_AUTO_KEY = stock2.PCC_AUTO_KEY
LEFT JOIN PART_CONDITION_CODES pcc3 ON pcc3.PCC_AUTO_KEY = stock3.PCC_AUTO_KEY
LEFT JOIN STOCK_RESERVATIONS ON STOCK_RESERVATIONS.STM_AUTO_KEY = stock3.STM_AUTO_KEY
LEFT JOIN WAREHOUSE wh2 ON wh2.WHS_AUTO_KEY = stock2.WHS_ORIGINAL
LEFT JOIN SM_HISTORY ON (SM_HISTORY.STM_AUTO_KEY = EXCHANGE.ORIG_STM AND SM_HISTORY.WOB_REF = EXCHANGE.WOB_AUTO_KEY)
LEFT JOIN RC_DETAIL ON stock3.RCD_AUTO_KEY = RC_DETAIL.RCD_AUTO_KEY
LEFT JOIN RC_HEADER ON RC_HEADER.RCH_AUTO_KEY = RC_DETAIL.RCH_AUTO_KEY
LEFT JOIN WAREHOUSE wh3 ON wh3.WHS_AUTO_KEY = COALESCE(RC_DETAIL.WHS_AUTO_KEY, stock3.WHS_ORIGINAL, stock3.WHS_AUTO_KEY)
WHERE STOCK_RESERVATIONS.STR_AUTO_KEY = (SELECT MAX(STR_AUTO_KEY) FROM STOCK_RESERVATIONS WHERE STM_AUTO_KEY = stock3.STM_AUTO_KEY)
OR STOCK_RESERVATIONS.STR_AUTO_KEY IS NULL
Removing LEFT JOIN WAREHOUSE wh3 gives me about unique EXC_AUTO_KEY values with a lot of NULL STR_AUTO_KEY, while leaving this row removes all NULL STR_AUTO_KEY.
I recreated simple tables with numbers with the same structure and query works without any problems o.0
I have a feeling COALESCE is acting as a REQUIRED flag for the joined table, hence shooting the LEFT JOIN to become an INNER JOIN.
Try this:
SELECT COUNT(*)
FROM main_table
LEFT JOIN front_table ON front_table.pk = main_table.fk_front_table
LEFT JOIN info_table ON info_table.pk = front_table.fk_info_table
LEFT JOIN key_table ON key_table.pk = NVL(info_table.fk_key_table, NVL(front_table.fk_key_table_1, front_table.fk_key_table_2))
LEFT JOIN (SELECT fk_, MAX(pk) as pk FROM side_table GROUP BY fk_) st ON st.fk_ = front_table.pk
NVL might behave just the same though...
I undertood what was the problem (not entirely though): there is a LEFT JOIN VIEW_WO_SUB in original query, 3rd line. It causes this query to act in a weird way.
When I replaced the view with the other table which contained the information I needed, the query started returning right results.
Basically, with this view join, NVL, COALESCE or CASE join with combination of certain arguments did not work along with OR clause in WHERE subquery, all rest was fine. ALthough, I could get the query to work with this view join, by changing the order of joined tables, I had to place table participating in where subquery to the bottom.

Is there a better way to write this Oracle SQL query?

I have been using Oracle SQL for around 6 months so still a beginner. I need to query the database to get information on all items on a particular order (order number is via $_GET['id']).
I have come up with the below query, it works as expected and as I need but I do not know whether I am over complicating things which would slow the query down at all. I understand there are a number of ways to do a single thing and there may be better methods to write this query since I am a beginner.
I am using Oracle 8i (due to this is the version an application we use is supplied with) so I believe that some JOIN etc. are not available in this version, but is there a better way to write a query such as the below?
SELECT auf_pos.auf_pos,
(SELECT auf_stat.anz
FROM auf_stat
WHERE auf_stat.auf_pos = auf_pos.auf_pos
AND auf_stat.auf_nr = ".$_GET['id']."),
(SELECT auf_text.zl_str
FROM auf_text
WHERE auf_text.zl_mod = 0
AND auf_text.auf_pos = auf_pos.auf_pos
AND auf_text.auf_nr = ".$_GET['id']."),
(SELECT glas_daten_basis.gl_bez
FROM glas_daten_basis
WHERE glas_daten_basis.idnr = auf_pos.glas1),
(SELECT lzr_daten.lzr_breite
FROM lzr_daten
WHERE lzr_daten.lzr_idnr = auf_pos.lzr1),
(SELECT glas_daten_basis.gl_bez
FROM glas_daten_basis
WHERE glas_daten_basis.idnr = auf_pos.glas2),
auf_pos.breite,
auf_pos.hoehe,
auf_pos.spr_jn
FROM auf_pos
WHERE auf_pos.auf_nr = ".$_GET['id']."
Thanks in advance to any Oracle gurus that could help this beginner out!
You could rewrite it using joins. If your subselects aren't expected to return any NULL values, then you can use INNER JOINS:
SELECT auf_pos.auf_pos,
auf_stat.anz,
auf_text.zl_str,
glas_daten_basis.gl_bez,
lzr_daten.lzr_breite,
glas_daten_basis.gl_bez,
auf_pos.breite,
auf_pos.hoehe,
auf_pos.spr_jn
FROM auf_pos
INNER JOIN auf_stat ON auf_stat.auf_pos = auf_pos.auf_pos AND auf_stat.auf_nr = ".$_GET['id'].")
INNER JOIN auf_text ON auf_text.zl_mod = 0 AND auf_text.auf_pos = auf_pos.auf_pos AND auf_text.auf_nr = ".$_GET['id'].")
INNER JOIN glas_daten_basis ON glas_daten_basis.idnr = auf_pos.glas1
INNER JOIN lzr_daten ON lzr_daten.lzr_idnr = auf_pos.lzr1
INNER JOIN glas_daten_basis ON glas_daten_basis.idnr = auf_pos.glas2
Or if there are cases where you wouldn't have matches on all the tables, you could replace the INNER joins with LEFT OUTER joins:
SELECT auf_pos.auf_pos,
auf_stat.anz,
auf_text.zl_str,
glas_daten_basis.gl_bez,
lzr_daten.lzr_breite,
glas_daten_basis.gl_bez,
auf_pos.breite,
auf_pos.hoehe,
auf_pos.spr_jn
FROM auf_pos
LEFT OUTER JOIN auf_stat ON auf_stat.auf_pos = auf_pos.auf_pos AND auf_stat.auf_nr = ".$_GET['id'].")
LEFT OUTER JOIN auf_text ON auf_text.zl_mod = 0 AND auf_text.auf_pos = auf_pos.auf_pos AND auf_text.auf_nr = ".$_GET['id'].")
LEFT OUTER JOIN glas_daten_basis ON glas_daten_basis.idnr = auf_pos.glas1
LEFT OUTER JOIN lzr_daten ON lzr_daten.lzr_idnr = auf_pos.lzr1
LEFT OUTER JOIN glas_daten_basis ON glas_daten_basis.idnr = auf_pos.glas2
Whether or not you see any performance gains is debatable. As I understand it, the Oracle query optimizer should take your query and execute it with a similar plan to the join queries, but this is dependent on a number of factors, so the best thing to do it give it a try..