web-dev-qa-db-ja.com

SQL Serverのビットフィールドにインデックスを付ける必要がありますか?

ある時点で、カーディナリティが低い(異なる値の数が少ない)フィールドにインデックスを付けることは、実際に行う価値がないと読んだことを覚えています。私は、インデックスがどのように機能するかについて十分な知識がなく、その理由を理解できないと認めています。

1億行のテーブルがあり、ビットフィールドが1のレコードを選択している場合はどうなりますか?そして、任意の時点で、ビットフィールドが1(0ではなく)であるレコードはほんの少ししかないとします。そのビットフィールドにインデックスを付ける価値があるかどうか?どうして?

もちろん、私はそれをテストして実行計画を確認することができますが、それを実行しますが、その背後にある理論にも興味があります。カーディナリティはいつ重要で、いつは重要ではありませんか?

94
jeremcc

SQLのインデックスとは何かを検討してください。インデックスは、実際には他のメモリチャンク(行へのポインタ)を指すメモリチャンクです。インデックスはページに分割されるため、使用状況に応じてインデックスの一部をメモリからロードおよびアンロードできます。

行のセットを要求すると、SQLはインデックスを使用して、テーブルスキャン(すべての行を見る)よりも迅速に行を見つけます。

SQLにはクラスター化インデックスと非クラスター化インデックスがあります。クラスター化インデックスについての私の理解は、類似したインデックス値を同じページにグループ化することです。この方法では、インデックス値に一致するすべての行を要求すると、SQLはクラスター化されたメモリページからそれらの行を返すことができます。 GUID列をインデックスにクラスター化しようとするのは悪い考えです-ランダムな値をクラスター化しようとしないでください。

整数列にインデックスを作成すると、SQLのインデックスには各インデックス値の行のセットが含まれます。 1〜10の範囲がある場合、10個のインデックスポインターがあります。行の数に応じて、これは異なる方法でページングできます。クエリが「1」に一致するインデックスを検索し、Nameに「Fred」が含まれる場合(Name列にインデックスが設定されていない場合)、SQLは「1」に一致する行のセットを非常にすばやく取得し、残りを見つけるためにテーブルをスキャンします。

したがって、SQLが実際に行っているのは、反復処理する必要がある作業セット(行数)を削減しようとすることです。

ビットフィールド(または狭い範囲)のインデックスを作成すると、その値に一致する行の数だけワーキングセットが減少します。一致する行の数が少ない場合、ワーキングセットが大幅に削減されます。 50/50分布の多数の行の場合、インデックスを最新の状態に維持するのと比べて、パフォーマンスがほとんど向上しない可能性があります。

誰もがテストする理由は、SQLには非常に巧妙で複雑なオプティマイザーが含まれており、テーブルスキャンが高速であると判断した場合にインデックスを無視したり、並べ替えを使用したり、メモリページを整理したりする可能性があるためです。

67
Geoff Cox

この質問に別の方法で出会ったばかりです。少数のレコードのみが1の値をとるというステートメント(およびそれらが関心のあるものである)を想定すると、フィルター選択されたインデックスが適切な選択になる可能性があります。何かのようなもの:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

これにより、オプティマイザーがクエリの述語である場合に使用するのに十分なほどスマートな、大幅に小さいインデックスが作成されます。

16
Ben Thul

ビットフィールドが1に設定されているレコードはわずかで1億のレコードですか?はい、ビットフィールドにインデックスを付けると、bit = 1レコードのクエリが確実に高速化されると思います。インデックスから対数検索時間を取得し、ビット= 1のレコードを持つ数ページのみをタッチする必要があります。それ以外の場合は、1億件のレコードテーブルのすべてのページをタッチする必要があります。

繰り返しますが、私は間違いなくデータベースの専門家ではなく、重要な何かを見逃している可能性があります。

9
C. Dragon 76

分布が既知で不均衡である場合、行の99%がビット= 1で、1%がビット= 0である場合、ビット= 1でWHERE句を実行すると、全テーブルスキャンはほぼ同じ時間になりますインデックススキャン。ビット= 0の高速クエリが必要な場合、私が知っている最良の方法は、WHEREビット= 0句を追加してフィルター処理されたインデックスを作成することです。その方法では、そのインデックスは1%行のみを格納します。次に、WHERE bit = 0を実行すると、クエリオプティマイザーがそのインデックスを選択できるようになり、そこからのすべての行がbit = 0になります。 。

8

JUSTだけでビット列をインデックス化するとは思いませんが、ビット列を複合インデックスの一部として含めることは非常に一般的です。

簡単な例は、アプリケーションがほとんど常にアクティブな顧客を探しているときに、単なる姓ではなく、ACTIVE、LASTNAMEのインデックスです。

7
BradC

あなたがそれを読んでいない場合のために、ジェイソン・マッシーはこのまさにトピックを議論した最近の記事を書きました。

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

編集:新しい記事の場所- http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

以前の「新しい」記事の場所のウェイバックマシン: http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-ビット/

新しいSQL Server Pediaの場所はToadworldで、このトピックについて議論しているKenneth Fisherの新しい記事があります。

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will- never-be-used.aspx

ウェイバックマシン: http://web.archive.org/web/20150508115802/http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba -myths-an-index-on-a-bit-column-will-never-be-used.aspx

7
Jeff

他の人が言ったように、あなたはこれを測定したいと思うでしょう。これをどこで読んだかは思い出せませんが、インデックスを有効にするには、列の基数が非常に高い(約95%)必要があります。これに対する最善のテストは、インデックスを作成し、BITフィールドの0および1の値の実行計画を調べることです。実行プランにインデックスシーク操作が表示される場合、インデックスが使用されることがわかります。

