web-dev-qa-db-ja.com

欠落している非クラスター化インデックスは既にクラスター化インデックスの一部です

実行速度の遅いクエリをデバッグしています。実行計画では、非クラスタ化インデックスが提案されており、51.6648の影響があります。ただし、非クラスター化インデックスには、主キー(PK)複合クラスター化インデックスに既にある列のみが含まれます。

これは、インデックス内の列の順序が原因である可能性がありますか?つまり、クラスター化インデックスの列が最も選択的なものから最も選択性の低いものへと順序付けられていない場合、非クラスター化インデックスがパフォーマンスを向上させる可能性はありますか?

さらに、非クラスター化インデックスには、3つのPK列のうち2つだけが含まれており、3番目の列は包含列として追加されています。 includeは、非クラスター化インデックスの使用がより最適になるもう1つの理由ですか?

以下は、私が使用しているテーブル構造の例です。

テーブル-

Retailers (
    RetailerID int PK, 
    name ...)

Retailer_Relation_Types (
    RelationType smallint PK, 
    Description nvarchar(50) ...)

Retailer_Relations (
    RetailerID int PK FK, 
    RelatedRetailerID int PK FK, 
    RelationType smallint PK FK, 
    CreatedOn datetime ...)

テーブル Retailer_Relationsには、次の複合PKインデックスと推奨インデックスがあります-

CONSTRAINT PK_Retailer_Relations 
PRIMARY KEY CLUSTERED (
    RetailerID ASC, 
    RelatedRetailerID ASC, 
    RelationType ASC
    ) ON [PRIMARY]

CREATE NONCLUSTERED INDEX <NameOfIndex> 
ON Retailer_Relations (
    RetailerID, 
    RelationType
    ) 
INCLUDE (
    RelatedRetailerID
    )
9
Fletch

テーブルRetailer_Relationsには、次の複合PKインデックスと推奨インデックスがあります。

欠落しているインデックスは役に立ち、間違いなく機能しますが、欠落しているインデックスにあまり時間をかけません。これらのヒントは、実際の実行プランではなく、推定実行プランに基づいて作成されます。

より正確には、これらのインデックスヒントは、プランでオペレーターが使用するQuery Bucks™のコストを削減するという前提に基づいています。オプティマイザは推定コストを計算し、不足しているインデックスのヒントをそれに応じて追加します。

その結果、それらは非常に間違っている可能性があります。効果があるかどうかわからない場合は、前後の状況をテストすることをお勧めします。これを行うには、クエリを実行する前に、ステートメントSET STATISTICS IO, TIME ON;を追加します。

また、 statisticsparser を使用して、これらの統計を読みやすくすることもできます。

これは、インデックスの列の順序が原因である可能性がありますか?

正解です。たとえば、クエリが次のようになっている場合、欠落しているインデックスを作成するとクエリの選択性が向上します。

SELECT  RelatedRetailerID
FROM Retailer_Relations 
WHERE
RetailerID = 5 AND
RelationType = 20;

またはこのように:

SELECT  RelatedRetailerID
FROM Retailer_Relations 
ORDER BY
RetailerID,
RelationType;

これの背後にある理由は、両方のインデックスがRetailerIDを探す可能性があることです。その部分は変更されません。しかし、RelationTypeに追加のフィルター/順序が適用されている場合はどうなりますか?これは、2番目のキー値ではなく3番目のキー値であるため、クラスター化インデックス内のあちこちにあります。そして、私たちが知っているように、これはNCIの2番目の重要な値です。

わかりましたが、非クラスター化インデックスはいつ、どのようにクエリを改善しますか?

いくつかのケースが考えられます:

  • RelationTypeが多くの値をフィルタリングする場合、残りのI/Oが高くなり、非クラスター化インデックスが必要になる可能性があります(クエリ#1)
  • 2つの列の順序付けが行われ(一方向)、結果セットが大きくなります(クエリ#2)。
  • @AaronBertrandが述べたように:NCIと比較したCIサイズの違いがかなりの量である場合、NCIを追加すると、その恩恵を受けるクエリによって読み取られるページが減少します。

NCIサイドノート

補足として、CIキー列はすべての非クラスター化インデックスに自動的に含まれるため、NCIのインクルードリストにキー列を追加する必要はありません。

クラスタ化インデックスが同じままであるかどうかが不明で、常に列が含まれるようにする場合は、そうすることを選択できます。

クエリ自体については、 PasteThePlan を介して実行プランを追加した場合、クエリのインデックス作成/改善に関する詳細情報を提供できます。


テスト

テーブルを作成していくつかの行を追加します

CREATE TABLE Retailer_Relations (
    RetailerID int , 
    RelatedRetailerID int , 
    RelationType smallint, 
    CreatedOn datetime,
    CONSTRAINT PK_Retailer_Relations 
PRIMARY KEY CLUSTERED (
    RetailerID ASC, 
    RelatedRetailerID ASC, 
    RelationType ASC
    ) ON [PRIMARY])


    DECLARE @I Int = 1
    WHILE @I < 1000
    BEGIN
    INSERT INTO Retailer_Relations(RetailerID,RelatedRetailerID,RelationType,CreatedOn)
    VALUES(@I,@I,@I,GETDATE()
    )
    set @I += 1
    END

クエリ#1

    SELECT  RelatedRetailerID
FROM Retailer_Relations 
WHERE
RetailerID = 5 AND
RelationType = 20;

インデックスなしの計画 ここ

シークを行っている間、Re​​tailerIDでシークを行っています。その後、RelationTypeに残りのI/O述語を発行します。

インデックスを追加します

CREATE NONCLUSTERED INDEX IX_TEST
ON Retailer_Relations (
    RetailerID, 
    RelationType
    ) 
INCLUDE (
    RelatedRetailerID
    )

残りの述語はなくなり、すべての列でシーク述語が発生します。

実行計画

2番目のクエリでは、追加されたインデックスの有用性がさらに明らかになります。

SELECT  RelatedRetailerID
FROM Retailer_Relations 
ORDER BY
RetailerID,
RelationType;

インデックスなしのプラン、ソート演算子:

enter image description here

インデックスを使用して計画し、インデックスを使用して並べ替え演算子を削除します

enter image description here

12
Randi Vertongen