いくつかの列とともに100万行を一時テーブルにスローしてみましょう。
CREATE TABLE #174860 (
PK INT NOT NULL,
COL1 INT NOT NULL,
COL2 INT NOT NULL,
PRIMARY KEY (PK)
);
INSERT INTO #174860 WITH (TABLOCK)
SELECT RN
, RN % 1000
, RN % 10000
FROM
(
SELECT TOP 1000000 ROW_NUMBER () OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values v1,
master..spt_values v2
) t;
CREATE INDEX IX_174860_IX ON #174860 (COL1) INCLUDE (COL2);
ここでは、PK
列にクラスター化インデックス(デフォルト)があります。のCOL1
キー列とCOL1
を含む非クラスター化インデックスがありますCOL2
。
次のクエリについて考えてみます。
SELECT *
FROM #174860
WHERE PK >= 15000 AND PK < 15005
AND COL2 = 5000;
ここではBETWEEN
、Aaron Bertrandがこの質問を回避しているため、使用していません。
SQL Serverオプティマイザはどのようにクエリを実行する必要がありますか?まあ、私はフィルターをオンPK
にすると結果セットが5行に減ることを知っています。SQLサーバーは、クラスター化インデックスを使用して、テーブル内の100万行すべてを読み取る代わりに、これらの5行にジャンプできます。ただし、クラスター化インデックスには、キー列としてPK列しかありません。行がメモリに読み込まれたら、フィルタをに適用する必要がありますCOL2
。ここで、PK
はシーク述語でCOL2
あり、述語です。
SQLサーバーは、シーク述語を使用して5つの行を検出し、通常の述語を使用して、これらの5行をさらに1行に減らします。
クラスタ化インデックスを別の方法で定義した場合:
CREATE TABLE #174860 (
PK INT NOT NULL,
COL1 INT NOT NULL,
COL2 INT NOT NULL,
PRIMARY KEY (COL2, PK)
);
同じクエリを実行すると、異なる結果が得られます。
この場合、SQL ServerはWHERE
句で両方の列を使用してシークできます。キー列を使用して、テーブルから正確に1行が読み込まれます。
もう1つの例として、次のクエリを考えてみます。
SELECT *
FROM #174860
WHERE COL1 = 500
AND COL2 = 3545;
IX_174860_IXインデックスは、クエリに必要なすべての列が含まれているため、カバリングインデックスです。ただし、COL1
キー列のみです。SQL Serverはその列をシークして、一致するCOL1
値を持つ1000行を見つけることができます。列のこれらの行をさらにフィルタリングしてCOL2
、最終的な結果セットを0行に減らすことができます。