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
この結合を行うためのより高性能な方法はありますか?
最後に、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)。
回答に時間を割いてくれたすべての人に感謝します。
クエリの露出が少ないことを考えると、非常に大まかな経験則は、テーブルのスキャンを回避するために、OrをUnionに置き換えることです。
Select..
LEFT JOIN Child c ON c.ParentAId = a.ParentAId
union
Select..
left Join Child c ON c.ParentBId = b.ParentBId
On内の述語の使用に注意する必要があります。
「外部結合では、ON句とWHERE句は非常に異なる役割を果たし、したがって交換可能ではないことを理解することが非常に重要です。WHERE句は依然として単純なフィルタリングの役割を果たします。つまり、真のケースと破棄を保持しますfalseおよび不明なケース。このようなものを使用し、where句で述語を使用しますが、ON句は単純なフィルタリングの役割を果たしません;むしろ、より一致する役割です。 ON述語が一致するかどうかを返します。したがって、ON述語は、保存された側の行を返すかどうかではなく、保存されていない側の行と保存された側の行を一致させるかどうかのみを決定します。 **試験70-461:Microsoft SQL Server 2012のクエリ
別の書き方:
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 のデモ