web-dev-qa-db-ja.com

このクエリがインデックススプールを使用しないのはなぜですか?

オプティマイザの動作をよりよく理解し、インデックススプールに関する制限を理解するために、この質問をします。 1から10000までの整数をヒープに入れたとします。

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

MAXDOP 1を使用してネストされたループ結合を強制します。

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

これは、SQL Serverに対して行うのはやや不親切なアクションです。ネストされたループ結合は、両方のテーブルに関連するインデックスがない場合、多くの場合適切ではありません。ここに計画があります:

bad query

私のマシンでは、テーブルスプールから100000000行がフェッチされるため、クエリに13秒かかります。ただし、クエリを遅くする必要がある理由はわかりません。クエリオプティマイザーには、その場でインデックスを作成する機能があります インデックススプール 。このクエリは、インデックススプールの候補として最適です。

次のクエリは、最初のクエリと同じ結果を返し、インデックススプールがあり、1秒未満で終了します。

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

workaround 1

このクエリにもインデックススプールがあり、1秒未満で終了します。

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

workaround 2

元のクエリにインデックススプールがないのはなぜですか?ドキュメント化された、またはドキュメント化されていないヒント、またはインデックススプールを提供するトレースフラグのセットはありますか?私は この関連する質問 を見つけましたが、それは私の質問に完全には答えず、このクエリで不思議なトレースフラグを機能させることができません。

23
Joe Obbish

ご存知のように、オプティマイザの検索は完全ではありません。これは、文脈上意味のあることを試み、実際のクエリに対して頻繁に利益をもたらします。 2つの単一列のインデックス付けされていないヒープテーブル間でループ結合を強制することは、そのようなシナリオではありません。とはいえ、ここにいくつかの詳細があります:

SQL Serverは、結合でより多くのトリックを知っているので、変換が早い段階で結合に適用されます。後で、結合を適用に戻すことを検討する可能性があります。 2つの違い は相関パラメータ(外部参照)です。内側に適切なインデックスがある場合、適用は意味があります。あなたの例にはインデックスがないため、オプティマイザは適用への変換を探索するように説得されていません。

単純な(非適用)結合には、外部参照ではなく結合演算子の結合述語があります。非適用のスプール最適化は通常、遅延テーブルスプールです。これは、内側に述部がないため、結合時のみです。

オプティマイザは、適用を可能にするためにその場でインデックスを構築することを考慮していません。むしろ、イベントのシーケンスは通常は逆です。適切なインデックスが存在するため、適用して変換します。

クエリでAPPLY構文を使用することで、結合ではなく適用を推奨できる場合があります。文書化されていないトレースフラグ9114は、オプティマイザが論理適用を前もって結合に変換しないようにすることで、これを支援できます。例えば:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

Spool plan

外側の参照は選択が結合の内側に適用されることを意味するため、インデックススプールは適用に適しています。 SelToIndexOnTheFlyを介してこれを確認することがよくありますが、他のパスが存在します。私の記事 The Eager Index Spool and The Optimizer を参照してください。

21
Paul White 9