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


9

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

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

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

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

テーブル-

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
    )

回答:


12

テーブル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;

インデックスなしでソート演算子を使用して計画します。

ここに画像の説明を入力してください

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

ここに画像の説明を入力してください


1
ランディに感謝します。これを答えとしてマークしますが、ミッシングインデックスの提案は推定実行計画に基づいているとお考えですか?SS2016の実績実行計画に表示されていますのでお聞きします。
Fletch

1
それがあなたの言っていることなのかどうか、はっきりさせてくれてありがとう。
Fletch
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.