web-dev-qa-db-ja.com

非常に大きなテーブルの正確な行数を数えるための最速の方法は?

テーブルにたくさんの行とたくさんの列があるとSELECT COUNT(*) FROM TABLE_NAMEは遅くなると言う記事に出会いました。

私は何十億もの行を含むテーブルを持っています[それはおよそ15の列を持っています]。テーブルの行数の _ exact _ カウントを取得するためのより良い方法はありますか?

あなたの答えの前に以下を考慮してください:

  • データベースベンダーに依存しないソリューションを探しています。 MySQL Oracle MS SQL Server をカバーすれば問題ありません。しかし、 本当に データベースベンダーに依存しないソリューションがある場合は、データベースベンダーごとに異なるソリューションを選択します。

  • 他の外部ツールを使ってこれを行うことはできません。私は主にSQLベースのソリューションを探しています。

  • データベース設計をこれ以上正規化することはできません。それはすでに3NFであり、さらにその周りにはすでにたくさんのコードが書かれています。

210
Swaranga Sarma

簡単な答え:

  • データベースベンダーに依存しないソリューション=標準を使用する= COUNT(*)
  • 概算SQL Serverのソリューションがありますが、範囲外のCOUNT(*)=は使用しないでください

注:

COUNT(1)= COUNT(*)= COUNT(PrimaryKey) 念のため

編集:

SQL Serverの例(14億行、12列)

SELECT COUNT(*) FROM MyBigtable WITH (NOLOCK)
-- NOLOCK here is for me only to let me test for this answer: no more, no less

1回の実行、5:46分、カウント= 1,401,659,700

--Note, sp_spaceused uses this DMV
SELECT
   Total_Rows= SUM(st.row_count)
FROM
   sys.dm_db_partition_stats st
WHERE
    object_name(object_id) = 'MyBigtable' AND (index_id < 2)

2ラン、両方とも1秒未満、カウント= 1,401,659,670

2番目のものはより少ない行を持っています=間違っています。書き込みに応じて同じかそれ以上になります(削除はここで数時間のうちに行われます)。

220
gbn

MySQLでこれまでで最も速い方法は、次のとおりです。

SHOW TABLE STATUS;

必要に応じて、たくさんの追加情報とともに、すべてのテーブルに行数(合計)が即座に表示されます。

25
salbahra

テーブルに多数の行と多数の列があると、SELECT COUNT(*)FROM TABLE_NAMEの処理が遅くなるという記事に遭遇しました。

それはデータベースによって異なります。たとえば、インデックス内で行がアクティブであるかデッドであるかを追跡し、インデックスのみをスキャンして行数を抽出できるようにすることで、カウントを高速化することができます。他の人はそうではなく、その結果、テーブル全体を訪問してライブ行を1つずつ数える必要があります。巨大なテーブルではどちらも遅くなります。

通常、クエリ最適化ツール、テーブル統計などを使用して適切な見積もりを抽出できます。たとえば、PostgreSQLの場合、explain count(*) from yourtableの出力を解析して、適切な行数の見積もりを得ることができます。それでは、私はあなたの2番目の質問に来ます。

私は何十億もの行を含むテーブルを持っています[それはおよそ15の列を持っています]。テーブルの行数の正確な数を取得するためのより良い方法はありますか?

真剣に? :-)あなたは本当に exact 何十億もの行を持つテーブルから数えることを意味していますか?本当によろしいですか? :-)

本当に doの場合、トリガーを使用して合計のトレースを残すことができますが、そうした場合は並行性とデッドロックに注意してください。

10

あなたはこれを試すことができます sp_spaceused(Transact-SQL)

現在のデータベース内のテーブル、インデックス付きビュー、またはService Brokerキューで使用されている行数、予約されているディスク容量、およびディスク全体で使用されているディスク容量を表示します。

9
jams

テーブルの行数の正確なカウントを取得するより良い方法はありますか?

あなたの質問に簡単に答えるために、No

これを行うためにDBMSに依存しない方法が必要な場合、fastestの方法は常に次のようになります。

SELECT COUNT(*) FROM TableName

一部のDBMSベンダーは、自社のシステムでのみ動作するより速い方法を持っている場合があります。これらのオプションの一部は、すでに他の回答に投稿されています。

COUNT(*)はとにかくDBMS(少なくともPRODに値するDB)によって最適化されるべきなので、最適化をバイパスしようとしないでください。

サイドノート:
他の多くのクエリも、テーブルサイズのために完了するまでに時間がかかると確信しています。パフォーマンスに関する懸念は、おそらく速度を考慮してスキーマ設計を検討することで対処する必要があります。これは変更するオプションではないが、10分以上のクエリもオプションではないことがわかったということです。速度が必要な場合、3番目のNFが常に最良のアプローチであるとは限りません。また、レコードが一緒に保存されないhaveでない場合、データを複数のテーブルに分割できる場合があります。考えるべきこと...

