SQL Server Left Join With 'Or' Operator

前端 未结 4 1621
Happy的楠姐
Happy的楠姐 2021-01-03 19:21

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

相关标签:
4条回答
  • 2021-01-03 19:42

    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
    
    0 讨论(0)
  • 2021-01-03 19:49

    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.

    0 讨论(0)
  • 2021-01-03 19:49

    another way to write it:

    LEFT JOIN Child c ON c.ParentAId = COALESCE(a.ParentAId, b.ParentBId)

    Edit

    One possible approach is querying first the MidParentA and then the MidParentB and then UNION the results:

    SELECT tlp.*,
           a.MidParentAId,
           null MidParentBId,
           c.ChildId
    FROM TopLevelParent tlp
    LEFT JOIN MidParentA a ON tlp.TopLevelPatientId = a.TopLevelPatientId
    LEFT JOIN Child c ON c.MidParentAId = a.MidParentAId 
    UNION
    SELECT tlp.*,
           null MidParentAId,
           b.MidParentBId,
           c.ChildId
    FROM TopLevelParent tlp
    LEFT JOIN MidParentB b ON tlp.TopLevelPatientId = b.TopLevelPatientId
    LEFT JOIN Child c ON c.MidParentBId = b.MidParentBId 
    

    A demo in SQLFiddle

    0 讨论(0)
  • 2021-01-03 19:57

    You should take care of using predicates inside On.

    "It is very important to understand that, with outer joins, the ON and WHERE clauses play very different roles, and therefore, they aren’t interchangeable. The WHERE clause still plays a simple filtering role—namely, it keeps true cases and discards false and unknown cases. Use something like this and use predicates in where clause. However, the ON clause doesn’t play a simple filtering role; rather, it’s more a matching role. In other words, a row in the preserved side will be returned whether the ON predicate finds a match for it or not. So the ON predicate only determines which rows from the nonpreserved side get matched to rows from the preserved side—not whether to return the rows from the preserved side." **Exam 70-461: Querying Microsoft SQL Server 2012

    0 讨论(0)
提交回复
热议问题