web-dev-qa-db-ja.com

すべての列を含むインデックスを単に追加できないのはなぜですか?

SQL Serverデータベースに、できるだけ高速にデータを検索および取得できるようにするテーブルがあります。テーブルへの挿入にかかる時間は気にせず、データを取得できる速度にのみ関心があります。

問題は、20以上の異なるタイプのクエリでテーブルにアクセスすることです。これにより、各クエリ用に特別に設計されたインデックスを追加するのは面倒な作業になります。代わりに、テーブルのすべての列を含むインデックスを単に追加することを検討しています。これは「良い」データベース設計で通常行うことではないので、私がそれを行うべきではないいくつかの正当な理由があると思います。

なぜ私がこれをしてはいけないのか誰にも教えてもらえますか?

更新:言及するのを忘れていました。データベースのサイズも気にしません。データベースのサイズが必要以上に大きくなることは問題ありません

35
Niels Brinch

まず、SQL Serverのインデックスは、インデックスエントリに最大900バイトしか含めることができません。それだけでは、すべての列のインデックスを持つことは不可能になります。

何よりも、そのようなインデックスはまったく意味がありません。何を達成しようとしていますか?

これを考慮してください:(LastName, FirstName, Street, City)にインデックスがある場合、そのインデックスはnotを使用してクエリを高速化できます

  • FirstName単独
  • City
  • Street

そのインデックスは、上の検索に役立ちます

  • (LastName)、または
  • (LastName, FirstName)、または
  • (LastName, FirstName, Street)、または
  • (LastName, FirstName, Street, City)

しかし、実際には他に何もありません-Streetだけ、またはCityだけを検索する場合は、確かにそうではありません。

インデックス内の列の順序には大きな違いがあり、クエリオプティマイザーは、インデックスの途中のどこかの列をルックアップに使用することはできません。

あなたの電話帳を考えてみましょう。それはおそらくLastName、FirstName、おそらくStreetの順です。それで、その索引付けはあなたの街のすべての「ジョー」を見つけるのに役立ちますか? 「メインストリート」に住むすべての人??いいえ-最初にLastNameで検索できます-次に、そのデータセット内でより具体的な情報を取得します。すべてのインデックスを作成するだけでは、すべての列の検索を高速化できませんすべて

Streetで検索できるようにしたい場合は、(Street)に個別のインデックスを追加する必要があります(また、おそらく意味のある別の1つまたは2つの列)。

Occupationなどで検索できるようにするには、別の特定のインデックスが必要です。

列がインデックスに存在するからといって、その列のすべての検索が高速化されるわけではありません。

主なルールは次のとおりです。使用するインデックスはできるだけ少なくしてください。インデックスが多すぎると、インデックスがない場合よりもシステムのパフォーマンスが低下する可能性があります。システムを構築し、パフォーマンスを監視して、最もコストのかかるクエリを見つけます。これらを最適化します。インデックスを追加する。

できる限り、すべての列に盲目的にインデックスを付けないでください。これは、システムパフォーマンスの低下を保証するものです。どのインデックスもメンテナンスと維持が必要であるため、インデックスが多いほど、INSERT、UPDATE、およびDELETE操作の影響が大きくなります(get遅い)これらすべてのインデックスを更新する必要があるため。

75
marc_s

インデックスのしくみについて根本的な誤解があります。

この説明を読む " マルチカラムインデックスの仕組み "。

あなたが持っているかもしれない次の質問は、なぜ 列ごとに1つのインデックス -を作成しないのかです。

退屈なタスクであると感じるかもしれませんが、私はそれを慎重にインデックス付けするための必須タスクだと思います。 この例 のように、ずさんな索引付けが反撃します。

注:適切なインデックス作成が効果を上げることを確信しており、多くの人があなたと同じ質問をしていることを知っています。それが私が無料の本を書いている理由です。上記のリンクは、質問への回答に役立つページを参照しています。ただし、 beginning から読み取ることもできます。

9
Markus Winand

代わりに、テーブルのすべての列を含むインデックスを単に追加することを検討しています。

これは常に悪い考えです。データベース内のインデックスは、魔法のように機能するピクシーダストの一種ではありません。あなたはする必要があるクエリを分析し、何をどのようにしてクエリされているかに従って、インデックスを追加します。

