これは、コストベースのオプティマイザーの決定です。
この選択で使用される推定コストは、異なる列の値間の統計的な独立性を前提としているため、正しくありません。
これは、偶数と奇数が負の相関関係にある「行の目標が不正になった」で説明されている問題に似ています。
簡単に再現できます。
CREATE TABLE dbo.animal(
id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
colour varchar(50) NOT NULL,
species varchar(50) NOT NULL,
Filler char(10) NULL
);
/*Insert 20 million rows with 1% black and 1% swan but no black swans*/
WITH T
AS (SELECT TOP 20000000 ROW_NUMBER() OVER (ORDER BY @@SPID) AS RN
FROM master..spt_values v1,
master..spt_values v2,
master..spt_values v3)
INSERT INTO dbo.animal
(colour,
species)
SELECT CASE
WHEN RN % 100 = 1 THEN 'black'
ELSE CAST(RN % 100 AS VARCHAR(3))
END,
CASE
WHEN RN % 100 = 2 THEN 'swan'
ELSE CAST(RN % 100 AS VARCHAR(3))
END
FROM T
/*Create some indexes*/
CREATE NONCLUSTERED INDEX ix_species ON dbo.animal(species);
CREATE NONCLUSTERED INDEX ix_colour ON dbo.animal(colour);
今すぐ試してください
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black'
AND species LIKE 'swan'
これにより、以下のコストの計画が得られ0.0563167
ます。
プランは、id
列の2つのインデックスの結果間でマージ結合を実行できます。(マージ結合アルゴリズムの詳細はこちら)。
結合の結合では、両方の入力が結合キーで順序付けられている必要があります。
非クラスター化インデックスは(species, id)
、(colour, id)
それぞれの順序で並べられます(非一意の非クラスター化インデックスには、明示的に追加されない場合、常に暗黙的にキーの最後に行ロケーターが追加されます)。ワイルドカードを使用しないクエリは、species = 'swan'
およびへの等価シークを実行していcolour ='black'
ます。各シークは先頭の列から正確な値を1つだけ取得するため、一致する行が並べ替えられid
ます。したがって、このプランは可能です。
クエリプラン演算子は、左から右に実行されます。左の演算子が子から行を要求し、子が子から行を要求します(葉ノードに到達するまで続きます)。TOP
10が受信された後のイテレータは、その子から任意の複数の行を要求して停止します。
SQL Serverには、行の1%が各述語と一致することを示すインデックスの統計があります。これらの統計は独立している(つまり、正または負の相関がない)と想定しているため、平均して最初の述語に一致する1,000行を処理すると、2番目に一致する10行を見つけて終了できます。(上記の計画は実際には1,000ではなく987を示していますが、十分に近いです)。
実際、述語は負の相関関係にあるため、実際の計画では、200,000の一致するすべての行を各インデックスから処理する必要があることが示されていますが、結合された行が0であるため、実際に必要な検索もゼロになるため、ある程度緩和されます。
と比べて
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
次のプランを提供します 0.567943
末尾のワイルドカードの追加により、インデックススキャンが発生しました。2000万行のテーブルをスキャンする場合でも、プランのコストは非常に低くなります。
追加するとquerytraceon 9130
、さらに情報が表示されます
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
OPTION (QUERYTRACEON 9130)
SQL Serverは、約100,000行をスキャンするだけで、述語に一致する10行が見つかり、TOP
行の要求を停止できると見なすことがわかります。
繰り返しますが、これは 10 * 100 * 100 = 100,000
最後に、インデックスの交差プランを強制してみましょう
SELECT TOP 10 *
FROM animal WITH (INDEX(ix_species), INDEX(ix_colour))
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
これにより、推定コスト3.4625の並列プランが得られます
主な違いは、colour like 'black%'
述語が複数の異なる色に一致できるようになったことです。これは、その述部に一致する索引行がの順序でソートされることが保証されなくなったことを意味しますid
。
たとえば、インデックスシークlike 'black%'
は次の行を返す場合があります
+------------+----+
| Colour | id |
+------------+----+
| black | 12 |
| black | 20 |
| black | 23 |
| black | 25 |
| blackberry | 1 |
| blackberry | 50 |
+------------+----+
各色の中ではIDが順序付けられていますが、異なる色のIDはそうでない場合があります。
その結果、SQL Serverは(結合ソート演算子を追加せずに)マージ結合インデックスの交差を実行できなくなり、代わりにハッシュ結合を実行することを選択します。ハッシュ結合はビルド入力をブロックしているため、最初の計画のように1,000だけをスキャンする必要があると仮定するのではなく、一致するすべての行をビルド入力から処理する必要があるという事実がコストに反映されます。
ただし、プローブ入力は非ブロッキングであり、それから987行を処理した後にプローブを停止できると誤って推定しています。
(ノンブロッキングとブロッキングイテレータの詳細はこちら)
追加の推定行とハッシュ結合のコストが高くなると、部分クラスター化インデックススキャンは安価になります。
もちろん実際には、「部分的な」クラスター化インデックススキャンはまったく部分的ではなく、プランを比較するときに想定される10万行ではなく、2000万行全体を一気に処理する必要があります。
の値を増やすTOP
(または完全に削除する)と、最終的にCIスキャンでカバーする必要があると推定する行の数がその計画をより高価にし、インデックス交点計画に戻ります。私にとって、2つの計画のカットオフポイントはTOP (89)
vs TOP (90)
です。
クラスター化インデックスの幅に応じて異なる場合があります。
削除しTOP
てCIスキャンを強制する
SELECT *
FROM animal WITH (INDEX = 1)
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
88.0586
私の例のテーブルでは、マシン上でコストがかかります。
動物園に黒い白鳥がいないこと、および100,000行を読み取るだけではなくフルスキャンを実行する必要があることをSQL Serverが認識している場合、この計画は選択されません。
私は上の複数のカラムの統計を試していないてきたanimal(species,colour)
とanimal(colour,species)
し、上の統計情報をフィルタリングanimal (colour) where species = 'swan'
が、これらのヘルプはいずれも黒い白鳥が存在しないということを説得し、TOP 10
10万の以上の行を処理するためにスキャン意志が必要。
これは、SQL Serverが何かを検索している場合はおそらく存在するとおそらく想定する「包含の仮定」によるものです。
2008+には、行目標をオフにする文書化されたトレースフラグ4138があります。これの効果は、TOP
一致するすべての行を読み取らずに子演算子が早期に終了できるという仮定なしに計画がコストがかかることです。このトレースフラグを設定すると、自然に、より最適なインデックス交差計画が得られます。
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
OPTION (QUERYTRACEON 4138)
このプランは、両方のインデックスシークで20万行すべてを読み取るのに正しくコストがかかりますが、キールックアップにはコストがかかります(推定2000対実際の0。TOP 10
これは最大10に制限されますが、トレースフラグは考慮されません) 。それでも、計画は完全なCIスキャンよりも大幅に安価なので、選択されます。
もちろん、この計画は一般的な組み合わせには最適ではないかもしれません。白鳥など。
animal (colour, species)
理想的にanimal (species, colour)
は、両方のシナリオで複合インデックスを使用すると、クエリの効率が大幅に向上します。
複合インデックスを最も効率的に使用するには、LIKE 'swan'
をに変更する必要もあり= 'swan'
ます。
次の表は、4つのすべての順列の実行プランに示されているシーク述語と残余述語を示しています。
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| WHERE clause | Index | Seek Predicate | Residual Predicate |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| colour LIKE 'black%' AND species LIKE 'swan' | ix_colour_species | colour >= 'black' AND colour < 'blacL' | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species LIKE 'swan' | ix_species_colour | species >= 'swan' AND species <= 'swan' | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species = 'swan' | ix_colour_species | (colour,species) >= ('black', 'swan')) AND colour < 'blacL' | colour LIKE 'black%' AND species = 'swan' |
| colour LIKE 'black%' AND species = 'swan' | ix_species_colour | species = 'swan' AND (colour >= 'black' and colour < 'blacL') | colour like 'black%' |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
TOP
変数の値を難読化するということは、変数ではTOP 100
なく仮定することを意味しTOP 10
ます。これは、2つの計画の転換点が何であるかによって、助けになる場合もあれば、ならない場合もあります。