web-dev-qa-db-ja.com

「Or」演算子を使用したSQL Serverの左結合

TopLevelParentの4つのテーブル、MidParentAとMidParentBの2つの中間レベルのテーブル、MidParentAまたはMidParentBの親を持つことができる子テーブルがあります(どちらか一方のmidParentを配置する必要があります)。両方の中間レベルのテーブルには、TopLevelParentの親テーブルがあります。

トップレベルテーブルは次のようになります。

TopLevelId | Name
--------------------------
1          | name1   
2          | name2   

MidParentテーブルは次のようになります。

MidParentAId | TopLevelParentId |           MidParentBId | TopLevelParentId |
------------------------------------       ------------------------------------
1            |        1         |           1            |        1         |
2            |        1         |           2            |        1         |

Childテーブルは次のようになります。

ChildId | MidParentAId | MidParentBId
--------------------------------
1       |     1        |   NULL
2       |    NULL      |     2

私はタイムアウトしている大きなストアドプロシージャで次の左結合を使用しましたが、最後の左結合のOR演算子が原因です。

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

この結合を行うためのより高性能な方法はありますか?

18
Declan McNulty

最後に、52秒から4秒に実行時間を短縮しました。

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

これにより、発生していたテーブルスキャンが不要になります。最上位レベルのレコードを、存在する場合は前もって中間レベルの親に一致させ、そのレコードにスタンプするためです。

子レコードでも同じことを行いました。つまり、MidParentIdの最上位レコードに子レコードを結合できます。IsMidParentAビットフラグを使用して、2つの同一のMidParentIdがあるかどうかを区別します(つまり、 IsMidParentAおよびIsMidParentB)。

回答に時間を割いてくれたすべての人に感謝します。

4
Declan McNulty

クエリの露出が少ないことを考えると、非常に大まかな経験則は、テーブルのスキャンを回避するために、OrをUnionに置き換えることです。

Select..
LEFT JOIN Child c ON c.ParentAId = a.ParentAId 
union
Select..
left Join Child c ON c.ParentBId = b.ParentBId
17
u07ch

On内の述語の使用に注意する必要があります。

「外部結合では、ON句とWHERE句は非常に異なる役割を果たし、したがって交換可能ではないことを理解することが非常に重要です。WHERE句は依然として単純なフィルタリングの役割を果たします。つまり、真のケースと破棄を保持しますfalseおよび不明なケース。このようなものを使用し、where句で述語を使用しますが、ON句は単純なフィルタリングの役割を果たしません;むしろ、より一致する役割です。 ON述語が一致するかどうかを返します。したがって、ON述語は、保存された側の行を返すかどうかではなく、保存されていない側の行と保存された側の行を一致させるかどうかのみを決定します。 **試験70-461:Microsoft SQL Server 2012のクエリ

5
Amir Keshavarz

別の書き方:

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

編集

可能なアプローチの1つは、最初にMidParentA、MidParentB、次にUNIONの結果をクエリすることです。

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 

SQLFiddle のデモ

1
mucio