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


99

カーディナリティが低い(個別の値の数が少ない)フィールドにインデックスを付けることは、実際に行う価値がないことを覚えていました。インデックスがどのように機能するのか、それがなぜなのかを理解するのに十分な知識がないことを認めます。

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

もちろん、テストして実行プランを確認するだけで十分ですが、その背後にある理論についても知りたいです。カーディナリティはいつ重要で、いつ重要ではないのですか?


これは一般的なクエリですか?「一握り」のレコードを探す場合は価値がありますが、他の行についてはあまり役に立ちません。データを特定する他の方法はありますか?
jason saldo 2008年

4
私はビット列だけでインデックスを作成するつもりはないと思いますが、複合インデックスの一部としてビット列を含めることは非常に一般的です。単純な例としては、アプリケーションがアクティブな顧客を常に探している場合、姓だけではなく、ACTIVE、LASTNAMEのインデックスがあります。
BradC 2008年

「カーディナリティが低い(異なる値の数が少ない)フィールドにインデックスを付けることは、実際にやる価値がないことを一度に読んだことを覚えています」SQL Serverはほとんどの場合、テーブルスキャンを実行するよりも、テーブルスキャンを実行する方が効率的であるためインデックス。したがって、基本的にインデックスは使用されず、維持することは無駄です。他の人が言ったように、それは複合インデックスで大丈夫かもしれない。
DJ。

5
私は同意しません。分布が50/50の場合、テーブルスキャンを実行する方が速いため、インデックスを使用することはありません。しかし、あなたが唯一の5、1つの値、および100万の0の値を持っている場合、それは非常に可能性の高い1を検索するときにインデックスを使用することです
Kibbee

1
あなたが挙げた例では、LastNameを最初に置く傾向があります。これは特定のクエリのワークロードに依存しますが、一般的に最初に選択性の高い列を持っているため、インデックスが使用される可能性が高くなります。
ミッチウィート

回答:


72

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

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

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

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

したがって、SQLが実際に行っているのは、反復する必要があるワーキングセット(行数)を減らすことです。

ビットフィールド(またはいくつかの狭い範囲)にインデックスを付ける場合、その値に一致する行の数だけワーキングセットを減らします。一致する行数が少ない場合、ワーキングセットが大幅に削減されます。50/50分散の多数の行の場合、インデックスを最新の状態に保つことと比較して、パフォーマンスの向上はほとんどありません。

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


したがって、ビットフィールドが1である行がほんの少ししかない場合(たとえば、 "IsProcessed"を追跡している場合)は、値で並べ替えてから選択できるため、インデックスは適切です。小さなワーキングセット。同意していただければ追加してください。
jeremcc 2008年

2
私の以前のコメントで私が意味しているのは、「ビットフィールド(または狭い範囲)にインデックスを付けると、ワーキングセットを半分に減らすだけです」というのは、分布が1つの値に大きく重み付けされている場合は当てはまりません。しかし、私はあなたの答えの残りが好きなので、あなたがそれを修正すれば、私はそれを受け入れます。
jeremcc 2008年

1
できました。100万行の場合、ビットフィールドの分布は50%になると考えていましたが、特定の問題のある領域では、ワーキングセットが大幅に削減される可能性があります。
ジェフコックス

インデックスがある場合とない場合の実行プランを見て、インデックスが使用されているかどうか、および実際にクエリのコストを削減できるかどうかを確認することは価値があります。簡単で科学的!
onupdatecascade 2010

ビットフィールド+別のフィールドのインデックス付けについてはどうですか?例えば。Webアクティビティログでは、タイムスタンプにインデックスを付けますが、すべてのhttpsアクションをすばやく表示するには、ビットフィールド「IsHTTPS」+タイムスタンプに役立つインデックスがもう1つあります。それも非効率でしょうか?
原料

19

私は別の方法でこの質問に出くわしました。ほんの一握りのレコードだけが値1を想定している(そしてそれらが興味のあるものである)というステートメントを想定すると、フィルターされたインデックスが適切な選択肢になる可能性があります。何かのようなもの:

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

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


1
クエリ内の述語は、フィルター選択されたインデックスの値にハードコーディングする必要があることに注意してください。パラメータyourBitColumn = @valueで値を渡した場合、オプティマイザはフィルタリングされたインデックスが使用可能かどうかを判断できません。
geofftnz 2016年

