I have a four tables, TopLevelParent, two mid level tables MidParentA and MidParentB, and a Child table which can have a parent of MidParentA or MidParentB (One or the other midParent must be in place). Both mid level tables have a parent table of TopLevelParent.
The Top Level table look like this:
TopLevelId | Name -------------------------- 1 | name1 2 | name2
The MidParent tables look like this:
MidParentAId | TopLevelParentId | MidParentBId | TopLevelParentId | ------------------------------------ ------------------------------------ 1 | 1 | 1 | 1 | 2 | 1 | 2 | 1 |
The Child table look like this:
ChildId | MidParentAId | MidParentBId -------------------------------- 1 | 1 | NULL 2 | NULL | 2
I have used the following left join in a larger stored procedure which is timing out, and it looks like the OR operator on the last left join is the culprit:
SELECT * FROM TopLevelParent tlp LEFT JOIN MidParentA a ON tlp.TopLevelPatientId = a.TopLevelPatientId LEFT JOIN MidParentB a ON tlp.TopLevelPatientId = b.TopLevelPatientId LEFT JOIN Child c ON c.ParentAId = a.ParentAId OR c.ParentBId = b.ParentBId
Is there a more performant way to do this join?
If you have an OR condition in the JOIN - and there is no possibility that the values in the OR statement overlap...then you can convert it to a UNION ALL. If the values overlap it would require a UNION which may not improve performance over the JOIN.
The plus sign is Oracle syntax for an outer join. There isn't a minus operator for joins. An outer join means return all rows from one table. Also return the rows from the outer joined where there's a match on the join key. If there's no matching row, return null.
Left join is applied to the tables Course and Student and the table below is the result set. The left table and its corresponding matching rows on the right table are displayed. If a user wants to display the rows only in the left table, where clause can be used in the query.
The Left Join in SQL basically returns all records from the left table and the matched records from the right tables. For example, let's say, we have two tables, Table A and Table B. When Left Join is applied on these two tables, all records from Table A and only the matched records from Table B will be displayed.
Given how little of the query is being exposed; a very rough rule of thumb is to replace an Or with a Union to avoid table scanning.
Select.. LEFT JOIN Child c ON c.ParentAId = a.ParentAId union Select.. left Join Child c ON c.ParentBId = b.ParentBId
Here is what I did in the end, which got the execution time down from 52 secs to 4 secs.
SELECT * FROM ( SELECT tpl.*, a.MidParentAId as 'MidParentId', 1 as 'IsMidParentA' FROM TopLevelParent tpl INNER JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID UNION SELECT tpl.*, b.MidParentBId as 'MidParentId', 0 as 'IsMidParentA' FROM TopLevelParent tpl INNER JOIN MidParentB b ON b.TopLevelParentId = tpl.TopLevelParentID UNION SELECT tpl.*, 0 as 'MidParentId', 0 as 'IsMidParentA' FROM TopLevelParent tpl WHERE tpl.TopLevelParentID NOT IN ( SELECT pa.TopLevelParentID FROM TopLevelParent tpl INNER JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID UNION SELECT pa.TopLevelParentID FROM TopLevelParent tpl INNER JOIN MidParentB b ON h.TopLevelParentId = tpl.TopLevelParentID ) ) tpl LEFT JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID LEFT JOIN MidParentB b ON b.TopLevelParentId = tpl.TopLevelParentID LEFT JOIN ( SELECT [ChildId] ,[MidParentAId] as 'MidParentId' ,1 as 'IsMidParentA' FROM Child c WHERE c.MidParentAId IS NOT NULL UNION SELECT [ChildId] ,[MidParentBId] as 'MidParentId' ,0 as 'IsMidParentA' FROM Child c WHERE c.MidParentBId IS NOT NULL ) AS c ON c.MidParentId = tpl.MidParentId AND c.IsMidParentA = tpl.IsMidParentA
This eliminates the table scanning that was happening, as I have matched the top level record to its midlevel parent up front if it exists, and stamped it on that record.
I have also done the same with the child record meaning I can then just join the child record to the top level record on the MidParentId, and I use the IsMidParentA bit flag to differentiate where there are two identical MidParentIds (ie an Id of 1 for IsMidParentA and IsMidParentB).
Thanks to all who took the time to answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With