9
Jesse Webb

私が使う

select /*+ parallel(a) */  count(1) from table_name a;
5
Mainsh S

私は回答した​​他の人ほど専門家ほど近くはありませんが、テーブルからランダムな行を選択するために使用していた手順に問題がありました(あまり関連性がありません)。ランダムインデックスを計算します。従来のCount(*)またはCount(1)の動作を使用していましたが、クエリが実行されるまでに最大2秒かかることがありました。代わりに( 'tbl_HighOrder'という名前のテーブル用に)使用しています。

Declare @max int

Select @max = Row_Count
From sys.dm_db_partition_stats
Where Object_Name(Object_Id) = 'tbl_HighOrder'

これはうまく機能し、Management Studioのクエリ時間はゼロです。

5
john rains

さて、5年後になり、それが助けになるかどうかわからない:

私はその数を数えようとしていました。 MS SQL Serverの管理Studio を使用してSQL Serverのテーブル内の行のいくつかのオーバーフローエラーが発生したので、私は以下を使用しました:

select count_big (1)FROM [dbname]。[dbo]。[FactSampleValue];

結果 :

24296650578行

5

SQL Serverのエディションが2005/2008の場合は、DMVを使用してテーブル内の行数を計算できます。

-- Shows all user tables and row counts for the current database 
-- Remove is_ms_shipped = 0 check to include system objects 
-- i.index_id < 2 indicates clustered index (1) or hash table (0) 
SELECT o.name, 
 ddps.row_count 
FROM sys.indexes AS i 
 INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID 
 INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID 
 AND i.index_id = ddps.index_id 
WHERE i.index_id < 2 
 AND o.is_ms_shipped = 0 
ORDER BY o.NAME 

SQL Server 2000データベースエンジンの場合、sysindexesは機能しますが、近い将来削除される可能性があるため、将来のSQL Serverのエディションでは使用しないことを強くお勧めします。

サンプルコード: テーブルの行数をすばやく簡単に取得する方法

4
Alireza Maddah

私はこの質問に遅れていますが、これはあなたがMySQLを使ってできることです(私はMySQLを使っているので)。私はここに私の観察を共有しています:

1) SELECT COUNT(*) AS TOTAL_ROWS FROM <TABLE_NAME>

結果
行数: 508534
コンソール出力:影響を受ける行数:0見つかった行数:1警告:0 1クエリーの期間:0.125秒。
多数の行を含むテーブルにはしばらく時間がかかりますが、行数は非常に正確です。

2) SHOW TABLE STATUS or SHOW TABLE STATUS WHERE NAME="<TABLE_NAME>"

結果
行数: 511235
コンソール出力:影響を受ける行数:0見つかった行数:1警告:0 1回のクエリの所要時間:0.250秒要約:行数が正確ではありません。

3) SELECT * FROM information_schema.tables WHERE table_schema = DATABASE();

結果
行数: 507806
コンソール出力:影響を受ける行数:0見つかった行数:48警告:0 1クエリーの期間:1.701秒。
行数が正確ではありません。

私はMySQLまたはデータベースのエキスパートではありませんが、非常に大きなテーブルではオプション2または3を使用して、存在する行数について「公正なアイデア」を得ることができることを私は発見しました。

これらの行数を取得して、UIに統計を表示する必要がありました。上記のクエリで、合計行数が50万行以上であることがわかったので、正確な行数を表示せずに「50万行以上」のような統計情報を表示することにしました。

たぶん私はOPの質問にはまだ答えていませんが、そのような統計が必要な状況で私がしたことを共有しています。私の場合は、おおよその行を表示することは許容できるので、上記は私のために働きました。

3
sunitkatkar

いつも最速の解決策があるとは私は思いません:いくつかのRDBMS /バージョンはより速いオプションを使うSELECT COUNT(*)のための特定の最適化を持ちますが、他は単にテーブルスキャンです。あなたは2番目のセットのためにドキュメンテーション/サポートサイトに行く必要があるでしょう、それはおそらく書かれているためにいくつかのより具体的なクエリを必要とするでしょう。

編集:

スキーマやデータの分布によっては、うまくいく可能性がある考えがあります。たとえば、増加する値、増加するID、またはタイムスタンプや日付を参照するインデックス付きの列がありますか。次に、削除が行われないと仮定して、最新の値(昨日の日付、最新のサンプルポイントでの最大ID値)までカウントを格納し、それを超えるカウントを追加することが可能になります。 。もちろん値やインデックスに非常に依存しますが、あらゆるDBMSのほとんどすべてのバージョンに適用可能です。

3
Mike Woodhouse

