web-dev-qa-db-ja.com

シーク述語と述語の違い

SQL Server 2014 Enterpriseにあるクエリのパフォーマンスを調整しようとしています。

SQL Sentry Plan Explorerで実際のクエリプランを開いたところ、1つのノードでSeek Predicate述語

Seek PredicatePredicateの違いは何ですか?

enter image description here

注:このノードには多くの問題(たとえば、推定行と実際の行、残りのIO)があることがわかりますが、質問はそれとは関係ありません。

12
Greg

いくつかの列とともに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は述語です。

enter image description here

SQLサーバーは、シーク述語を使用して5つの行を検出し、通常の述語を使用して、これらの5つの行を1つの行にさらに減らします。

クラスタ化インデックスを別の方法で定義した場合:

CREATE TABLE #174860 (
PK INT NOT NULL, 
COL1 INT NOT NULL,
COL2 INT NOT NULL,
PRIMARY KEY (COL2, PK)
);

同じクエリを実行すると、異なる結果が得られます。

enter image description here

この場合、SQL ServerはWHERE句で両方の列を使用してシークできます。キー列を使用して、テーブルから正確に1行が読み込まれます。

もう1つの例として、次のクエリを考えてみます。

SELECT *
FROM #174860
WHERE COL1 = 500
AND COL2 = 3545;

IX_174860_IXインデックスは、クエリに必要なすべての列が含まれているため、カバリングインデックスです。ただし、COL1のみがキー列です。 SQL Serverはその列でシークして、一致するCOL1値を持つ1000行を見つけることができます。 COL2列の行をさらにフィルタリングして、最終的な結果セットを0行に減らすことができます。

enter image description here

18
Joe Obbish