web-dev-qa-db-ja.com

SQL Serverのカウントが遅い

大量のデータを含むテーブルのカウントは非常に遅くなる場合があり、数分かかる場合があります。また、ビジーなサーバーでデッドロックを生成する場合があります。実際の値を表示したいのですが、NOLOCKはオプションではありません。

使用するサーバーは、SQL Server 2005または2008 StandardまたはEnterpriseです(重要な場合)。 SQL Serverがすべてのテーブルのカウントを維持し、WHERE句がない場合、その数をかなり迅速に取得できると想像できますか?

例えば:

SELECT COUNT(*) FROM myTable

すぐに正しい値で戻る必要があります。統計を更新する必要がありますか?

34
Adi

近似値(実行中のトランザクションを無視する)は次のようになります。

_SELECT SUM(p.rows) FROM sys.partitions AS p
  INNER JOIN sys.tables AS t
  ON p.[object_id] = t.[object_id]
  INNER JOIN sys.schemas AS s
  ON s.[schema_id] = t.[schema_id]
  WHERE t.name = N'myTable'
  AND s.name = N'dbo'
  AND p.index_id IN (0,1);
_

これはCOUNT(*)よりもはるかに速く戻ります。また、テーブルが十分に速く変化している場合は、それほど正確ではありません-COUNTを開始したとき(およびロックを取得したとき)と返されたとき(ロックが解放され、すべての待機中の書き込みトランザクションがテーブルへの書き込みを許可されたとき)、それははるかに価値がありますか?そうは思いません。

カウントするテーブルのサブセット(_WHERE some_column IS NULL_など)がある場合、その列にフィルター選択されたインデックスを作成し、例外かどうかに応じて、where句を何らかの方法で構造化できます。またはルール(したがって、より小さなセットでフィルター選択されたインデックスを作成します)。したがって、これら2つのインデックスのいずれか:

_CREATE INDEX IAmTheException ON dbo.table(some_column)
  WHERE some_column IS NULL;

CREATE INDEX IAmTheRule ON dbo.table(some_column)
  WHERE some_column IS NOT NULL;
_

次に、以下を使用して同様の方法でカウントを取得できます。

_SELECT SUM(p.rows) FROM sys.partitions AS p
  INNER JOIN sys.tables AS t
  ON p.[object_id] = t.[object_id]
  INNER JOIN sys.schemas AS s
  ON s.[schema_id] = t.[schema_id]
  INNER JOIN sys.indexes AS i
  ON p.index_id = i.index_id
  WHERE t.name = N'myTable'
  AND s.name = N'dbo'
  AND i.name = N'IAmTheException' -- or N'IAmTheRule'
  AND p.index_id IN (0,1);
_

逆を知りたい場合は、上記の最初のクエリから差し引くだけです。

56
Aaron Bertrand

(「大量のデータ」はどれくらいの大きさですか?-これを最初にコメントする必要がありますが、おそらく以下のエグゼクティブがあなたを助けてくれます)

私の開発マシンで15秒で2億行とCOUNT(*)を持つテーブルで静的(他の誰もかなり長い間読み取り/書き込み/更新で迷惑なことはないので競合は問題ないことを意味します) Oracle)。純粋なデータ量を考慮すると、これはまだ非常に高速です(少なくとも私にとって)

NOLOCKはオプションではないと言ったので、検討することができます

exec sp_spaceused 'myTable'

同様に。

しかし、これはNOLOCKとほぼ同じに固定されます(競合+削除/更新を無視)

9
Najzero

私は10年以上もSSMSを扱ってきましたが、過去1年だけで this answer のおかげで、この情報をすばやく簡単に提供できることがわかりました。

  1. データベースツリー(オブジェクトエクスプローラー)から[テーブル]フォルダーを選択します。
  2. F7を押すか、View>Object Explorer DetailsでObject Explorer Detailsビューを開きます
  3. このビューでは、列ヘッダーを右クリックして、使用する表スペース、使用する索引スペース、行数など、表示する列を選択できます。 enter image description here

Azure SQLデータベースでのこのサポートは、せいぜい少し不安定に見えることに注意してください-私の推測では、SSMSからのクエリはタイムアウトしているため、更新ごとに少数のテーブルのみを返しますが、強調表示されたテーブルは常に返されるようです。

1
pcdev

Countは、テーブルスキャンまたはインデックススキャンを実行します。そのため、行数が多い場合は遅くなります。この操作を頻繁に行う場合、最善の方法は、カウントレコードを別のテーブルに保持することです。

ただし、これを行いたくない場合は、ダミーインデックス(クエリで使用されない)を作成し、次のようなアイテムの数をクエリできます。

select 
    row_count
from sys.dm_db_partition_stats as p
inner join sys.indexes as i 
  on p.index_id = i.index_id
  and p.object_id = i.object_id
where   i.name = 'your index'

このインデックス(使用しない場合)は他の操作中にロックされないため、新しいインデックスを作成することをお勧めします。

アーロン・バートランドが言ったように、既存のクエリを使用するよりもクエリを維持する方がコストがかかる場合があります。したがって、選択はあなた次第です。

1