私は別のStackOverflow質問/回答からこのスクリプトを得ました:

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'YourTableNameHere'
  AND s.name = N'dbo'
  AND p.index_id IN (0,1);

私のテーブルには5億件のレコードがあり、上記のリターンは1ms以内です。その間、

SELECT COUNT(id) FROM MyTable

全39分52秒かかります。

それらはまったく同じ行数(私の場合は正確に519326012)を返します。

それが常に当てはまるかどうかはわかりません。

3
JakeJ

insert trigger を使用するには費用がかかりすぎるが、 delete trigger を指定することができ、テーブル全体を一度カウントした後に自動インクリメントidがあるカウントをlast-countlast-counted-idとして覚えています、

それから 毎日 id> last-counted-idを数え、それをlast-countに加え、そして新しいlast-counted-idを格納するだけです。

削除されたレコードのID <= last-count-idの場合、削除トリガーはlast-countをデクリメントします。

2
ToolmakerSteve

厳密にはDBMSにとらわれないソリューションではありませんが、少なくともクライアントコードでは違いがわかりません。

1つの行と1つの整数フィールドNを持つ別のテーブルTを作成します。1そして、単に実行するINSERT TRIGGERを作成します。

UPDATE T SET N = N + 1

実行するDELETE TRIGGERも作成します。

UPDATE T SET N = N - 1

その価値があるDBMSは、上記の操作の原子性を保証します。2、そしてNには常に正確な行数が含まれているでしょう。

SELECT N FROM T

トリガーはDBMS固有のものですが、Tから選択するのではなく、サポートされるDBMSごとにクライアントコードを変更する必要はありません。

ただし、特にINSERT/DELETEの直後にCOMMITを実行しない場合は、テーブルにINSERTまたはDELETEを多用する場合、スケーラビリティの問題が発生する可能性があります。


1 これらの名前は単なるプレースホルダーです - プロダクションにおいてより意味のあるものを使用してください。

2 すなわち読み取りと書き込みの両方が単一のSQLステートメントで行われる限り、Nの読み取りと書き込みの間の並行トランザクションによってNを変更することはできません。

2

私はこの良い記事を見つけました SQL Server - HOW-TO:テーブルの正確な行数を素早く取り出すmartijnh1から各シナリオの概要を説明します。

特定の条件に基づいてカウントを提供する必要がある場合はこれを拡張する必要があります。この部分を理解したら、この回答をさらに更新します。

それまでの間、記事からの詳細は以下のとおりです。

方法1:

クエリ:

SELECT COUNT(*) FROM Transactions 

コメント:

全表スキャンを実行します。大きなテーブルでは遅くなります。

方法2:

クエリ:

SELECT CONVERT(bigint, rows) 
FROM sysindexes 
WHERE id = OBJECT_ID('Transactions') 
AND indid < 2 

コメント:

行数を取得するための高速な方法。統計に依存し、不正確です。

大きなテーブルではかなり時間がかかる可能性があるDBCC UPDATEUSAGE(Database)WITH COUNT_ROWSを実行します。

方法3:

クエリ:

SELECT CAST(p.rows AS float) 
FROM sys.tables AS tbl 
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and
idx.index_id < 2 
INNER JOIN sys.partitions AS p ON p.object_id=CAST(tbl.object_id AS int) 
AND p.index_id=idx.index_id 
WHERE ((tbl.name=N'Transactions' 
AND SCHEMA_NAME(tbl.schema_id)='dbo')) 

コメント:

SQL Management Studioが行を数える方法(テーブルプロパティ、ストレージ、行数を見てください)。非常に高速ですが、それでもおよその行数です。

方法4:

クエリ:

SELECT SUM (row_count) 
FROM sys.dm_db_partition_stats 
WHERE object_id=OBJECT_ID('Transactions')    
AND (index_id=0 or index_id=1); 

コメント:

迅速な(方法2ほど速くはないが)操作および同様に重要で信頼性が高い。

2
Thierry

文字通り不可解な答えですが、ある種の複製システムを設定している場合(10億行のシステムでは、MAX(pk)のように)、その値を次の数で割ることができます。あなたが持っている奴隷は、並行していくつかのクエリを実行します。

ほとんどの場合、次のような方法で、最良のキー(または私が推測する主キー)に基づいて、クエリをスレーブ間で分割します(Rows/Slavesとして250000000を使用します)。

-- First slave
SELECT COUNT(pk) FROM t WHERE pk < 250000000
-- Ith slave where 2 <= I <= N - 1
SELECT COUNT(pk) FROM t WHERE pk >= I*250000000 and pk < (I+1)*250000000
-- Last slave
SELECT COUNT(pk) FROM t WHERE pk > (N-1)*250000000

しかしSQLだけが必要です。なんてバストなんだ。さて、あなたはサドマゾニストだとしましょう。マスター(または最寄りのスレーブ)で、おそらくこれを行うためのテーブルを作成する必要があります。

