主キーが含まれている場合、複合インデックスを一意としてマークする必要がありますか?


9

主キーを持ついくつかのテーブルがあるとします。例:

CREATE TABLE Customers (
   CustomerID int NOT NULL PRIMARY KEY,
   FirstName nvarchar(50),
   LastName nvarchar(50),
   Address nvarchar(200),
   Email nvarchar(260)
   --...
)

に一意の主キーがありCustomerIDます。

伝統的に私はいくつかの追加のカバーするインデックスが必要になるかもしれません。たとえば、CustomerIDまたはのいずれかでユーザーをすばやく見つけるにはEmail

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)

そして、これらは私が何十年もの間作成してきた種類のインデックスです。

一意である必要はありませんが、実際には

インデックス自体は、テーブルスキャンを回避するために存在します。これは、パフォーマンスを向上させるためのカバリングインデックスです(一意性を強制するための制約としてインデックスはありません)。

今日私はちょっとした情報を思い出しました-SQL Serverは次の事実を利用できます:

  • 列に外部キー制約があります
  • 列には一意のインデックスがあります
  • 制約は信頼されています

クエリの実行を最適化するのに役立ちます。実際、SQL Serverインデックスデザインガイドから

データが一意であり、一意性を適用したい場合は、列の同じ組み合わせで一意でないインデックスの代わりに一意のインデックスを作成すると、クエリオプティマイザーに追加情報が提供され、より効率的な実行プランを作成できます。この場合は、一意のインデックスを作成すること(できればUNIQUE制約を作成すること)をお勧めします。

私の複数列インデックス主キーが含まれているとすると、この複合インデックスは事実上一意になります。挿入や更新のたびにSQL Serverで強制する必要があるのは、特に制約ではありません。しかし、実際には、この非クラスター化インデックス一意です。

このデファクトユニークインデックスを実際にユニークであるとマークすることに何か利点はありますか?

CREATE UNIQUE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   顧客ID、
   Eメール
)

SQL Serverがように私には思えることができ、私のインデックスが既にことを実現するスマート十分である、それは主キーが含まれているという事実のおかげでユニーク。

  • しかし、おそらくこれはこれを認識していないため、インデックスを一意として宣言すると、オプティマイザに利点があります。
  • おそらく、それが挿入と更新の間にスローダウンにつながる可能性があることを除いて、一意性チェックを実行する必要があります-以前は、以前は必要でなかった場所です。
  • インデックスが主キーを含んでいるため、インデックスがすでに一意であることが保証されていない場合は除きます。

複合インデックスに主キーが含まれている場合の対処法について、Microsoftからのガイダンスはありません。

一意のインデックスの利点は次のとおりです。

  • 定義された列のデータの整合性が保証されます。
  • クエリオプティマイザに役立つ追加情報が提供されます。

主キーが既に含まれている場合、複合インデックスを一意としてマークする必要がありますか?または、SQL Server自体でこれを理解できますか?


4
CustomerIDで顧客をすばやく見つけるには、PKでおそらく十分です。電子メールで顧客を見つけるには、電子メールだけに2番目のインデックスを用意します(すでにクラスタリングキーをカバーしています)。とにかく、私はこれが架空のものであることを知っていますが、一意である必要があることがわかっている場合は、一意としてマークを付けます。その制約は、オプティマイザに役立つ可能性のあるケースを相殺するほど挿入を傷つけないでしょう。しかし、それはすべて、作業負荷に依存します。これは、推測する必要があります...
アーロンベルトラン

1
ここで詳しく説明しました。そして、何らかの方法でそれを証明するためのデータを考え出す方法は考えられませんが、SQL Serverの担当者はおそらく非常に賢く、オプティマイザはおそらくこのメタ最適化をすでに実行できていると思います。また、インデックスに意図を伝えさせるのがおそらく最善であると考えています。これは単なるインデックスであり、SQL Serverの機能を推測するためだけに行われる場合にのみ、ビジネスルールとして一意性を課すものではありません。
Ian Boyd 2015

回答:


11

主キーが既に含まれている場合、複合インデックスを一意としてマークする必要がありますか?

おそらく違います。オプティマイザは通常、とにかく含まれているキー列の一意性に関する情報を使用できるため、本当の利点はありません。

考慮すべきインデックスのキーを変更する更新プランでインデックスを一意にマークすることの重要な結果もあります。

セットアップ

