web-dev-qa-db-ja.com

列xにインデックスを作成して、SELECT DISTINCT x FROMhugeTableクエリを最適化できますか?

私は巨大なテーブルを持っており、いくつかの列xに異なる値の数がはるかに少ない(桁違いに)。

_SELECT DISTINCT x FROM hugeTable_のようなクエリを実行する必要があり、これを比較的高速に実行したいと思います。

CREATE INDEX hugeTable_by_x ON hugeTable(x)のようなことをしましたが、何らかの理由で、出力が小さいにもかかわらず、クエリの実行がそれほど速くありません。クエリプランは、時間の97%が_hugeTable_by_x_のインデックススキャンに費やされており、推定行数がテーブル全体のサイズに等しいことを示しています。これに続いて、とりわけ、ハッシュ一致操作が行われます。

xにインデックスを作成したので、このクエリが非常に高速に実行されることを期待できませんか?

Microsoft SQL Server2005を使用していることに注意してください。

22

これはおそらくインデックス作成の問題ではなく、データ設計の問題です。正確には、正規化。フィールドの個別の値をクエリする必要があり、インデックスを追加する必要があるという事実は、フィールドを(小さな)結合キーを使用して別のテーブルに正規化する必要があることを示す強力な指標です。次に、はるかに小さいルックアップ外部テーブルをスキャンすることにより、個別の値がすぐに利用可能になります。

更新
回避策として、「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);
23
Remus Rusanu

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)
7
Martin Smith

事前に値がわかっていて、列xにインデックスがある場合(または各値がテーブル全体のseqスキャンですぐに表示される可能性がある場合)、各値を個別にクエリする方がはるかに高速です。

select vals.x
from [values] as vals (x)
where exists (select 1 from bigtable where bigtable.x = vals.x);

Presents()を使用して続行すると、有効な値と同じ数のインデックスルックアップが実行されます。

あなたがそれを書いた方法(値が事前に知られていない場合は正しい)、クエリエンジンはテーブル全体を読み取り、値を抽出するために混乱をハッシュ集計する必要があります。 (これにより、インデックスが役に立たなくなります。)

3

いいえ。ただし、いくつかの回避策があります(正規化を除く)。

インデックスが配置されると、オプティマイザーが自動的に実行できることをSQLに実装できます。

https://stackoverflow.com/a/29286754/53876 (複数の回避策が引用されています)

他の回答では、問題を解決する正規化が可能であると述べていますが、正規化されたSQL Serverは、スキャンを実行してグループ内のmax()を見つけることを好みます。回避策:

https://dba.stackexchange.com/questions/48848/efficiently-query-max-over-multiple-ranges?rq=1

1
crokusek

インデックス付きフィールドでSELECT DISTINCTを実行する場合、実行ではテーブル全体のインデックス内の各値をスキャンする必要があるため、インデックススキャンは理にかなっています(WHERE句がないと仮定すると、あなたの例による場合)。

インデックスは通常、WHERE条件、JOINS、およびORDER BY句により大きな影響を与えます。

0
Jerad Rose

実行計画の説明によると、それが可能な限り最良の実行であると私は信じています。

インデックススキャンは、インデックス全体を(インデックスの順序ではなく)格納されているとおりに読み取り、HASHMATCHが区別します。

あなたの問題を回避する他の方法があるかもしれません。 SQL Serverでは、インデックス付きビューが頭に浮かびます。しかし、それはあなたにそのテーブルへの書き込みに大ヒットを与えるかもしれません。

0
Markus Winand

列xのカーディナリティが低い場合、ローカルビットマップインデックスを作成すると、パフォーマンスが何倍も向上します。