116

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

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

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

4

19 に答える 19

80

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

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

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

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

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

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

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

于 2008-10-23T20:41:35.537 に答える
22

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

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

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

于 2013-09-23T21:26:52.880 に答える
11

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

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

于 2008-10-23T20:58:56.267 に答える
8

まだ読んでいない方のために説明すると、Jason Massie が最近このトピックについて論じた記事を書いています。

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-中古.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

于 2009-01-09T17:11:15.013 に答える
8

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

于 2015-05-21T19:15:07.070 に答える
7

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

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

于 2008-10-23T19:54:36.333 に答える
2

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

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

于 2008-10-23T20:07:12.533 に答える
2

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

あなたの最善の行動は、基本的な SELECT * FROM table WHERE BitField = 1; でテストすることです。クエリを実行し、アプリケーションの現実的なクエリが得られるまで、そこから段階的に機能をゆっくりと構築し、すべてのステップで実行計画を調べて、インデックス シークがまだ使用されていることを確認します。確かに、この実行計画が本番環境で使用されるという保証はありませんが、使用される可能性は十分にあります。

一部の情報は、sql-server-performance.com フォーラムおよび参照記事で見つけることができます。

于 2008-10-23T19:47:18.797 に答える
2

インデックスが希望する効果を持っているかどうかを知りたい場合:テストとテストを繰り返します。

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

于 2008-10-23T19:39:12.793 に答える
2

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

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

于 2008-10-23T19:42:35.657 に答える
2

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

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

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

于 2008-10-23T21:41:03.467 に答える
1

非常に遅い答え...

はい、 SQL CAT チームによると、役に立つ可能性があります(更新、統合されました)

于 2011-12-21T15:20:26.117 に答える
1

当時オンライン ブックで指摘されていたように、SQL Server 2000 ではビット フィールドにインデックスを作成することはできません。

少し

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

備考

ビット型の列にはインデックスを設定できません。

はい、数百万行のうち、ほんの一握りの行しかない場合は、インデックスが役立ちます。ただし、この場合に実行したい場合は、列を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

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

実験と直接観察によって検証されています。

于 2008-10-23T20:29:04.393 に答える
1

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

于 2008-10-23T20:31:05.467 に答える
0

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

于 2010-02-09T23:49:09.060 に答える
0

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

于 2008-10-23T19:38:32.653 に答える
0

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

于 2008-10-23T19:38:43.683 に答える
0

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

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

于 2008-10-23T20:13:22.070 に答える
0

ここでクエリを実行するには賢くする必要があります。システムで true の負荷が多く、すべての true 値をチェックしてクエリを書き込み、false ではないことを確認する場合は、列の負荷値を知る必要があります。これは非常に役立ちます。 、それはただのトリックです。

于 2017-04-01T12:54:33.797 に答える