CREATE TABLE dbo.Customers 
(
   CustomerID int NOT NULL PRIMARY KEY,
   FirstName nvarchar(50),
   LastName nvarchar(50),
   [Address] nvarchar(200),
   Email nvarchar(260)
);

CREATE NONCLUSTERED INDEX 
    IX_Customers_CustomerIDEmail 
ON dbo.Customers
(
   CustomerID,
   Email
);

-- Pretend we have some rows
UPDATE STATISTICS dbo.Customers 
WITH ROWCOUNT = 100000, PAGECOUNT = 20000;

インデックスごとの更新プラン(一意でないインデックス)

UPDATE dbo.Customers 
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old' 
OPTION (QUERYTRACEON 8790); -- Per-index update plan

実行計画:

分割とフィルター

オプティマイザーは、非クラスター化インデックスを行ごと(「狭い」プラン)ごとに更新するか、インデックスごと(「広い」プラン)ごとに更新するかをコストベースで決定します。デフォルトの戦略(インメモリOLTPテーブルを除く)は、幅広い計画です。

狭い計画(非クラスター化インデックスがヒープ/クラスター化インデックスと同時に維持される)は、小さな更新のパフォーマンス最適化です。この最適化はすべての場合に実装されているわけではありません。特定の機能(インデックス付きビューなど)を使用すると、関連するインデックスが幅広い計画で維持されます。

詳細:データを変更するT-SQLクエリの最適化

この場合、文書化されていないトレースフラグ8790を使用して、幅広い更新計画を強制しました。したがって、計画には、クラスター化インデックスと非クラスター化インデックスが別々に維持されていることが示されています。

分割により、各更新が個別の削除と挿入のペアに変わります。フィルターは、インデックスの変更につながらない行をフィルターで除外します。

詳細:(非更新更新)SQL Server QOチームによる。

インデックスごとの更新プラン(一意のインデックス)

-- Same index, but unique
CREATE UNIQUE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)
WITH (DROP_EXISTING = ON);

UPDATE dbo.Customers 
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old' 
OPTION (QUERYTRACEON 8790); -- Per-index update plan

実行計画:

分割-並べ替え-折りたたみ

インデックスに一意のマークが付けられている場合、追加のSortおよびCollapse演算子に注意してください。

このSplit-Sort-Collapseパターンは、一意のインデックスのキーを更新するときに、中間の一意のキー違反を防ぐために必要です。

詳細:Craig Freedmanによる一意のインデックスの維持

特にソートは問題になる可能性があります。不要な追加コストであるだけでなく、見積もりが不正確な場合はディスクに流出する可能性があります。

非クラスター化キーについて

考慮すべきもう1つの要因は、非クラスター化インデックス構造は、UNIQUE指定されていなくても、インデックスのすべてのレベルで常に一意であることです。クラスタリングキー(およびクラスタ化インデックスが一意としてマークされていない場合は一意化記号)が、すべてのレベルで非一意の非クラスタ化インデックスに追加されます。

結果として、次のインデックス定義:

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   Email
)
WITH (DROP_EXISTING = ON);

...実際には、すべてのレベルのキー(Email、CustomerID)が含まれています。したがって、両方の列で「シーク可能」です。

SELECT * 
FROM dbo.Customers AS C WITH (INDEX(IX_Customers_CustomerIDEmail))
WHERE C.Email = N'Email'
AND C.CustomerID = 1;

求める

詳細:カレンデラニーによる非クラスター化インデックスキーの詳細


8

SQLは、ユーザーが明示的に指定したかどうかに関係なく、既に一意であることを認識しています(PKが含まれている場合、一意にすることはできません)。

非一意のインデックスと一意のインデックスの大きな違いは、非一意のインデックスでは、リーフだけでなく、インデックスのより高いレベルでクラスタ化インデックスキー(CIXが一意として宣言されていない場合は一意識別子の値)が必要になることです。レベル。

あなたの場合、あなたはすでにキーにCIXを持っています、それはそれがすでにインデックスのすべてのレベルにあることを意味するでしょう。

しかし、別のPK(一意)とCIX(問題ではない)を持つテーブルを作成することもできます。次に、キーにPKを含む一意でないインデックスを作成します。CIX列の簡単に検索できるvarchar値を含む、いくつかの行をテーブルに入れます。複数レベルのインデックスを作成するのに十分な行を配置します。次に、DBCC INDを使用してNCIX内のページを検索し、DBCC PAGEを使用してデータを表示して、CIXキー値がより高いレベルにあるかどうかを確認できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.