web-dev-qa-db-ja.com

インデックスの列の順序はどれくらい重要ですか?

インデックス宣言の最初に、最も選択的な列を配置する必要があると聞きました。例:

CREATE NONCLUSTERED INDEX MyINDX on Table1
(
   MostSelective,
   SecondMost,
   Least
)

まず、私が言っていることは正しいですか?もしそうなら、インデックス内の列の順序を並べ替えることでパフォーマンスに大きな違いが見られる可能性がありますか、それとも「いいこと」の練習ですか?

私が質問している理由は、DTAを介してクエリを実行した後、既存のインデックスと同じ列をほぼすべて含むインデックスを、異なる順序で作成することを推奨しているためです。不足している列を既存のインデックスに追加し、それを適切に呼び出すことを検討していました。考え?

149
Abe Miessler

このようなインデックスを見てください:

Cols
  1   2   3
-------------
|   | 1 |   |
| A |---|   |
|   | 2 |   |
|---|---|   |
|   |   |   |
|   | 1 | 9 |
| B |   |   |
|   |---|   |
|   | 2 |   |
|   |---|   |
|   | 3 |   |
|---|---|   |

最初の列が最初の2番目の列を制限するよりも多くの結果を排除するため、最初にAを制限する方法をご覧ください。インデックスをどのように横断する必要があるかを想像すると、1列目、2列目などが簡単になります。最初のパスでほとんどの結果を切り取ると、2番目のステップがはるかに速くなることがわかります。

別のケースとして、列3でクエリを実行した場合、オプティマイザーはインデックスを使用しません。これは、結果セットを絞り込むのにまったく役に立たないためです。 クエリを実行しているときはいつでも、次のステップの前に処理する結果の数を絞り込むと、パフォーマンスが向上します。

インデックスもこの方法で保存されるため、クエリを実行しているときに最初の列を見つけるためにインデックス全体でバックトラックすることはありません。

要するに、いいえ、それはショーのためではなく、本当のパフォーマンスの利点があります。

178
Nick Craver

列の順序は重要です。どの順序が正しいかは、クエリの方法によって異なります。インデックスを使用して、正確なシークまたは範囲スキャンを実行できます。正確なシークとは、インデックス内のすべての列の値が指定されており、クエリが正確に行にある場合です。シークでは、列の順序は関係ありません。範囲スキャンは、一部の列のみが指定されている場合であり、この場合は順序が重要になります。 SQL Serverは、左端の列が指定されている場合にのみ、次に左端の列が指定されている場合にのみ、範囲スキャンにインデックスを使用できます。 (A、B、C)にインデックスがある場合、A=@aの範囲スキャン、A=@a AND B=@bの範囲スキャンに使用できますが、B=@bの-​​notC=@c norB=@b AND C=@cA=@a AND C=@c部分はインデックスを使用しますが、A=@a部分はインデックスを使用しますが、C=@cは混在します(クエリはA=@aのすべてのB値をスキャンしますが、 C=@c)に「スキップ」します。他のデータベースシステムには、「スキップスキャン」演算子と呼ばれるものがあり、外部列が指定されていない場合にインデックスの内部列を利用することができます。

その知識があれば、インデックスの定義をもう一度見ることができます。 (MostSelective, SecondMost, Least)のインデックスは、MostSelective列が指定されている場合にのみ有効になります。しかし、それが最も選択的であるため、内側の列の関連性はすぐに低下します。多くの場合、(MostSelective) include (SecondMost, Least)または(MostSelective, SecondMost) include (Least)に優れたインデックスがあることがわかります。内側の列は関連性が低いため、インデックス内のこのような正しい位置に低い選択性の列を配置すると、シークのノイズになります。したがって、中間ページから移動してリーフページのみに保持するのが理にかなっています。クエリのカバー可能性の目的。つまり、それらをINCLUDEに移動します。これは、Least列のサイズが大きくなるにつれて重要になります。このインデックスは、正確な値または範囲としてMostSelectiveを指定するクエリにのみ有効であり、その列が最も選択的であり、すでに候補行を大幅に制限しているという考えです。

一方、(Least, SecondMost, MostSelective)のインデックスは間違いに見えるかもしれませんが、実際には非常に強力なインデックスです。最も外側のクエリとしてLeast列があるため、選択性の低い列で結果を集計する必要があるクエリに使用できます。このようなクエリは、OLAPおよび分析データウェアハウスで広く使用されており、まさにこのようなインデックスが非常に良いケースを持っている場所です。このようなインデックスは、実際には優れたclusteredインデックスを作成します。これは、関連する行の大きなチャンクで物理レイアウトを整理するためです(通常、同じLeast値ある種のカテゴリまたはタイプを示します)、分析クエリを容易にします。

そのため、残念ながら、「正しい」順序はありません。 Cookieカッターレシピに従う必要はありませんが、代わりにこれらのテーブルに対して使用するクエリパターンを分析し、どのインデックス列の順序が正しいかを判断する必要があります。

114
Remus Rusanu

