継承したデータベースでいくつかのクエリの最適化に取り組んでいます。クエリを開示することは許可されていませんが、(私にとって)非常に奇妙な動作を示しているクエリプランの匿名バージョンを提示できます。
私の質問は、画像の(1)で示されている「クラスター化インデックススキャン」についてです。私が理解しているように、黄色で強調表示されている「クラスター化インデックススキャン」には依存関係がありません。操作には述語がなく(ここでは示していませんが)、ツリー内の兄弟(2)は、行数(1)が返すのと同じ回数実行されます-これは、 (1)が最初に実行され、(2)が結合に対して行ごとに実行されます(結合は「ネストされたループ」であるため、これは理にかなっています)。
問題は次のとおりです。(1)の「推定行数」は46です。「実際の行数」は283972で、これはそのテーブルの行数です正確に...それを削減するものがないので、これは理にかなっています。クエリが非常に複雑(4レベルのネストされたビュー)であることは知っていますが、SQL Serverはこれをどのように間違っているのでしょうか?述語のないインデックススキャンで返される行数の推定値がテーブルの行数と等しくないのはなぜですか?
関連するすべてのテーブル統計を更新しましたが、成功しませんでした。また、クエリのネストレベルを下げると、結果は「不安定」ですが、問題の発生頻度が低くなるという影響があることにも気づきました。このビューを参照するクエリは多数あり、このビューのネストを部分的に減らすと、いくつかのクエリが修正される可能性がありますが、さらにいくつか破損する可能性があります。
クラスター化されたインデックススキャンは、SQLServerがテーブルからすべての行を読み取ることを期待しているとは限らないことを理解することが重要です。一部のクエリでは、SQL Serverは、必要なすべての行を見つけた後、早期に停止できます。プランに表示されている推定行数が少ないのは、 行の目標 が原因である可能性があります。
簡単な例を見てみましょう。共有値なしで2つのヒープテーブルを作成するとします。
CREATE TABLE X_HEAP_1 (ID INT NOT NULL);
INSERT INTO X_HEAP_1 WITH (TABLOCK)
SELECT N FROM dbo.GetNums(1000000)
WHERE N % 10 <> 5;
CREATE TABLE X_HEAP_2 (ID INT NOT NULL);
INSERT INTO X_HEAP_2 WITH (TABLOCK)
SELECT N FROM dbo.GetNums(1000000)
WHERE N % 10 = 5;
次のクエリは行を返さないことを知っています。
SELECT TOP (10) H1.ID
FROM X_HEAP_1 h1
INNER JOIN X_HEAP_2 h2 ON h1.ID = h2.ID;
テーブルにはインデックスがないため、SQLServerは両方のテーブルからすべての行を読み取るように強制されます。ただし、SQL Serverは、クエリが行を返さないことを認識していません。実際、統計とモデリングの仮定に基づいて、クエリが10行全体を返すことを期待しています。また、10行に到達するために、両方のテーブルのすべての行をスキャンする必要がないことも想定しています。推定計画は次のとおりです。
クエリオプティマイザは、X_HEAP_1
から89行を取得した後、両方のテーブルから合計10行が一致すると考えています。ただし、実際の計画では、X_HEAP_1
から900000行すべてをスキャンする必要があることはすでにわかっています。
これは、行の目標が10000Xオフの推定行数につながる方法の簡単な例です。