Speed up SQL query performance with nested queries - sql

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.

Related

Updating upon a join

I can simply use a select statment on a join to pull results I'd Like using:
Select * from RESULTS (NOLOCK) left join orders on Results.ordno = orders.ordno
left join folders on folders.folderno = orders.folderno
left join pranaparms on folders.prodcode = pranaparms.prodcode and results.analyte = pranaparms.analyte
WHERE Results.s <> 'OOS-A' and Results.Final Between pranaparms.LOWERQCLIMIT and pranaparms.UPPERQCLIMIT and (pranaparms.LOWERQCLIMIT IS NOT NULL and pranaparms.UPPERQCLIMIT IS NOT NULL)
and results.ordno in (1277494)
However, is there a convenient way which I could do an update on the selected fields?
I have tried this so far:
Update RESULTS (NOLOCK) left join orders on Results.ordno = orders.ordno
left join folders on folders.folderno = orders.folderno
left join pranaparms on folders.prodcode = pranaparms.prodcode and results.analyte = pranaparms.analyte
set Results.S = 'OOS-B'
WHERE Results.s <> 'OOS-A' and Results.Final Between pranaparms.LOWERQCLIMIT and pranaparms.UPPERQCLIMIT and (pranaparms.LOWERQCLIMIT IS NOT NULL and pranaparms.UPPERQCLIMIT IS NOT NULL)
and results.ordno in (1277494)
However, it is passing an error indicating "Incorrect syntax near '('"
Is there a way for me to update off of this join or will I need to do tables individually?
Your select syntax suggests SQL Server. The correct update syntax in SQL Server is:
update r
set S = 'OOS-B'
from results r left join
orders o
on r.ordno = o.ordno left join
folders f
on f.folderno = o.folderno left join
pranaparms p
on f.prodcode = p.prodcode and r.analyte = p.analyte
where r.s <> 'OOS-A' and
r.Final Between p.LOWERQCLIMIT and p.UPPERQCLIMIT and
(p.LOWERQCLIMIT IS NOT NULL and p.UPPERQCLIMIT IS NOT NULL) and
r.ordno in (1277494);
Notes:
Table aliases make the query much easier to write and to read.
Do not use NOLOCK unless you know what you are doing. Given that you don't know the syntax rules for update for the database you are using, I will guess that you don't understand locks either.
Your WHERE conditions are turning the outer joins into inner joins. You should just specify the correct join type -- and probably move the conditions to the on clauses. I've left the logic as you wrote it.

Oracle (Netsuite) SQL one join limit results

I have an oracle SQL query and a slight problem. I need to check if an item has a PO# that it has at least 1 line item. The query below works however it returns a result for each line of transaction_lines and I need only une result. PS I tried DISTINCT but get an ODBC error.
SELECT ITEMS.NAME, INVENTORY_NUMBER.INVENTORY_NUMBER, INVENTORY_NUMBER.ON_HAND_COUNT, ITEMS.SALESDESCRIPTION, CONDITION.LIST_ITEM_NAME,
BRAND_PARTNER.LIST_ITEM_NAME, PPROGRAM.LIST_ITEM_NAME, ENTITY.NAME, PO.TRANSACTION_NUMBER, INVENTORY_NUMBER.RECEIVED_COST, ITEMS.SALESPRICE, IR.TRANSACTION_NUMBER,
INVENTORY_SOURCE.LIST_ITEM_NAME, LOCATIONS.NAME, INVENTORY_NUMBER.RECEIPT_DATE, PO.INTERNAL_MEMO, INVENTORY_NUMBER.REFERENCE_, TEST_RESULTS.LIST_ITEM_NAME,
INVENTORY_NUMBER.TEST_FILE_LINK, INVENTORY_NUMBER.CONNECT_TRADE_ID, INVENTORY_NUMBER.SOLD_DATE, INVENTORY_NUMBER.SOLD_PRICE, INVENTORY_NUMBER.MEMO, ITEMS.UPC_CODE, ITEMS.MPN,
ITEMS.ITEM_ID, INVENTORY_NUMBER.CLEI, INVENTORY_NUMBER.CERTIFICATION_REF_ID
FROM INVENTORY_NUMBER
INNER JOIN ITEMS ON INVENTORY_NUMBER.ITEM_ID = ITEMS.ITEM_ID
INNER JOIN TRANSACTIONS AS PO ON INVENTORY_NUMBER.PURCHASE_ORDER_ID = PO.TRANSACTION_ID
INNER JOIN TRANSACTIONS AS IR ON INVENTORY_NUMBER.ITEM_RECEIPT_ID = IR.TRANSACTION_ID
INNER JOIN TRANSACTION_LINES ON PO.TRANSACTION_ID = TRANSACTION_LINES.TRANSACTION_ID
INNER JOIN ENTITY ON TRANSACTIONS.ENTITY_ID = ENTITY.ENTITY_ID
INNER JOIN CONDITION ON INVENTORY_NUMBER.CONDITION_ID = CONDITION.LIST_ID
INNER JOIN BRAND_PARTNER ON INVENTORY_NUMBER.BRAND_PARTNER_ID = BRAND_PARTNER.LIST_ID
INNER JOIN PPROGRAM ON INVENTORY_NUMBER.PROGRAM_ID = PPROGRAM.LIST_ID
INNER JOIN INVENTORY_SOURCE ON INVENTORY_NUMBER.INVENTORY_SOURCE_ID = INVENTORY_SOURCE.LIST_ID
INNER JOIN LOCATIONS ON INVENTORY_NUMBER.LOCATION_ID = LOCATIONS.LOCATION_ID
INNER JOIN TEST_RESULTS ON INVENTORY_NUMBER.TEST_RESULTS_ID = TEST_RESULTS.LIST_ID
WHERE INVENTORY_NUMBER.ON_HAND_COUNT IS NOT NULL AND ((INVENTORY_NUMBER.PURCHASE_ORDER_ID IS NULL) OR (INVENTORY_NUMBER.PURCHASE_ORDER_ID IS NOT NULL AND TRANSACTION_LINES.TRANSACTION_LINE_ID IS NOT NULL))
you could also remove the join to the transaction_lines and instead of tl.TRANSACTION_LINE_ID IS NOT NULL use an exists clause
and exists (select 1 from transaction lines tl
where tl.transaction_id = po.transaction_id)
I would suggest using a GROUP BY to help limit your results. You could also if you are interacting with transactions in your query you must always remember that without limiting results based on the "main line" you will receive the header record and then a record for each individual line item.
If you were doing this with a saved search you could put the criteria as "main line = true". Since I don't understand your query entirely I can't advise where to put this limitation in.