Remusが言うように、ワークロードに依存します。

しかし、受け入れられた答えの誤解を招く側面に対処したいと思います。

インデックス内のすべての列で等値検索を実行しているクエリの場合、大きな違いはありません。

以下は2つのテーブルを作成し、それらに同一のデータを入力します。唯一の違いは、一方のキーが最も選択性の高いキーから最も選択性の低いキーに、もう一方のキーが逆になっていることです。

CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);
CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);

CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least);
CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective);

INSERT INTO Table1 (MostSelective, SecondMost, Least)
output inserted.* into Table2
SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~'
FROM master..spt_values
WHERE type = 'P' AND number >= 0
ORDER BY number;

両方のテーブルに対してクエリを実行しています...

SELECT *
FROM   Table1
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~';

SELECT *
FROM   Table2
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~'; 

...両方ともインデックスを使用し、両方ともまったく同じコストがかかります。

enter image description here

受け入れられた答えのASCIIアートは、実際にはインデックスの構造ではありません。 Table1のインデックスページを以下に示します(画像をクリックしてフルサイズで開きます)。

enter image description here

インデックスページには、キー全体を含む行が含まれます(この場合、インデックスは一意として宣言されていないため、実際には行識別子に追加のキー列が追加されていますが、無視できます 詳細については、こちらを参照してください )。

上記のクエリでは、SQL Serverは列の選択性を考慮しません。ルートページのバイナリ検索を実行し、Key(PPP...,3,~ )>=(JJJ...,1,~ )および< (SSS...,3,~ )であることを検出しますページ1:118を読み取る必要があります。次に、そのページのキーエントリのバイナリ検索を実行し、下に移動するリーフページを見つけます。

選択性の順序でインデックスを変更しても、バイナリ検索から予想されるキー比較の数や、インデックスシークを実行するためにナビゲートする必要があるページの数には影響しません。せいぜいmightキー比較自体をわずかに高速化します。

ただし、ワークロード内の他のクエリでは、最も選択的なインデックスを最初に並べることが意味をなす場合があります。

たとえば、ワークロードに次の両方の形式のクエリが含まれている場合。

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

上記のインデックスは、どちらにも対応していません。 MostSelectiveは、シークとルックアップを含むプランを作成するのに十分な選択性がありますが、Leastに対するクエリは価値がありません。

ただし、このシナリオ(複合インデックスの先頭列のサブセットでのインデックスシークは対象外)は、インデックスによって支援できるクエリの1つの可能なクラスにすぎません。 MostSelective自体またはMostSelective, SecondMostの組み合わせで実際に検索することはなく、常に3つすべての列の組み合わせで検索する場合、この理論上の利点は役に立ちません。

逆のようなクエリ

SELECT MostSelective,
       SecondMost,
       Least
FROM   Table2
WHERE  Least = '~'
ORDER  BY SecondMost,
          MostSelective 

一般的に規定されているものと逆の順序にすることで助けられます-クエリをカバーするため、シークをサポートし、ブートするために望ましい順序で行を返します。

したがって、これは頻繁に繰り返されるアドバイスですが、せいぜいotherクエリの潜在的な利点についてのヒューリスティックです。実際にyourワークロード。

41
Martin Smith

インデックス宣言の最初に、最も選択的な列を配置する必要があります。

正しい。インデックスは複合(複数の列で構成される)にすることができ、左端の原則のために順序が重要です。その理由は、データベースがリストを左から右にチェックし、定義された順序に一致する対応する列参照を見つける必要があるからです。たとえば、列を持つアドレステーブルにインデックスを作成する場合:

  • 住所
  • シティ
  • 状態

address列を使用するすべてのクエリはインデックスを利用できますが、クエリにcityおよび/またはstate参照のいずれかのみがある場合、インデックスは使用できません。これは、左端の列が参照されていないためです。クエリのパフォーマンスは、最適なもの(個々のインデックス、または異なる順序の複数のコンポジット)を示す必要があります。良い読み物: The Tipping Point 、Kimberley Tripp著

30
OMG Ponies

答えはすべて間違っています。

複合インデックス内の個々の列の選択性は、順序を選択する際に重要ではありません

簡単な思考プロセスを次に示します。事実上、インデックスは関連する列の連結です。

その理論的根拠を与えると、唯一の違いは、文字列の早い部分と遅い部分で異なる2つの「文字列」を比較することです。これは総コストのごく一部です。ある回答に記載されているように、「最初のパス/ 2番目のパス」はありません。

つまり、どの順序を使用する必要がありますか?

  1. anyの順序で、=でテストされた列から開始します。
  2. 次に、1つの範囲列にタックします。

たとえば、非常に低い選択性の列mustが最初に来る:

WHERE deleted = 0  AND  the_datetime > NOW() - INTERVAL 7 DAY
INDEX(deleted, the_datetime)

インデックス内の順序を入れ替えると、deletedが完全に無視されます。

(列の順序付けにはさらに多くのルールがあります。)

3
Rick James