アクションの最良のコースは、基本的なSELECT * FROMテーブルWHERE BitField = 1;でテストすることです。クエリを実行し、アプリケーションの現実的なクエリが得られるまで、段階的に機能を徐々に構築し、すべてのステップで実行プランを調べて、インデックスシークがまだ使用されていることを確認します。確かに、この実行計画が実稼働で使用される保証はありませんが、使用される可能性は十分にあります。

いくつかの情報は sql-server-performance.comフォーラム および参照されている 記事 にあります。

2

「あるカーディナリティ(異なる値の数が少ない)でフィールドにインデックスを付けることは、実際に行う価値がないことを一度読んだことを覚えています」

SQL Serverは、インデックスを読み取るよりもテーブルスキャンを実行する方がほとんど常に効率的であるためです。したがって、基本的にインデックスは使用されることはなく、それを維持するのは無駄です。他の人が言ったように、複合インデックスでは大丈夫かもしれません。

2
DJ.

ビットフィールド値が「1」に等しいレコードのクエリを高速化することが目標の場合、ビットフィールドが「1」に等しいレコードのみを含むベーステーブルのインデックス付きビューを試すことができます。エンタープライズエディションでは、クエリが指定されたテーブルの代わりにインデックス付きビューを使用してクエリのパフォーマンスを改善できる場合、ビューを使用します。理論的には、これにより、ビットフィールド値が「1」のレコードのみを検索する選択クエリの速度が向上します。

http://www.Microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

これはすべて、あなたがMicrosoft SQL Server 2005 Enterpriseであることを前提としています。同じことが2008年にも当てはまるかもしれませんが、私はそのバージョンについてはよく知りません。

2
Jeremy

インデックスに希望する効果があるかどうかを知りたい場合:test and test again。

一般に、インデックスを維持するコストのために、テーブルを十分に絞り込まないインデックスは必要ありません。 (コスト>利益)。しかし、あなたの場合のインデックスがテーブルを半分にカットするなら、何かを得るかもしれませんが、テーブルに置くだけです。それはすべて、テーブルの正確なサイズ/構造と、その使用方法(読み取り/書き込みの数)に依存します。

2
thijs

もちろん、特にその値でデータを取得する必要がある場合は価値があります。通常のマトリックスを使用する代わりに、スパースマトリックスを使用することに似ています。

SQL 2008では、パーティション関数を使用でき、インデックスに含まれるデータをフィルタリングできます。以前のバージョンの欠点は、すべてのデータに対してインデックスが作成されることですが、興味深い値を別のファイルグループに保存することで最適化できます。

2
Bogdan Maxim

当時のBooks Onlineで示されたように、SQL Server 2000のビットフィールドにインデックスを付けることはできません:

ビット

整数データ型1、0、またはNULL。

備考

タイプbitの列には、インデックスを設定できません。

はい、数百万行のうち数行しかない場合は、インデックスが役立ちます。ただし、この場合にそれを行うには、列をtinyintにする必要があります。

:Enterprise Managerでは、ビット列にインデックスを作成できません。希望する場合は、ビット列にインデックスを手動で作成できます。

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

しかし、SQL Server 2000は実際にはそのようなインデックスを使用しません。インデックスが完全な候補になるクエリを実行します。

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000は代わりにテーブルスキャンを行い、インデックスが存在しないように動作します。列をtinyint SQL Server 2000に変更すると、インデックスシークが実行されます。また、次のカバーされていないクエリ:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

インデックスシークを実行してから、ブックマークルックアップを実行します。


SQL Server 2005では、ビット列のインデックスのサポートが制限されています。例えば:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

カバーするインデックスを介してインデックスシークが発生します。しかし、カバーされていない場合:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

インデックス検索に続いてブックマーク検索が行われることはなく、インデックス検索に続いてブックマーク検索が実行されるのではなく、テーブルスキャン(またはクラスター化インデックススキャン)が実行されます。

実験と直接観察により検証。

1
Ian Boyd

それ自体では、選択性がほとんどないため、いいえ。複合インデックスの一部として。おそらく可能ですが、他の等式列の後にのみ。

1
Craig Nicholson

非常に遅い答え...

はい、できます SQL CATチームによると便利 (更新、統合されました)

1
gbn

Ian Boydは、Enterprise Managerを使用してSQL 2000で実行できないと言ったときは正しいです(T-SQLでの作成に関する彼のメモを参照してください)。

0
John B

カーディナリティは1つの要因であり、もう1つはインデックスがデータをどの程度うまく分割するかです。半分の1と半分の0があれば、それが役立ちます。 (そのインデックスが他のインデックスよりも選択に適したパスであると仮定します)。ただし、挿入および更新の頻度はどれくらいですか? SELECTパフォーマンスのインデックスを追加すると、INSERT、UPDATE、およびDELETEのパフォーマンスも低下するため、注意してください。

1から0(またはその逆)が75%から25%を超えない場合、気にしないでください。

0
Anthony Potts

ここでスマートにクエリを実行する必要があります。システムのtrueの負荷が大きい場合、列の負荷値を知っている必要があります。 、それはただのトリックです。

0
Chetan Verma

測定前後の応答時間、およびそれが価値があるかどうかを確認します。理論的には、インデックス付きフィールドを使用したクエリのパフォーマンスが向上するはずですが、true/false値の分布と、関心のあるクエリに関係する他のフィールドに依存します。

0
Steven A. Lowe

これは一般的なクエリですか? 「一握りの」レコードを探すときに価値があるかもしれませんが、他の行ではあまり役に立ちません。データを識別する他の方法はありますか?

0
jason saldo