私は巨大なテーブルを持っており、いくつかの列x
に異なる値の数がはるかに少ない(桁違いに)。
_SELECT DISTINCT x FROM hugeTable
_のようなクエリを実行する必要があり、これを比較的高速に実行したいと思います。
CREATE INDEX hugeTable_by_x ON hugeTable(x)
のようなことをしましたが、何らかの理由で、出力が小さいにもかかわらず、クエリの実行がそれほど速くありません。クエリプランは、時間の97%が_hugeTable_by_x
_のインデックススキャンに費やされており、推定行数がテーブル全体のサイズに等しいことを示しています。これに続いて、とりわけ、ハッシュ一致操作が行われます。
列x
にインデックスを作成したので、このクエリが非常に高速に実行されることを期待できませんか?
Microsoft SQL Server2005を使用していることに注意してください。
これはおそらくインデックス作成の問題ではなく、データ設計の問題です。正確には、正規化。フィールドの個別の値をクエリする必要があり、インデックスを追加する必要があるという事実は、フィールドを(小さな)結合キーを使用して別のテーブルに正規化する必要があることを示す強力な指標です。次に、はるかに小さいルックアップ外部テーブルをスキャンすることにより、個別の値がすぐに利用可能になります。
更新
回避策として、「distinct」フィールドを使用して、アグリゲートに インデックス付きビュー を作成できます。 COUNT_BIG
は、インデックス付きビューで許可される集計です。
create view vwDistinct
with schemabinding
as select x, count_big(*)
from schema.hugetable
group by x;
create clustered index cdxDistinct on vwDistinct(x);
select x from vwDistinct with (noexpand);
SQL Serverは、途中で重複をスキップして、インデックス内の次の個別の値を直接シークする機能を実装していません。
重複が多い場合は、再帰CTEを使用してこれをシミュレートできる場合があります。テクニックは ここ から来ています。 (「再帰CTEを使用した超高速DISTINCT」)。例えば:
with recursivecte as (
select min(t.x) as x
from hugetable t
union all
select ranked.x
from (
select t.x,
row_number() over (order by t.x) as rnk
from hugetable t
join recursivecte r
on r.x < t.x
) ranked
where ranked.rnk = 1
)
select *
from recursivecte
option (maxrecursion 0)
事前に値がわかっていて、列xにインデックスがある場合(または各値がテーブル全体のseqスキャンですぐに表示される可能性がある場合)、各値を個別にクエリする方がはるかに高速です。
select vals.x
from [values] as vals (x)
where exists (select 1 from bigtable where bigtable.x = vals.x);
Presents()を使用して続行すると、有効な値と同じ数のインデックスルックアップが実行されます。
あなたがそれを書いた方法(値が事前に知られていない場合は正しい)、クエリエンジンはテーブル全体を読み取り、値を抽出するために混乱をハッシュ集計する必要があります。 (これにより、インデックスが役に立たなくなります。)
いいえ。ただし、いくつかの回避策があります(正規化を除く)。
インデックスが配置されると、オプティマイザーが自動的に実行できることをSQLに実装できます。
https://stackoverflow.com/a/29286754/53876 (複数の回避策が引用されています)
他の回答では、問題を解決する正規化が可能であると述べていますが、正規化されたSQL Serverは、スキャンを実行してグループ内のmax()を見つけることを好みます。回避策:
https://dba.stackexchange.com/questions/48848/efficiently-query-max-over-multiple-ranges?rq=1
インデックス付きフィールドでSELECT DISTINCT
を実行する場合、実行ではテーブル全体のインデックス内の各値をスキャンする必要があるため、インデックススキャンは理にかなっています(WHERE
句がないと仮定すると、あなたの例による場合)。
インデックスは通常、WHERE
条件、JOINS
、およびORDER BY
句により大きな影響を与えます。
実行計画の説明によると、それが可能な限り最良の実行であると私は信じています。
インデックススキャンは、インデックス全体を(インデックスの順序ではなく)格納されているとおりに読み取り、HASHMATCHが区別します。
あなたの問題を回避する他の方法があるかもしれません。 SQL Serverでは、インデックス付きビューが頭に浮かびます。しかし、それはあなたにそのテーブルへの書き込みに大ヒットを与えるかもしれません。
列xのカーディナリティが低い場合、ローカルビットマップインデックスを作成すると、パフォーマンスが何倍も向上します。