Simplifying where clause when a column is mutual for the tables at from clause

I would like to learn if there is any more efficient way to write the query below:
SELECT *
FROM requests srp
INNER JOIN surgeons rpsur
ON rpsur.id = srp.surgeon_id
LEFT OUTER JOIN #usersurgeons usersurgeons
ON usersurgeons.surgeon_id = srp.surgeon_id
LEFT OUTER JOIN surgeons LOsurgeons
ON usersurgeons.surgeon_id = LOsurgeons.id
LEFT OUTER JOIN provsurgeons LOprovsurgeons
ON LOprovsurgeons.id = LOsurgeons.provsurgeon_id
INNER JOIN #selectedsurgeons up
ON up.surgeon_id = rpsur.id
LEFT OUTER JOIN provsurgeons ps
ON ps.id = rpsur.provsurgeon_id
WHERE rpsur.isprimary = 0
AND usersurgeons.isprimary = 0
AND LOsurgeons.isprimary = 0
AND LOprovsurgeons.isprimary = 0
AND up.isprimary = 0
AND ps.isprimary = 0
I am not happy with the where clause here, is there any more professional way to write this, rather than adding the clauses to the join lines (such as on xx.id = yy.id and xx.isPrimary=0)??
From this query alone there are not many things that can be said. You should consider adding some more context (how do you get data into those temporary tables and the structure of %surgeons tables):
1) Select * makes almost impossible to use any index and also provides a lot of columns (Requests.*, surgeons.*, Provsurgeons.* etc.) in your final result. Return only the columns that you need.
2) If isPrimary = 0 filtering is performed often in your queries (not just this one), you can consider creating a view that fetches data filtered by isPrimary = 0. E.g. vwSurgeons, vwProvsurgeons. Then, you can just JOIN directly to the view instead of the table.
3) [already mentioned in the comments] Any condition that excludes NULL values for the OUTER JOINed table will transform the OUTER into INNER.
Instead of joining all tables and having a where clause at the end, use a derived tables only with filtered records. This way your query performance will be better.
SELECT *
FROM requests srp
INNER JOIN surgeons rpsur
ON rpsur.id = srp.surgeon_id
LEFT OUTER JOIN
(
SELECT *
FROM #usersurgeons
WHERE isprimary = 0
)usersurgeons
ON usersurgeons.surgeon_id = srp.surgeon_id
LEFT OUTER JOIN
(
SELECT *
FROM surgeons
WHERE isprimary = 0
)LOsurgeons
ON usersurgeons.surgeon_id = LOsurgeons.id
LEFT OUTER JOIN
(
SELECT *
FROM provsurgeons
WHERE isprimary = 0
)LOprovsurgeons
ON LOprovsurgeons.id = LOsurgeons.provsurgeon_id
INNER JOIN
(
SELECT *
FROM #selectedsurgeons
WHERE isprimary = 0
)up
ON up.surgeon_id = rpsur.id
LEFT OUTER JOIN
(
SELECT *
FROM provsurgeons
WHERE isprimary = 0
) ps
ON ps.id = rpsur.provsurgeon_id
WHERE rpsur.isprimary = 0