CREATE TABLE counter_table (minpk integer, maxpk integer, cnt integer, slaveid integer)

それで、あなたの奴隷で実行中のselectだけを持つのではなく、これに似た挿入をする必要があるでしょう:

INSERT INTO counter_table VALUES (I*25000000, (I+1)*250000000, (SELECT COUNT(pk) FROM ... ), @@SLAVE_ID)

あなたは奴隷達がマスターのテーブルに書いている時に問題に遭遇するかもしれません。あなたはさらにもっと悲しいことをする必要があるかもしれません。

-- A table per slave!
INSERT INTO counter_table_slave_I VALUES (...)

最後に、最初のスレーブに対して、複製グラフがたどるパスの最後に存在するスレーブがあるはずです。そのスレーブは今や他のすべてのカウンタ値を持ち、それ自身の値を持つべきです。しかし、終了する頃には、おそらく行が追加されているので、counter_tableに記録された最大pkと現在の最大pkを補正する別の行を挿入する必要があります。

その時点では、合計行数を把握するために集約関数を実行する必要がありますが、最大で「所有するスレーブの数と変更する」行数に対して実行するほうが簡単です。

あなたがスレーブに別々のテーブルを持っている状況にあるなら、あなたはあなたが必要とするすべての行を得るためにUNIONすることができます。

SELECT SUM(cnt) FROM (
    SELECT * FROM counter_table_slave_1
      UNION
    SELECT * FROM counter_table_slave_2
      UNION
    ...
  )

あるいは、データを分散処理システムに移行すること、またはデータウェアハウスソリューションを使用すること(多分将来的には素晴らしいデータ処理が可能になるでしょう)。

注意してください、これはあなたのレプリケーションがどの程度うまく設定されているかに依存します。主なボトルネックは永続的なストレージである可能性が最も高いので、あなたが雑然としたストレージや重い近隣ノイズのあるデータストアの分離が不十分な場合、これはおそらく単一のSELECT COUNT(*) ...を待つより遅くなります。

しかし、あなたが良い複製を持っているなら、あなたの速度の向上は直接数か奴隷に関連するはずです。実際、カウントクエリを単独で実行するのに10分かかり、8人のスレーブがいる場合、時間は数分以内に短縮されます。このソリューションの詳細を解決するには、おそらく1時間かかるでしょう。

もちろん、この分散解法では行を削除して挿入できる時間が少しかかるため、驚くほど正確な答えは得られませんが、同じインスタンスで行の分散ロックを取得して正確なカウントを取得することができます。特定の時点におけるテーブル内の行の数。

実際には、これは不可能に思えます。基本的にはSQLのみの解決策で立ち往生しているからです。そして、即座に複数のスレーブにまたがってシャードでロックされたクエリを実行するメカニズムは提供されません。レプリケーションログファイルを制御していたのであれば...これは、文字通りこの目的のためにスレーブをスピンアップすることを意味します。いずれにしても、1台のマシンでcountクエリを実行するよりも遅くなりません。

だから私の2013年のペニーは2つあります。

2
Yangmun Choi

行が削除されない自動インクリメントの主キー列を持つ典型的なテーブル構造がある場合は、レコード数を決定する最も簡単な方法は次のとおりであり、ほとんどのANSI準拠データベースで同様に機能するはずです。

SELECT TOP(1) <primarykeyfield> FROM <table> ORDER BY <primarykeyfield> DESC;

レコード数など、1秒未満のデータ応答時間が必要な数十億行を含むMS SQLテーブルを使用しています。同様のSELECT COUNT(*)を比較すると、処理に数分かかります。

1
KevinS

SQLサーバのためにこれを試してください

SELECT T.name, 
       I.rows AS [ROWCOUNT] 
FROM   sys.tables AS T 
       INNER JOIN sys.sysindexes AS I 
               ON T.object_id = I.id AND I.indid < 2 
WHERE T.name = 'Your_Table_Name'
ORDER  BY I.rows DESC 
1

Oracleを使用している場合、これはどうですか(テーブル統計が更新されていると仮定して)。

select <TABLE_NAME>, num_rows, last_analyzed from user_tables

last_analyzedは、統計が最後に収集された時刻を表示します。

0
ZenithDreams

いくつかの列にインデックスを付けます。これにより、オプティマイザはテーブルのフルスキャンではなく、インデックスブロックのフルスキャンを実行できます。それによってIOコストが削減されます。前後の実行計画を見てください。それから両方の方法で実時間を測定します。

0
EvilTeach

sysindexesからid = Object_ID( 'TableName')およびindid <2の行を選択します。

0
Enzero

PostgreSQLの場合:

SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = 'table_name'
0
Dorian