インデックスに列を含めるための厳格なルール


38

非クラスター化インデックスに含める列とその順序を決定するための厳格なルールはありますか?私はちょうどこの投稿https://stackoverflow.com/questions/1307990/why-use-the-include-clause-when-creating-an-index を読んでいて、次のクエリでそれを見つけました:

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

ポスターは、次のようなインデックスを作成することを提案しました。

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

ここに、なぜこのようなインデックスを作成できないのかという質問があります

CREATE NONCLUSTERED INDEX NC_EmpDep 
      ON Employee( EmployeeID, DepartmentID, LastName)

または

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

そして、LastName列を含めることを決定するためにポスターを導くものは何ですか。他の列はなぜですか?そして、列をどの順序で保持するかをどのように決定するのですか?


3
INCLUDEには通常、レコードが見つかった後に必要なフィールドがあり、より多くのデータを取得するための往復を節約できます。INCLUDEのフィールドの順序は重要ではありません。
神保

Ryk、個人的に私はこの投稿が役立つと思います。
ジェイソンヤング

この質問も役に立ちました。代わりに、個人をストーカーの良い質問と良い答えにレッツ・フォーカス....
ボルボックス

回答:


47

marc_sによるインデックスの提案は間違っています。コメントを追加しました。(そして、それも受け入れられた私の答えでした!)

このクエリのインデックスは

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

通常、インデックスは

CREATE INDEX <name> ON <table> (KeyColList) INCLUDE (NonKeyColList)

どこで:

  • KeyColList =キー列=行の制限と処理に使用される
    WHERE、JOIN、ORDER BY、GROUP BYなど
  • NonKeyColList =非キー列= SELECTおよび集約(例:SUM(col))で選択/制限後に使用

+1-OPのサンプルインデックスがクエリにとって価値がないことに同意します(私のansを参照)。
-JNK

すばらしいです!KeyColListとNonKeyColListの順序を決定するものはもう1つだけです。私の例で説明できますか?ここで、クエリがSELECT EmployeeID、DepartmentID、LastName FROM EmployeeWHERE DepartmentID = 5、StateID = 4である場合、インデックスはどのようになりますか?

@Rocky- NonKeyColList順序は関係ありません。 KeyColList順序は、クエリで使用されると予想される頻度の順にする必要があります。以下の回答のメモを参照してください。ただしLast Name, First Name, Middile Initial、電話帳のようなものです。2番目のフィールドを見つけるには、最初のフィールドが必要です。
JNK

@gbnインクルードリストにEmployeeIDが本当に必要ですか?EmployeeID列にクラスター化インデックスがあり、その上にDeptId列に非クラスター化インデックスを作成する場合、NonClusteredインデックスは、INCLUDEリストのクラスター化キーを含む、NonClusteredインデックス構造に含まれるクラスター化キーを既に参照していますt利点を追加します。
ヴィスワナサンアイアー

1
@ViswanathanIyerは、実際のディスク上のストレージに2回追加されることはありません。SQLServerはこれを検出します。したがって、それは必要ではありませんが、物事を明確にします。ただし、質問にクラスター化インデックスが含まれていないため、何も想定しない方が安全です。
-gbn

19