Access left join not working as I am picturing it

I am trying to write a query that involves 3 tables and left joining two of them onto a main one.
SELECT UNIT_MAIN.UNIT_NO, DEPT_MAIN.LEV_2, Card.CardNumberLong AS [Some
Number], Card.Enabled, F_CARD.CARD_NO, F_CARD.END_DT
FROM (((UNIT_MAIN
INNER JOIN DEPT_MAIN ON UNIT_MAIN.USING_DEPT = DEPT_MAIN.DEPT_ID)
LEFT JOIN Card ON (UNIT_MAIN.UNIT_NO = Card.UnitCode AND Card.Enabled = True) )
LEFT JOIN F_CARD ON (UNIT_MAIN.UNIT_ID = F_CARD.ASSIGNED_ID AND (F_CARD.END_DT) Is Null ))
WHERE (((UNIT_MAIN.STATUS)="A") AND ((DEPT_MAIN.LEV_2)="AM") AND ((Card.Enabled)=True) )
OR (((UNIT_MAIN.STATUS)="D") AND ((DEPT_MAIN.LEV_2)="AM") AND ((Card.Enabled)=True) )
The issue I am having is when F_CARD table has rows where the F_CARD.END_DT is not null, causing the main table (unit table) not to show up even though it is a left join and the F_CARD table rows did not satisfy the join condition (or I am to believe).
I don't have any where clauses on the F_CARD table and they are only on the join condition.
edit
When I perform
LEFT JOIN MFIVE_F_CARD ON (MFIVE_UNIT_DEPT_COMP_MAIN.UNIT_ID = MFIVE_F_CARD.ASSIGNED_ID AND ((MFIVE_F_CARD.END_DT) Is Null)
The unit does not appear if the F_CARD table contained rows that had an END_DT, I was to believe that since the left join condition failed, the inner table (unit table) should appear regardless.
If I remove any F_CARD related values from the query, the missing units I am looking for appear. It is an inner join, left join, left join. When the second left join happens, I lose rows even when they should appear.
I narrowed down my joins and just did the inner with the left with F_CARD. Trying to see why it doesnt return rows where the join fails.
Tried to do the following, however im getting an unsupported join error...
SELECT
UNIT_MAIN.UNIT_NO
,DEPT_MAIN.LEV_2
,Card.CardNumberLong AS [SomeNumber]
,Card.Enabled
,F_CARD.CARD_NO
,F_CARD.END_DT FROM
(
(
(
UNIT_MAIN
INNER JOIN DEPT_MAIN
ON UNIT_MAIN.USING_DEPT = DEPT_MAIN.DEPT_ID
)
LEFT JOIN Card
ON (UNIT_MAIN.UNIT_NO = Card.UnitCode AND Card.Enabled = True)
)
LEFT JOIN F_CARD
ON (UNIT_MAIN.UNIT_ID = F_CARD.ASSIGNED_ID AND F_CARD.END_DT Is Null)
) WHERE
(UNIT_MAIN.STATUS = "A" OR UNIT_MAIN.STATUS = "D")
AND DEPT_MAIN.LEV_2 = "AM"
Thanks.
I have a feeling it's because you have clauses in your joins that are doing boolean checks rather than matching records between the tables, i.e.
Card.Enabled = True and (F_CARD.END_DT) IS NULL
Try changing the query to:
SELECT
UNIT_MAIN.UNIT_NO
,DEPT_MAIN.LEV_2
,Card.CardNumberLong AS [SomeNumber]
,Card.Enabled
,F_CARD.CARD_NO
,F_CARD.END_DT
FROM
(
(
(
UNIT_MAIN
INNER JOIN DEPT_MAIN
ON UNIT_MAIN.USING_DEPT = DEPT_MAIN.DEPT_ID
)
LEFT JOIN Card
ON UNIT_MAIN.UNIT_NO = Card.UnitCode
)
LEFT JOIN F_CARD
ON UNIT_MAIN.UNIT_ID = F_CARD.ASSIGNED_ID
)
WHERE
(UNIT_MAIN.STATUS = "A" OR UNIT_MAIN.STATUS = "D")
AND DEPT_MAIN.LEV_2 = "AM"
AND Card.Enabled = True

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..