web-dev-qa-db-ja.com

索引を作成するときにINCLUDE句を使用するのはなぜですか。

70から433試験の勉強中に私はあなたが次の2つの方法のいずれかでカバーインデックスを作成することができることに気づいた。

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

- OR -

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

INCLUDE句は私にとって新しいものです。 INCLUDE句を使用して、または使用しないでカバリングインデックスを作成するかどうかを判断する際に、なぜそれを使用し、どのガイドラインを提案するのですか。

402
Cory

列がWHERE/JOIN/GROUP BY/ORDER BYには含まれず、SELECT節の列リストにのみ含まれる場合。

INCLUDE句は、インデックスツリーではなく、最下位/最下位レベルでデータを追加します。これはツリーの一部ではないのでインデックスは小さくなります

INCLUDE columnsはインデックスのキー列ではないため、順序付けされていません。これは、上で述べたように、述語、ソートなどにはあまり役に立ちません。ただし、キー列から数行以内に残余検索がある場合は、を使用すると便利です

もう1つのMSDNの記事で動作する例

340
gbn

非クラスタ化インデックスのリーフレベルに1つ以上の列を追加するには、INCLUDEを使用します。そうすることで、クエリを "カバー"できます。

従業員のID、部門ID、および姓を照会する必要があるとします。

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

(EmployeeID、DepartmentID)に非クラスタ化インデックスがある場合、ある部門の従業員を見つけたら、姓の列だけを取得するために、実際の完全な従業員レコードを取得するために「ブックマークルックアップ」を実行する必要があります。 。あなたが多くの従業員を見つけた場合、それはパフォーマンスの面でかなり高価になる可能性があります。

その姓をインデックスに含めた場合

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

そうすれば、必要なすべての情報がノンクラスタードインデックスのリーフレベルで利用可能になります。クラスタ化されていないインデックスを探して特定の部署の従業員を見つけるだけで、必要な情報がすべて揃い、インデックスで見つかった各従業員のブックマーク検索が不要になります。 - >多くの時間を節約できます。

明らかに、すべての非クラスタ化インデックスにすべての列を含めることはできません - ただし、1つか2つの列が欠けていて "カバー"されているクエリがある場合、それは非常に役に立ちます。適切な非クラスタ化インデックスに。

208
marc_s

この議論は重要な点に欠けています:「非キー列」がindex-columnsとして、またはincluded-columnsとして含める方が良いかどうかは問題ではありません。

問題は、インクルードメカニズムを使用してインデックスには実際には必要ない? (通常はwhere句の一部ではありませんが、多くの場合selectに含まれます)。したがって、ジレンマは常に次のとおりです。

  1. Id1、id2 ... idNでインデックスを使用aloneまたは
  2. Id1、id2 ... idNのインデックスを使用プラスinclude col1、col2 ... colN

ここで、id1、id2 ... idNは制限でよく使用される列であり、col1、col2 ... colNはよく選択される列ですが、通常は制限でnotが使用されます

(これらのすべての列をインデックスキーの一部として含めるオプションは、常に馬鹿げています(制限で使用されている場合を除く)-インデックスの更新と並べ替えが必要な場合でも、常にメンテナンスが高価になる「キー」は変更されていません)。

オプション1または2を使用しますか?

回答:テーブルがめったに更新されない-ほとんどが挿入/削除される場合、インクルードメカニズムを使用して「ホットカラム」(selectでよく使用されます-しかしnot =制限で頻繁に使用されます)挿入/削除ではインデックスを更新/ソートする必要があるため、インデックスを既に更新している間にいくつかの余分な列を保存することで余分なオーバーヘッドがほとんどありません。オーバーヘッドは、インデックスに関する冗長情報を保存するために使用される追加のメモリとCPUです。

Include-columnsとして追加することを検討する列が頻繁に更新される場合(インデックス-key-columnsが更新されることなく)-または-それが非常に多い場合インデックスはテーブルのコピーに近くなります-オプション1を使用してください。また、特定のinclude-columnを追加してもパフォーマンスに違いが生じない場合は、追加のアイデアをスキップすることをお勧めします:)それらが有用であることを確認してください!

キー(id1、id2 ... idN)の同じ値ごとの行の平均数も重要です。

インデックスのincluded-columnとして追加された列がrestrictionで使用されている場合:インデックス自体が可能な限り使用(インデックスに対する制限に基づいて-キー-列)-SQL Serverは、高価な方法を回避するのではなく、列制限をインデックス(リーフノード値)に対して一致させますテーブル自体。

25
Fredrik Solhaug

