100万行を少し超えるテーブルがあります。これらの行は、6つの異なる列(つまり、単一のParentID
列はありません)でそれ自体に結合することによって検出される同じテーブルに親レコードを持つことができます。すべての子は、これらの結合に基づいてちょうど1つの親を持ち、すべてのレコードは親または子レコードです(つまり、祖父母レコードはありません)。
SELECT *
FROM TheTable AS ChildRecords
JOIN TheTable AS ParentRecords
ON ChildRecords.Column1 = ParentRecords.Column1
AND ChildRecords.Column2 = ParentRecords.Column2
AND ChildRecords.Column3 = ParentRecords.Column3
AND ChildRecords.Column4 = ParentRecords.Column4
AND ChildRecords.Column5 = ParentRecords.Column5
AND ChildRecords.Column10 = ParentRecords.Column6
列10は列6に結合しますが、この列自体は一意の親を見つけられないことに注意してください。column10
= column6
を持つ複数の「親」が存在する可能性があります。
これは通常は正常に機能しますが、より大きなクエリの一部として使用している場合、SQL Serverは他のものを解決する前に、最初にこの結合を解決しようとします。これは、CTE内またはCTEに参加している場合に特に当てはまります。多くの場合、クエリプランで発生する最初の結合です。多くの場合、これにより結合が数万になり、その後、関心のある100件程度のレコードにフィルター処理されます。これが発生すると、クエリの実行に数分かかります。
クエリプランを左結合にすることで、クエリプランに影響を与える可能性があることに気付きました。左結合の場合、SQL Serverはすべての子が1つの親を持っていることを認識しないため、常に最初に子レコードを見つける必要があるため、これは理にかなっています。
SELECT *
FROM TheTable AS ChildRecords
LEFT JOIN TheTable AS ParentRecords
ON ChildRecords.Column1 = ParentRecords.Column1
AND ChildRecords.Column2 = ParentRecords.Column2
AND ChildRecords.Column3 = ParentRecords.Column3
AND ChildRecords.Column4 = ParentRecords.Column4
AND ChildRecords.Column5 = ParentRecords.Column5
AND ChildRecords.Column10 = ParentRecords.Column6
この方法でクエリを実行すると、時間は数分から2秒未満に短縮されます。
各子には親があるため、左結合は内部結合と同じ結果になりますが、間違っていると感じます-内部結合である必要があります。
このテーブルでインデックスが正しく設定されていることを確認しました。既存のインデックスを追加して編集しようとしましたが、クエリプランは変更されません。それはそれがそれが私が望むものだけに制限する前にすべての子/親のコンボを取得するためにクエリをしているからです。
SQL Serverにクエリを並べ替えさせるのではなく、指定した順序で結合を実行するように強制できますか?
クエリの最後にOPTION(FORCE ORDER)を指定でき、結合が正しい順序で行われるようです。 SQLが私のクエリを最適化するのを停止するため、これを行わないよう警告する人はたくさんいます。