「すべてをインデックスに追加して昼寝をする」ほど簡単ではありません

2
zerkms

これがクエリがREADクエリ用に高度に最適化されているデータウェアハウスタイプの操作であり、データを分析する方法が20ある場合.

WHERE句が含まれます。

 Q1: status, type, customer
 Q2: price, customer, band
 Q3: sale_month, band, type, status
 Q4: customer
 etc

そして、あなたは絶対に書き込むための十分な高速ストレージスペースを持っているので、必ずすべての単一の列、個別にのインデックスを作成します。したがって、20列のテーブルには、20個のインデックスが個々の列ごとに1つあります。私はおそらくビット列または低カーディナリティー列を無視すると言うこともできますが、ここまで進んでいるので、なぜ(その警告で)気にするのでしょうか。彼らはただそこに座ってWRITE時間をチャーンしますが、画像のその部分を気にしないのであれば、私たちはすべて問題ありません。

20個のクエリを分析します。それでも高速にならないホットクエリ(最もホットなクエリ)がある場合は、SSMSを使用して(Ctrl-Lを押して)、クエリウィンドウで1つのクエリを計画します。どのクエリがそのクエリに役立つかを教えてくれます-作成するだけです。これらをすべて作成します。これにより、書き込みコスト、バックアップファイルのサイズ、データベースのメンテナンス時間などが再び増えることを完全に覚えておいてください。

2
RichardTheKiwi

...すべての列を含むインデックスを追加し、クエリが実際にそのインデックスを使用できた場合、主キーの順にスキャンされます。つまり、ほぼすべてのレコードをヒットします。平均検索時間はO(n/2)です。実際のデータベースにアクセスするのと同じです。

あなたは読む必要があります ビット インデックスについてたくさん。

テーブルのインデックスをC#のディクショナリのように考えると、役立つ場合があります。

var nameIndex = new Dictionary<String, List<int>>();

つまり、name列にはインデックスが付けられ、主キーのリストが返されます。

var nameOccupationIndex = new Dictionary<String, List<Dictionary<String, List<int>>>>();

つまり、名前列+職業列にインデックスが付けられます。今度は、インデックスに10個の異なる列が含まれていて、これまで深くネストされていて、テーブルのすべての行が含まれているとします。

これはそれがあなたの心にどのように働くか正確ではありません。しかし、C#で実装した場合にインデックスがどのように機能するかがわかるはずです。必要なのは、広範囲に照会される1つまたは2つのキーに基づいてインデックスを作成することです。これにより、テーブル全体をスキャンするよりもインデックスの方が便利です。

2
Josh Smeaton

質問者が尋ねていると思います

'なぜインデックスを作成できないのですか'

create index index_name
on table_name
(
    *
)

その問題は解決されました。

しかし、MS SQLサーバーを使用しているように思えます。非キー列をインデックスに含めることができるので、それらの列の値はインデックスから取得できますが、選択基準としては使用できないことを理解しておくと役立ちます。

create index index_name
on table_name
(
    foreign_key
)
include (a,b,c,d) -- every column except foreign key

100万行の同一の2つのテーブルを作成しました

私はこのようにテーブルAにインデックスを付けました


create nonclustered index index_name_A
on A
(
    foreign_key -- this is a guid
)

そして、このようなテーブルB

create nonclustered index index_name_B
on B
(
    foreign_key -- this is a guid
)
include (id,a,b,c,d) -- ( every key except foreign key)

当然のことですが、テーブルAの方が挿入が少し高速でした。

しかし、私がこれらのクエリを実行したとき

select * from A where foreign_key = @guid
select * from B where foreign_key = @guid

テーブルAでは、SQLサーバーはインデックスも使用せず、テーブルスキャンを実行し、id、a、b、c、dを含むインデックスの欠落について不平を言いました

テーブルBでは、クエリは50倍以上高速で、IOははるかに少なかった

aのクエリでインデックスを使用するように強制しても、インデックスは速くなりませんでした

select * from A where foreign_key = @guid
select * from A with (index(index_name_A)) where foreign_key = @guid

0
compound eye

1)サイズ、インデックスは基本的にその列のデータのコピーをバイナリツリーのような簡単に検索できる構造を構築します(SQL Serverの仕様は知りません)。 2)速度について説明しましたが、インデックス構造の追加は遅くなります。

0
ewanm89