PostgreSQLのテーブルの行数をすばやく見つける方法


107

パーセンテージを計算するには、テーブルの行数を知る必要があります。総数が事前定義された定数よりも大きい場合は、定数値を使用します。それ以外の場合は、実際の行数を使用します。

使用できますSELECT count(*) FROM table。しかし、私の定数値がある場合は500,000と私が持っている5,000,000,000多くの時間を無駄になるすべての行を数え、私のテーブルの行を。

私の定数値を超えるとすぐにカウントを停止することはできますか?

特定の制限を下回っている場合にのみ、正確な行数が必要です。それ以外の場合、カウントが制限を超えている場合は、代わりに制限値を使用して、できるだけ早く答えを求めます。

このようなもの:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;

5
n = 定数+ 1である最初のn行を選択しようとしただけではありませんか?それがあなたの定数よりも多くを返す場合、あなたはあなたがあなたの定数を使うべきであると知っています、そしてそれがあなたが良くないなら?
gddc '30年

あなたは、テーブル内の同一性または自動インクリメントフィールドを持っていますか
スパーキー

1
@Sparky:シーケンスに基づくPKが連続しているとは限りません。行が削除されるか、トランザクションの中止によってギャップが生じる可能性があります。
muが短すぎる

更新は元の質問と矛盾しているようです...行の正確な数を知る必要がありますか、それともしきい値を下回っている場合にのみ正確な数を知る必要がありますか?
Flimzy、2011年

1
@RenatoDinhaniConceição:解決しようとしている正確な問題を説明できますか?以下の私の答えは、あなたが最初に言ったのはあなたの問題だと思っています。更新により、count(*)およびその他の多くのフィールドが必要なように見えます。あなたがやろうとしていることを正確に説明できればそれは助けになるでしょう。ありがとう。
Ritesh 2011年

回答:


224

大きなテーブルの行をカウントすることは、PostgreSQLでは遅いことが知られています。正確な数を取得するには、MVCCの性質により、行を完全にカウントする必要があります。あなたの場合のようにカウントが正確である必要がない場合、これを劇的スピードアップする方法があります。

正確なカウントを取得する代わりに(大きなテーブルでは遅くなります):

SELECT count(*) AS exact_count FROM myschema.mytable;

次のような概算が得られます(非常に高速)。

SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';

見積もりがどれだけ近いかは、ANALYZE十分に実行しているかどうかによって異なります。それは通常非常に近いです。PostgreSQL Wiki FAQを
参照してください
または、count(*)パフォーマンス専用のwikiページ

さらに良い

PostgreSQLのWikiの中の記事をされビットずさん。異なるスキーマの1つのデータベースに同じ名前のテーブルが複数存在する可能性を無視しました。それを説明するには:

SELECT c.reltuples::bigint AS estimate
FROM   pg_class c
JOIN   pg_namespace n ON n.oid = c.relnamespace
WHERE  c.relname = 'mytable'
AND    n.nspname = 'myschema'

またはもっと良い

SELECT reltuples::bigint AS estimate
FROM   pg_class
WHERE  oid = 'myschema.mytable'::regclass;

より速く、よりシンプルに、より安全に、よりエレガントに。Object Identifier Typesのマニュアルを参照してください。

to_regclass('myschema.mytable')Postgres 9.4以降で使用して、無効なテーブル名の例外を回避します。


TABLESAMPLE SYSTEM (n) Postgres 9.5以降

SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);

@a_horseがコメントしたように、SELECTコマンドに新しく追加された句は、pg_class何らかの理由で統計が十分に最新でない場合に役立つことがあります。例えば:

  • autovacuum実行していません。
  • 直後に大きいINSERTDELETE
  • TEMPORARYテーブル(これはでカバーされていませんautovacuum)。

これは、ランダムなn%(1例では)のブロックの選択のみを調べ、その中の行をカウントします。サンプルが大きいほど、コストが増加し、エラーが減ります。精度はより多くの要因に依存します:

  • 行サイズの分布。特定のブロックがたまたま通常よりも広い行を保持している場合、カウントは通常より少なくなります。
  • デッドタプルまたはFILLFACTORブロックごとの占有スペース。テーブル全体に不均一に分布している場合、見積もりがずれている可能性があります。
  • 一般的な丸めエラー。

ほとんどの場合、からの推定はpg_classより速く、より正確になります。

実際の質問への回答

最初に、合計数が事前定義された定数よりも大きい場合、そのテーブルの行数を知る必要があります。

そして、それが...

...カウントが私の定数値を通過した瞬間に可能になります。これはカウントを停止します(行カウントがより大きいことを通知するためにカウントが完了するのを待ちません)。

はい。次の場合にサブクエリをLIMIT使用できます。

SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;

Postgresは実際には指定された制限を超えてカウント停止し、最大n行(例では500000)までの正確な現在のカウントを取得します。それ以外の場合はnです。ただし、の推定ほど速くはありません。pg_class


8
最終的にPostgres Wikiページを改善されたクエリで更新しました。
Erwin Brandstetter 2013年

5
9.5では、次のtablesample句を使用して見積もりを高速に取得できるはずです。例:select count(*) * 100 as cnt from mytable tablesample system (1);
a_horse_with_no_name