2
これを回避する方法はいくつかありますが、あなたは正しいです。オプティマイザーは、コンパイル時に、フィルターされたインデックス述語に一致するすべての述語の値が静的/不変であることを保証する必要があります。これは、パラメーターのセットに対して機能する一般的な計画を作成するのがオプティマイザーの仕事だからです。
Ben Thul

9

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

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


8

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


2
行の99%がビット= 1の場合、オプティマイザはインデックスを無視してテーブルスキャンを実行する必要があります。インデックスを使用すると、少なくとも回転ドライブでは、テーブルスキャンよりも実際にパフォーマンス低下し、I / Oが増加し、ディスクからの読み取りが連続しなくなります。フィルター処理されたインデックス(Postgresの同等物:部分インデックス)が適しています。質問から何年もたっていたので、この回答はそれにふさわしい票を得られなかったと思います。
Andrew Lazarus

7

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

単純な例としては、アプリケーションがほとんど常にアクティブな顧客を探している場合、姓だけではなく、ACTIVE、LASTNAMEのインデックスが挙げられます。


7
あなたが挙げた例では、LastNameを最初に置く傾向があります。これは特定のクエリのワークロードに依存しますが、一般的に最初に選択性の高い列を持っているため、インデックスが使用される可能性が高くなります。
ミッチウィート

7

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

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-bit/

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


この記事は表示されなくなりました
Homer6、2011

@ Homer6この記事の新しい家のようなものへのリンクを追加しました。
ジェフ

新しいリンクがToad Worldホームページに移動します。
北西部

Waybackマシンを使用して記事を見つけ、新しい関連記事を見つけました。お役に立てれば。
ジェフ

2

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

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


2

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

最善の方法は、基本的なSELECT * FROMテーブルWHERE BitField = 1;でをテストすることです。クエリを実行し、アプリケーションの現実的なクエリができるまで、ステップバイステップで機能を段階的に構築し、すべてのステップで実行プランを調べて、インデックスシークが引き続き使用されていることを確認します。確かに、この実行プランが本番環境で使用される保証はありませんが、使用される可能性は十分にあります。

一部の情報は、sql-server-performance.comフォーラムおよび参照記事に記載されています。


重要なのは、列全体のカーディナリティではありません。WHERE句の選択性です。したがって、値が1の列がほとんどない場合でも、インデックスを作成することをお勧めします。50/50(男性/女性など)の場合、それほど価値はありません。
WW。

2

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

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


2

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

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

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


2

インデックスに希望する効果があるかどうかを知りたい場合は、テストしてもう一度テストしてください。

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


1

それだけでは、選択性はほとんどありません。複合インデックスの一部として。かなり可能性がありますが、他の等式列の後だけです。


1

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

ビット

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

備考

タイプがビットの列にインデックスを付けることはできません。

はい。数百万行のうち、行数が少ない場合は、インデックスが役立ちます。ただし、この場合は、列をaにする必要があります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

インデックスシークの後にブックマークルックアップが発生するのではなく、インデックスシークの後にブックマークルックアップが実行されるのではなく、テーブルスキャン(またはクラスター化インデックススキャン)が実行されます。

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


参考までに-SQL Server 2005 Management Studioでは、それを実行できます。
jeremcc 2008年

SQL Server 2000のコピーでは、ビット列にインデックスを設定できました。
Kibbee 08年

SQL Server 2000のコピーでは、ビット列にインデックスを設定できません。
Ian Boyd、

1

非常に遅い答え...

はい、SQL CATチームによる便利です(更新済み、統合済み)


1
リンクは現在無効になっているようです。ただし、その投稿は他のいくつかの電子書籍と統合されたようです。参照セクションは86ページから始まります。この電子書籍は、SQLCAT.com eBooksの「SQLCAT's Guide to Relational Engine」のリンクからダウンロードできます。
mwolfe02

0

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


0

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

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


1
私は同意しません。分布が50/50の場合、テーブルスキャンを実行する方が速いため、インデックスを使用することはありません。しかし、あなたが唯一の5、1つの値、および100万の0の値を持っている場合、それは非常に可能性の高い1を検索するときにインデックスを使用することです
Kibbee

0

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


0

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


0

クエリを実行するには、ここで賢くする必要があります。システムでtrueのロードの方が多い場合は、列のロード値を知っている必要があり、すべてのtrue値をチェックしてクエリがfalseではないことを確認する必要があります。 、それはトリックです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.