回答:
列がではなくWHERE/JOIN/GROUP BY/ORDER BY
、SELECT
句の列リストのみにある場合。
このINCLUDE
句は、インデックスツリーではなく、最下位/リーフレベルでデータを追加します。ツリーの一部ではないため、インデックスが小さくなります
INCLUDE columns
インデックスのキー列ではないため、順序付けされていません。これは、前述したように、述語やソートなどにはあまり役に立たないことを意味します。ただし、キー列から数行の残余ルックアップがある場合に役立つことがあります。
SELECT
あり、一部ではない場合はどうなりますか?\
INCLUDEを使用して1つ以上の列を非クラスター化インデックスのリーフレベルに追加します。そうすることで、クエリを "カバー"できます。
従業員の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つの列が欠落しているクエリがある場合、それらを含めると非常に役立ちます。適切な非クラスター化インデックスに。
JOIN
はクエリのフィルターまたはキー上にあるINCLUDE
必要があり、sは、取得するが並べ替えではないデータである必要があります。
この議論は重要な点を見落としている:問題は、「非キー列」がインデックス列として含まれるか、含まれる列として含まれるほうがよいかではない。
問題は、include-mechanismを使用して、実際にはインデックスに必要のない列を含めるのがどれほどコストがかかるかです。(通常、where句の一部ではありませんが、selectに含まれることがよくあります)。したがって、あなたのジレンマは常に:
ここで、id1、id2 ... idNは制限でよく使用される列であり、col1、col2 ... colNは選択されることが多いが、通常は制限で使用されない列です。
(これらの列をすべてインデックスキーの一部として含めるオプションは、常にばかげています(制限で使用されている場合を除く)-インデックスを更新して並べ替える必要があるため、メンテナンスが常に高価になるため「キー」は変更されていません)。
オプション1または2を使用しますか?
回答:テーブルがめったに更新されない場合(主に挿入/削除される場合)、include-mechanismを使用して「ホット列」(selectでよく使用されますが、制限ではあまり使用されません)を含めることは比較的安価です。挿入/削除では、とにかくインデックスを更新/ソートする必要があるため、すでにインデックスを更新しているときに、余分な列をいくつか格納することに伴うオーバーヘッドはほとんどありません。オーバーヘッドは、インデックスに冗長な情報を格納するために使用される追加のメモリとCPUです。
付属-列が頻繁に更新される列が追加し検討した場合(index-せずにキー更新中-columns) - または - useオプション1 -インデックスがあなたのテーブルのコピーに近くなると彼らのように多くの場合お勧めします!また、特定のinclude-columnを追加してもパフォーマンスに差がないことが判明した場合は、それらを追加するという考えをスキップすることをお勧めします:)それらが有用であることを確認してください!
キー(id1、id2 ... idN)の同じ値ごとの行の平均数も、いくつか重要な場合があります。
カラムが- インデックスのインクルード -カラムとして追加される- 制限で使用されている場合:インデックスが使用できる限り(index- key -columns に対する制限に基づく)、SQL Serverは一致します。テーブル自体を高価な方法で回避するのではなく、インデックス(leaf-node-values)に対する列制限。
基本インデックス列はソートされますが、含まれている列はソートされません。これにより、インデックスを維持するためのリソースを節約しながら、クエリをカバーするために含まれる列にデータを提供することが可能になります。したがって、クエリを対象とする場合は、検索条件を指定して、インデックスの並べ替えられた列に行を配置し、その後、検索データ以外の並べ替えられていない追加の列を「含める」ことができます。これは、インデックスのメンテナンスにおけるソートと断片化の量を減らすのに役立ちます。
理由(インデックスのリーフレベルのデータを含む)が適切に説明されている理由。これについて2つの揺れを与える理由は、クエリを実行するときに、追加の列が含まれていない場合(SQL 2005の新機能)、SQL Serverはクラスター化インデックスに移動して追加の列を取得する必要があるためです。これには時間がかかり、新しいデータページがメモリに読み込まれると、SQL Serverサービス、ディスク、およびメモリ(具体的にはバッファキャッシュ)への負荷が増加し、バッファキャッシュから頻繁に必要な他のデータを押し出す可能性があります。
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
のは、これがあると仮定しましょうではない場合、私たちが持っているcol2
中でINCLUDE
、インデックスの木の部分でそれを持つだけの何のメリットがないため句。
数年早送りします。
このクエリを調整する必要があります。
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)
今、あなたはそれを知っているCol2
し、Col3
インデックス・ツリーの一部ではないため、読み取りインデックス範囲を狭めたり、行を注文するために使用されていません。Isは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_col
間に追加するCol1
とCol2
どうなりますか?他のクエリは影響を受けますか?
テーブルからのフェッチを回避するためだけに列を追加する場合は、INCLUDE
キー列と比べて他の「利点」があります。しかし、私はドキュメンテーションの側面を最も重要なものと考えています。
あなたの質問に答えるには:
INCLUDE句の有無にかかわらずカバリングインデックスを作成するかどうかを決定する際に、どのガイドラインを提案しますか?
テーブルにアクセスせずにインデックスでその列を使用できるようにするためだけに列をインデックスに追加する場合は、その列をINCLUDE
句に挿入します。
列をインデックスキーに追加すると、追加の利点がもたらされる場合(たとえばorder by
、読み取りインデックスの範囲を狭める可能性があるため、またはそのため)、キーに追加します。
これについてのより長い議論をここで読むことができます:
https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes
インデックス定義にインライン化されたすべての列の合計サイズには制限があります。とはいえ、これほど広いインデックスを作成する必要はありませんでした。私にとっての大きな利点は、特定の順序で定義する必要がないため、列を含む1つのインデックスでより多くのクエリをカバーできることです。インデックス内のインデックスとして考えてください。1つの例は、StoreID(StoreIDは選択性が低いことを意味し、各店舗が多くの顧客に関連付けられていることを意味します)と顧客の人口統計データ(LastName、FirstName、DOB)です。これらの列をこの順序でインライン化した場合(StoreID、LastName 、FirstName、DOB)の場合、StoreIDとLastNameがわかっている顧客のみを効率的に検索できます。
一方、StoreIDにインデックスを定義し、LastName、FirstName、DOB列を含めると、本質的に、StoreIDで2つのシークインデックス述語を実行し、次に含まれる列のいずれかで述語をシークできます。これにより、StoreIDで始まる限り、可能なすべての検索順列をカバーできます。