1
@JeffWidman:これらの推定値すべて、さまざまな理由で実際の行数よりも大きくなる可能性があります。特に、削除はその間に行われた可能性があります。
Erwin Brandstetter、2015

2
@ErwinBrandstetterはこの質問が古いことを認識していますが、クエリをサブクエリでラップした場合、制限を行っても、これは依然として効率的であるか、サブクエリ全体が実行されてから外部クエリで制限されます。 SELECT count(*) FROM (Select * from (SELECT 1 FROM token) query) LIMIT 500000) limited_query;(すでに制限句が含まれている可能性のある任意のクエリからカウントを取得しようとしているため、私は質問します)
Nicholas Erdenberger

1
@NicholasErdenberger:それはサブクエリに依存します。とにかく、Postgresは制限よりも多くの行を考慮する必要がある場合があります(ORDER BY somethingインデックスを使用できない場合、または集計関数を使用する場合など)。それ以外は、サブクエリからの限られた数の行のみが処理されます。
Erwin Brandstetter 2017年

12

私はpostgresアプリで一度実行することでこれを行いました:

EXPLAIN SELECT * FROM foo;

次に、正規表現または同様のロジックで出力を調べます。単純なSELECT *の場合、出力の最初の行は次のようになります。

Seq Scan on uids  (cost=0.00..1.21 rows=8 width=75)

このrows=(\d+)値は、返される行数のおおよその見積もりとして使用できます。実際のSELECT COUNT(*)計算は、見積もりが、たとえばしきい値の1.5倍(または、アプリケーションにとって意味のある数)未満である場合にのみ行います。

クエリの複雑さによっては、この数値はますます正確でなくなる可能性があります。実際、私のアプリケーションでは、結合と複雑な条件を追加したため、100の累乗の範囲で返された行の数を知ることさえまったく不正確になり、その戦略を放棄する必要がありました。

しかし、Pgが返すことができる行数をPgがある程度の誤差範囲内で予測できるほど単純なクエリであれば、うまくいくかもしれません。


2

このブログから引用した参照。

以下を使用してクエリを実行し、行数を見つけることができます。

pg_classの使用:

 SELECT reltuples::bigint AS EstimatedCount
    FROM   pg_class
    WHERE  oid = 'public.TableName'::regclass;

pg_stat_user_tablesの使用:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;

このメソッドが機能するためには、テーブルをVACUUM ANALYZEする必要があることに注意してください。
William Abma

1

Oracleでは、を使用rownumして、返される行の数を制限できます。他のSQLにも同様の構成が存在すると思います。したがって、指定した例では、返される行の数を500001に制限し、thenを適用できますcount(*)

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)

1
SELECT count(*)cnt FROM tableは常に単一の行を返します。LIMITがどのようにそこに利益をもたらすかはわかりません。
Chris Bednarski、2011年

@ChrisBednarski:私はOracle dbで私の回答のOracleバージョンを確認しました。それはうまく機能し、私がOPの問題だと思ったことを解決count(*)します(rownumを使用して0.05秒、rownumを使用せずに1秒)。はい、SELECT count(*) cnt FROM table常に1行を返すために起こっているが、テーブルのサイズが500000を超えていると<サイズ>するとき、テーブルのサイズ<= 500000ときLIMIT条件で、それは「500001」を返します
Ritesh

2
PostgreSQLクエリは完全にナンセンスです。構文的にも論理的にも間違っています。修正するか削除してください。
Erwin Brandstetter、2011年

@ErwinBrandstetter:削除されましたが、PostgreSQLがそれほど異なることに気付きませんでした。
Ritesh 2011年

@allrite:間違いなく、Oracleクエリは正常に機能します。LIMITの動作は異なります。基本的なレベルでは、データベースエンジンによってクエリされた行数ではなく、クライアントに返される行数を制限します。
Chris Bednarski、2011年

0

テキスト列の幅はどのくらいですか?

GROUP BYでは、データスキャン(少なくともインデックススキャン)を回避するためにできることはあまりありません。

私はお勧めします:

  1. 可能であれば、スキーマを変更してテキストデータの重複を削除します。このようにして、「多数の」テーブルの狭い外部キーフィールドでカウントが行われます。

  2. または、テキストのHASHを含む生成列を作成し、ハッシュ列をGROUP BYします。繰り返しますが、これはワークロードを減らすためです(狭い列インデックスをスキャンします)

編集:

元の質問は編集内容と完全には一致しませんでした。COUNTをGROUP BYと一緒に使用すると、テーブル全体のアイテム数ではなく、グループごとのアイテム数が返されることを知っているかどうかはわかりません。


0

以下のクエリ(*または列名なし)でカウントを取得できます。

select from table_name;

2
これはと比べて高速ではないようですcount(*)
晴れた

-3

SQL Server(2005以降)の場合、迅速で信頼性の高い方法は次のとおりです。

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

sys.dm_db_partition_statsの詳細はMSDNで説明されています

クエリは、(おそらく)パーティションテーブルのすべての部分から行を追加します。

index_id = 0は順序付けられていないテーブル(ヒープ)であり、index_id = 1は順序付けされたテーブル(クラスター化インデックス)です。

さらに高速な(ただし信頼性の低い)メソッドについては、ここで詳しく説明します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.