600,000レコードを少し超えるテーブル(MainTable
)があります。親/子タイプの関係で、2番目のテーブル(JoinTable
)を介して自身に結合します。
SELECT Child.ID, Parent.ID
FROM MainTable
AS Child
JOIN JoinTable
ON Child.ID = JoinTable.ID
JOIN MainTable
AS Parent
ON Parent.ID = JoinTable.ParentID
AND Parent.SomeOtherData = Child.SomeOtherData
すべての子レコードには親レコードがあり、JoinTableのデータは正確です。
このクエリを実行すると、文字通り数分で実行されます。ただし、左結合を使用して親に結合すると、実行に1秒未満かかります。
SELECT Child.ID, Parent.ID
FROM MainTable
AS Child
JOIN JoinTable
ON Child.ID = JoinTable.ID
LEFT JOIN MainTable
AS Parent
ON Parent.ID = JoinTable.ParentID
AND Parent.SomeOtherData = Child.SomeOtherData
WHERE ...[some info to make sure we don't select parent records in the child dataset]...
INNER JOIN
とLEFT JOIN
の結果の違いを理解しました。この場合、すべての子が親を持つのとまったく同じ結果を返します。両方のクエリを実行すると、データセットを比較できますが、まったく同じです。
LEFT JOIN
がINNER JOIN
よりもはるかに高速に実行されるのはなぜですか?
UPDATEクエリプランをチェックし、内部結合を使用する場合は、親データセットから開始します。左結合を行う場合、子データセットから始まります。
使用するインデックスはすべて同じです。
常に子供から始めるように強制できますか?左結合を使用するとうまくいきます。
以前に同様の質問がここで行われましたが、私の質問に答える人はいないようです。
例えば SQL ServerでのINNER JOINとLEFT JOINのパフォーマンス で選択した答えは、左結合は常に内部結合よりも遅いことを示しています。議論は理にかなっていますが、私が見ているものではありません。
SQLは最初に小さい選択を実行してから、この小さいレコードセットに結合するように強制されるため、左結合はより高速に見える。何らかの理由で、オプティマイザはこれを自然に実行したくありません。
結合を正しい順序で強制する3つの方法:
これを試してみてください。同じ結果、異なるアプローチ:
SELECT c.ID, p.ID
FROM
(SELECT Child.ID, JoinTable.ParentID
FROM MainTable
AS Child
JOIN JoinTable
ON Child.ID = JoinTable.ID) AS c
INNER JOIN
(SELECT Parent.ID, JoinTable.ID
FROM MainTable
AS Parent
JOIN JoinTable
ON Parent.ID = JoinTable.ParentID
AND Parent.SomeOtherData = Child.SomeOtherData) AS p
ON c.ParentID = p.ID
問題が解決しない場合は、cteを使用します。
;WITH cte AS
(SELECT Child.ID, JoinTable.ParentID
FROM MainTable
AS Child
JOIN JoinTable
ON Child.ID = JoinTable.ID)
SELECT cte.ID, Parent.ID
FROM cte INNER JOIN
MainTable
AS Parent
ON Parent.ID = cte.ParentID
AND Parent.SomeOtherData = cte.SomeOtherData