基本索引列はソートされますが、組み込み列はソートされません。これにより、インデックスを管理するためのリソースを節約しながら、クエリをカバーするためにインクルード列にデータを提供することが可能になります。そのため、クエリをカバーしたい場合は、検索基準を使用してインデックスのソートされた列に行を配置し、その後、検索されないデータを含む追加のソートされていない列を含めることができます。それは間違いなくインデックスメンテナンスにおけるソートと断片化の量を減らすのに役立ちます。

17
onupdatecascade

その理由(インデックスのリーフレベルのデータを含む)は、よく説明されています。これについて2つ振るのは、クエリを実行するときに追加の列が含まれていない場合(SQL 2005の新機能)、SQL Serverは追加の列を取得するためにクラスタ化インデックスに移動する必要があるためです。新しいデータページがメモリにロードされると、時間がかかり、SQL Serverサービス、ディスク、およびメモリ(具体的にはバッファキャッシュ)により多くの負荷がかかります。

6
mrdenny

私がすでに与えられた答えで見たことがないというさらなる考察は、含まれた列がvarchar(max)のようなインデックスキー列として許されないデータ型である可能性があるということです。

これにより、そのような列をカバーインデックスに含めることができます。私は最近、SELECTに多くの列を持つnHibernateが生成したクエリに有用なインデックスを提供するためにこれをしなければなりませんでした。

5
Robin Hames

索引定義にインライン化されるすべての列の合計サイズには制限があります。そうは言っても、私はそれほど広いインデックスを作成する必要はありませんでした。私にとって大きな利点は、列を特定の順序で定義する必要がないため、列を含む1つのインデックスでより多くのクエリをカバーできるという事実です。考えることはインデックス内のインデックスとしてです。 1つの例としては、StoreID(StoreIDは選択性が低いことを意味し、各店舗が多数の顧客に関連付けられていることを意味します)、次に顧客の人口統計データ(LastName、FirstName、DOB)があります。 、FirstName、DOB)、StoreIDおよびLastNameを知っている顧客のみを効率的に検索できます。

一方、StoreIDでインデックスを定義し、LastName、FirstName、DOBの各列を含めると、本質的にStoreIDで2つのシークインデックス述語を作成してから、含まれている列のいずれかで述語を検索できます。これにより、StoreIDで始まる限り、すべての可能な検索順列をカバーできます。

2
mEmENT0m0RI

キー列よりもINCLUDEを好む1つの理由キーにその列が必要ない場合はドキュメントです。これにより、将来、インデックスの進化がはるかに簡単になります。

あなたの例を考えてみましょう:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

クエリが次のような場合、そのインデックスが最適です。

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

もちろん、キーパーツに列を追加することでさらにメリットが得られる場合は、INCLUDEに列を配置しないでください。次の両方のクエリは、実際にはインデックスのキーでcol2列を優先します。

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

これがnotであり、INCLUDE節にcol2があると仮定しましょう。なぜなら、インデックスのツリー部分にあるだけの利点がないからです。

数年早送りします。

このクエリを調整する必要があります。

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

そのクエリを最適化するには、次のインデックスが最適です。

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

そのテーブルに既にあるインデックスをチェックすると、以前のインデックスがまだ残っている可能性があります。

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

これで、Col2Col3はインデックスツリーの一部ではないため、読み取りインデックスの範囲を狭めたり、行を並べ替えたりすることはできません。インデックスのキー部分の末尾(another_columnの後)にcol1を追加するのはかなり安全です。何かを壊すリスクはほとんどありません。

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

そのインデックスは大きくなりますが、それでもリスクがありますが、一般に、新しいインデックスを導入するよりも既存のインデックスを拡張する方が適切です。

INCLUDEのないインデックスがある場合、another_colの直後にCol1を追加することで、どのクエリが壊れるかわかりません。

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

another_colCol1の間にCol2を追加するとどうなりますか?他のクエリは影響を受けますか?

INCLUDE対キーカラムには他にも「利点」がありますテーブルからのフェッチを避けるためだけにこれらのカラムを追加する場合。ただし、ドキュメントの側面を最も重要だと考えています。

質問に答えるには:

iNCLUDE句を使用して、または使用せずにカバーリングインデックスを作成するかどうかを決定する際に、どのガイドラインを提案しますか?

テーブルにアクセスせずにインデックスでその列を使用できるようにするためだけに、インデックスに列を追加する場合は、INCLUDE句に入れます。

インデックスキーに列を追加すると追加の利点が得られる場合(たとえば、order byの場合、または読み取りインデックス範囲を狭めることができるため)、キーに追加します。

これについての長い議論をここで読むことができます:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes

1
Markus Winand