JNKとgbnは素晴らしい答えを出しましたが、単一のクエリに焦点を合わせるだけでなく、全体像を検討する価値もあります。この特定のクエリはインデックス(#1)から恩恵を受けるかもしれませんが:

Employee(DepartmentID) INCLUDE (Lastname, EmployeeID)

次のようなクエリがわずかに変更される場合、このインデックスはまったく役に立ちません。

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5 AND LastName = 'Smith'

これにはインデックスが必要です(#2):

Employee(DepartmentID, LastName) INCLUDE (EmployeeID)

部門5に1,000人の従業員がいるとします。インデックス#1を使用して、すべてのスミスを見つけるには、含まれる列がキーの一部ではないため、部門5の1,000行すべてを検索する必要があります。インデックス2を使用すると、部門5、LastName Smithに直接シークできます。

したがって、インデックス#2はより広範なクエリの処理に役立ちますが、コストはインデックスキーが肥大化するため、インデックスの非リーフページが大きくなります。すべてのシステムは異なるため、ここには経験則はありません。


サイドノートとして、EmployeeIDがこのテーブルのクラスター化キーである場合(クラスター化インデックスを前提とする場合)、EmployeeIDを含める必要はありません。これは、すべての非クラスター化インデックスに存在します。なる

Employee(DepartmentID, LastName)

2
有用な情報については+1。最後のポイントとして、これをテストし、EmployeeIDがクラスター化インデックスである場合、INCLUDEでのEmployeeIDの明示的な使用は(インデックスのサイズに基づいて)実際に無視されます。私が思うに、それはより明白であり、スペースの欠点はありません。
-gbn

1
私は絶対に同意します-特に費用がかからない場合は、明示する方が常に良いです!

1
念のため... INCLUDE(明示的にEmployeeIDではありません)でクラスター化キーをテストしましたが、スペースは追加されません。キー列でそれを行います。
gbn

@gbnはい、クラスターキーは、INCLUDE列が存在するインデックスのリーフレベルにのみ存在する必要があります。インデックスキーに移動すると、リーフ以外のページにも存在することになります。これにより、少し膨らみますが、ひどい量ではありません(中間レベルのページでは、整数を想定して、リーフレベルのページごとにさらに4バイトを追加します)。

これは、この記事で説明されている効果の一部を含む素晴らしい回答です。sqlperformance.com / 2014/07 / sql-indexes / クエリが変更された場合、インデックスの要件も変更されます。Jimの答えのほうが良いかもしれませんが、@ gbnの答えの方がうまくいくかもしれません。
ジョン別名hot2use 16年

7

どうやって最初に手に入れたのかわかりません。私にとって、そのクエリには、次のものを使用します。

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

SQLのほとんどすべてに「ハードで高速なルール」はありません。

しかし、あなたの例では、インデックスが使用するフィールドはDepartmentID、それがWHERE句の中にあるためです。

他のフィールドはそこから簡単にアクセスできる必要があります。あなたは、に基づいて選択DepartmentID後、INCLUDEインデックスのリーフ・ノードでこれらのフィールドがあります。

このインデックスでは機能しないため、他の例を使用したくありません。

電話帳のようなインデックスを考えてください。ほとんどの電話帳は、姓、名、ミドルネームの頭文字で並べられています。姓ではなく名を知っている場合、電話帳のインデックスの順序に基づいて名を検索できないため、電話帳は役に立ちません。

INCLUDEフィールドがなど、電話番号、住所、ブック内の各エントリのためのその他の情報のようなものです。

編集:

使用しない理由をさらに明確にするには:

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

このインデックスは、どちらかEmployeeIDまたは両方が EmployeeIDありLastNameWHERE句にある場合にのみ役立ちます。これは、このクエリに必要なもののほとんど反対です。


@ajbeavenは本当です。だから、私が編集に入れたコメントは、employeeIDまたは両方の列が必要だと言っています。
JNK

durr申し訳ありませんが誤読:(
ajbeaven

0

まだ(employee_id、department_id)インデックスを使用できるかもしれませんが、whereフレーズに「ダミー」行を含める必要があります。たとえば、「employee_id = employee_id)

  • (employee_id、departemnent_id)にインデックスがあります。
  • department_idでのみ検索/制限する必要がある
  • 間違った順序ので、インデックスを使用しません知っていない(または、物事が今では変更されている、および以下の「トリック」はもはや必要とされている。私は、「oldy」です?)
  • 「古い」トリックを使用しますか?

    select * from Employee emp
    where emp.employee_id = emp.employee_id
    およびemp.department_id = 5

(だから私はここで姓のインクルード部分に焦点を当てるのではなく、キーのイエス/または使用されていないことに焦点を当てています。)

敬具、

ミゲル


2
いいえ、それは役に立たず、効率的ではありません。
ypercubeᵀᴹ

具体的には、すべての従業員IDを検索してdepartment_id 5のすべてのインスタンスを見つけるために、インデックススキャンを行う必要があります。
マーク・ソウル

次に、逆のケースを考えます(インデックスはdepartment_id、employee_idにあります)。特定の部門を今すぐ見つけるのは簡単ですが、特定の従業員を見つけるには、SQLは5つの部門をスキャンするだけで特定の従業員のすべての行を見つけることができます。
マーク・ソウル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.