Postgresですべてのテーブルの行数をどのように見つけますか


395

Postgresのすべてのテーブルの行数を見つける方法を探しています。私はこれを一度に1つのテーブルで実行できることを知っています:

SELECT count(*) FROM table_name;

しかし、すべてのテーブルの行数を確認し、それを並べ替えて、すべてのテーブルの大きさを把握したいと思います。

回答:


582

この種のカウントを取得するには3つの方法があり、それぞれ独自のトレードオフがあります。

真のカウントが必要な場合は、各テーブルに対して使用したものと同様のSELECTステートメントを実行する必要があります。これは、PostgreSQLが行の可視性情報を他の場所ではなく行自体に保持するため、正確なカウントは特定のトランザクションにのみ関連するためです。そのトランザクションが実行された時点でそのトランザクションに表示されるものの数を取得しています。これを自動化してデータベース内のすべてのテーブルに対して実行することもできますが、おそらくそのレベルの精度は必要ないか、それほど長く待ちたくないでしょう。

2番目のアプローチは、統計コレクターがいつでも「ライブ」(削除されたり、後の更新によって廃止されたりしない)のおおよその行数を追跡することを示しています。この値は、負荷が高い状況では少しずれている可能性がありますが、概して適切な見積もりです。

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

これは、死んだ行の数を示すこともできます。これは、それ自体が監視する興味深い数値です。

3番目の方法は、テーブルの統計情報を更新するためにPostgreSQL 8.3の時点でautovacuumプロセスによって定期的に実行されるシステムANALYZEコマンドも行推定を計算することです。あなたはこのようなものをつかむことができます:

SELECT 
  nspname AS schemaname,relname,reltuples
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE 
  nspname NOT IN ('pg_catalog', 'information_schema') AND
  relkind='r' 
ORDER BY reltuples DESC;

これらのクエリのどちらを使用する方が良いかは言いがたいです。通常、私はpg_classの内部またはpg_stat_user_tablesの内部でも使用したい有用な情報があるかどうかに基づいてその決定を行います。基本的なカウントの目的では、全体の大きさを確認するためだけに、どちらも十分に正確である必要があります。


2
完了のために、これを最初のオプションに追加してください(@a_horse_with_no_nameに感謝):with tbl as (SELECT table_schema,table_name FROM information_schema.tables where table_name not like 'pg_%' and table_schema in ('public')) select table_schema, table_name, (xpath('/row/c/text()', query_to_xml(format('select count(*) as c from %I.%I', table_schema, table_name), false, true, '')))[1]::text::int as rows_n from tbl ORDER BY 3 DESC;
estani

1
@Greg Smithどのバージョンが導入されましたn_live_tupか?私のRedshiftデータベースにはその列がありません。これはPostgres 8.0.2の派生物です。
Iain Samuel McLean Elder

1
(2番目のアプローチpg_stat_user_tables)クエリ(を使用)は、実行さn_live_tupれたことANALYZEがないため、ほとんどゼロを返しました。ANALYZEすべてのスキーマ/テーブルで実行して回答を永遠に待つのではなく、最初に「第3のアプローチ」を使用して結果を確認し、その(pg_class非常に正確な)カウントが返されました。
ブライアンD

@ BrianD、analyzedb -d dbnameとして
analyticsb

69

以下は、各テーブルの正確なカウントを取得するための関数を必要としないソリューションです。

select table_schema, 
       table_name, 
       (xpath('/row/cnt/text()', xml_count))[1]::text::int as row_count
from (
  select table_name, table_schema, 
         query_to_xml(format('select count(*) as cnt from %I.%I', table_schema, table_name), false, true, '') as xml_count
  from information_schema.tables
  where table_schema = 'public' --<< change here for the schema you want
) t

query_to_xml渡されたSQLクエリを実行し、結果(そのテーブルの行数)を含むXMLを返します。アウターxpath()はそのxmlからカウント情報を抽出し、それを数値に変換します

派生テーブルは実際には必要ありませんがxpath()、少しわかりやすくなります。それ以外の場合は、全体query_to_xml()xpath()関数に渡す必要があります。


3
非常に賢い。残念query_to_jsonb()です。
klin 2016

@a_horse_with_no_name、実行中にビジーで巨大なテーブルでパフォーマンスの問題が発生しますか?
スパイク

@スパイク:何と比較した場合のパフォーマンスの問題?主要なパフォーマンスのボトルネックは、select count(*)すべてのテーブルで実行されています。
a_horse_with_no_name

@ a_horse_with_no_name、1億レコードに対してx_path関数を実行します。
スパイク

@Spike:xpath()関数は一行にのみ適用されますcount(*)
a_horse_with_no_nameの

24

見積もりを取得するには、Greg Smithの回答を参照してください。

正確な数を取得するために、これまでの他の回答はいくつかの問題に悩まされており、そのいくつかは深刻です(以下を参照)。これはうまくいけばより良いバージョンです:

CREATE FUNCTION rowcount_all(schema_name text default 'public')
  RETURNS table(table_name text, cnt bigint) as
$$
declare
 table_name text;
begin
  for table_name in SELECT c.relname FROM pg_class c
    JOIN pg_namespace s ON (c.relnamespace=s.oid)
    WHERE c.relkind = 'r' AND s.nspname=schema_name
  LOOP
    RETURN QUERY EXECUTE format('select cast(%L as text),count(*) from %I.%I',
       table_name, schema_name, table_name);
  END LOOP;
end
$$ language plpgsql;

スキーマ名をパラメーターとして、またはpublicパラメーターが指定されていない場合に使用します。

関数を変更せずにスキーマの特定のリストまたはクエリから取得したリストを操作するには、次のようにクエリ内から呼び出すことができます。

WITH rc(schema_name,tbl) AS (
  select s.n,rowcount_all(s.n) from (values ('schema1'),('schema2')) as s(n)
)
SELECT schema_name,(tbl).* FROM rc;

これにより、スキーマ、テーブル、および行数を含む3列の出力が生成されます。

ここで、この関数が回避する他の回答のいくつかの問題を次に示します。

  • テーブル名とスキーマ名は、引用符なしで実行可能SQLに注入しないでください。形式文字列をquote_ident使用する、またはより近代的なformat()関数を使用する必要があり%Iます。そうしないと、悪意のある人がtablename;DROP TABLE other_tableテーブルに名前を付け、テーブル名として完全に有効です。

  • SQLインジェクションとおかしな文字の問題がなくても、大文字と小文字が異なるバリアントでテーブル名が存在する場合があります。表に名前が付いている場合ABCD、別の1 abcdSELECT count(*) FROM...それ以外の場合はスキップする引用符付きの名前を使用する必要がありますABCDし、カウントabcd回。%Iフォーマットのは、これを自動的に行います。

  • information_schema.tablestable_typeが'BASE TABLE'(!)の場合でも、テーブルに加えてカスタム複合タイプをリストします。結果として、を反復することはできません。反復しないと、が発生するinformation_schema.tablesリスクがselect count(*) from name_of_composite_typeあり、失敗します。OTOH pg_class where relkind='r'は常に正常に動作するはずです。

  • COUNT()のタイプはbigint、ではなく、intです。21億5000万行を超えるテーブルが存在する可能性があります(ただし、それらにcount(*)を実行することはお勧めできません)。

  • 関数が複数の列を持つ結果セットを返すために、永続的な型を作成する必要はありません。RETURNS TABLE(definition...)より良い代替手段です。


18

古くなっている可能性のあるデータを気にしない場合は、クエリオプティマイザーで使用されるのと同じ統計にアクセスできます。

何かのようなもの:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;

@mlissner:自動バキュームの間隔が長すぎる場合、またはANALYZEテーブルでマニュアルを実行していない場合、統計が途切れる可能性があります。データベースの負荷とデータベースの構成方法に関する問題です(統計がより頻繁に更新される場合、統計はより正確になりますが、実行時のパフォーマンスが低下する可能性があります)。最終的に、正確なデータを取得する唯一の方法はselect count(*) from table、すべてのテーブルに対して実行することです。
ig0774

17

必要なHeroku計画を評価しようとしていて、herokuの低速な行カウンターが更新されるのを待つことができない人々に対する、ハッキーで実用的な答え:

基本的にはで実行\dtしたいのでpsql、結果をお気に入りのテキストエディターにコピーします(次のようになります:

 public | auth_group                     | table | axrsosvelhutvw
 public | auth_group_permissions         | table | axrsosvelhutvw
 public | auth_permission                | table | axrsosvelhutvw
 public | auth_user                      | table | axrsosvelhutvw
 public | auth_user_groups               | table | axrsosvelhutvw
 public | auth_user_user_permissions     | table | axrsosvelhutvw
 public | background_task                | table | axrsosvelhutvw
 public | django_admin_log               | table | axrsosvelhutvw
 public | django_content_type            | table | axrsosvelhutvw
 public | django_migrations              | table | axrsosvelhutvw
 public | django_session                 | table | axrsosvelhutvw
 public | exercises_assignment           | table | axrsosvelhutvw

)、次に正規表現検索を実行し、次のように置き換えます:

^[^|]*\|\s+([^|]*?)\s+\| table \|.*$

に:

select '\1', count(*) from \1 union/g

これはあなたにこれに非常に似た何かをもたらします:

select 'auth_group', count(*) from auth_group union
select 'auth_group_permissions', count(*) from auth_group_permissions union
select 'auth_permission', count(*) from auth_permission union
select 'auth_user', count(*) from auth_user union
select 'auth_user_groups', count(*) from auth_user_groups union
select 'auth_user_user_permissions', count(*) from auth_user_user_permissions union
select 'background_task', count(*) from background_task union
select 'django_admin_log', count(*) from django_admin_log union
select 'django_content_type', count(*) from django_content_type union
select 'django_migrations', count(*) from django_migrations union
select 'django_session', count(*) from django_session
;

(最後を削除する必要があります unionにセミコロンを手動で追加)

それを実行すれpsqlば完了です。

            ?column?            | count
--------------------------------+-------
 auth_group_permissions         |     0
 auth_user_user_permissions     |     0
 django_session                 |  1306
 django_content_type            |    17
 auth_user_groups               |   162
 django_admin_log               |  9106
 django_migrations              |    19
[..]

私はこのアイデアが好き
GuilPejon 2018

Atomでは、次のように検索と置換を正規表現で行う必要がありました select '$1', count(*) from $1 union/g
チャック

また、「ユニオンを削除して、末尾にセミコロンを追加する必要があります」と書かれています。これはタイプミスです。削除/g(保持)し、最後にunionセミコロン(;)を1つ追加する必要があります。unionセミコロンの前の最後を削除することを忘れないでください。
チャック

1
unionセミコロンの前の最後を削除することを忘れないでください」が私が意味したものです:)明確にするために「最後」という単語を追加しました
Aur Saraf

10

bashでの回答が受け入れられるかどうかはわかりませんが、FWIW ...

PGCOMMAND=" psql -h localhost -U fred -d mydb -At -c \"
            SELECT   table_name
            FROM     information_schema.tables
            WHERE    table_type='BASE TABLE'
            AND      table_schema='public'
            \""
TABLENAMES=$(export PGPASSWORD=test; eval "$PGCOMMAND")

for TABLENAME in $TABLENAMES; do
    PGCOMMAND=" psql -h localhost -U fred -d mydb -At -c \"
                SELECT   '$TABLENAME',
                         count(*) 
                FROM     $TABLENAME
                \""
    eval "$PGCOMMAND"
done

7
本質的に、これselect count(*) from table_name;はOPで同じに要約されます!
Noach Magedman 2013年

8

特にPostgreSQLでは、私は通常、統計に依存しません。

SELECT table_name, dsql2('select count(*) from '||table_name) as rownum
FROM information_schema.tables
WHERE table_type='BASE TABLE'
    AND table_schema='livescreen'
ORDER BY 2 DESC;
CREATE OR REPLACE FUNCTION dsql2(i_text text)
  RETURNS int AS
$BODY$
Declare
  v_val int;
BEGIN
  execute i_text into v_val;
  return v_val;
END; 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

これは便利ですが、最初のクエリにはrownum値のスキーマも含める必要があります。異なるスキーマに競合する名前がある場合、これは期待どおりに機能しません。したがって、クエリのこの部分は、dsql2('select count(*) from livescreen.'||table_name)それ自体の関数に変換できるように見えるか、よりよく見えるはずです。
jakub-olczyk

6

これを収集したURLを覚えていません。しかし、これがあなたに役立つことを願っています:

CREATE TYPE table_count AS (table_name TEXT, num_rows INTEGER); 

CREATE OR REPLACE FUNCTION count_em_all () RETURNS SETOF table_count  AS '
DECLARE 
    the_count RECORD; 
    t_name RECORD; 
    r table_count%ROWTYPE; 

BEGIN
    FOR t_name IN 
        SELECT 
            c.relname
        FROM
            pg_catalog.pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
        WHERE 
            c.relkind = ''r''
            AND n.nspname = ''public'' 
        ORDER BY 1 
        LOOP
            FOR the_count IN EXECUTE ''SELECT COUNT(*) AS "count" FROM '' || t_name.relname 
            LOOP 
            END LOOP; 

            r.table_name := t_name.relname; 
            r.num_rows := the_count.count; 
            RETURN NEXT r; 
        END LOOP; 
        RETURN; 
END;
' LANGUAGE plpgsql; 

実行select count_em_all();すると、すべてのテーブルの行数が表示されます。


1
通常とquote_ident(t_name.relname)は異なる名前(「column-name」など)が適切にサポートされるように、列名(など)を引用符で囲むことをお勧めします。
gorsky 2010

後で削除するには:DROP FUNCTION count_em_all();
Aalex Gabi 2016

エラーが発生しました:select count_em_all(); エラー:「グループ」またはその近くでの構文エラー行1:SELECT COUNT()AS "カウント" FROMグループ^クエリ:SELECT COUNT()AS "カウント" FROMグループコンテキスト:PL / pgSQL関数count_em_all()行18でFOR以上EXECUTEステートメント
Aalex Gabi 2016

すごい!選択して並べ替えるには– SELECT * FROM count_em_all() as r ORDER BY r.num_rows DESC;
Ken4scholars

6

簡単な2つのステップ:(
注:何も変更する必要はありません-コピーして貼り付けてください)
1.関数を作成します

create function 
cnt_rows(schema text, tablename text) returns integer
as
$body$
declare
  result integer;
  query varchar;
begin
  query := 'SELECT count(1) FROM ' || schema || '.' || tablename;
  execute query into result;
  return result;
end;
$body$
language plpgsql;

2.このクエリを実行して、すべてのテーブルの行数を取得します

select sum(cnt_rows) as total_no_of_rows from (select 
  cnt_rows(table_schema, table_name)
from information_schema.tables
where 
  table_schema not in ('pg_catalog', 'information_schema') 
  and table_type='BASE TABLE') as subq;

または

テーブルごとに行数を取得するには

select
  table_schema,
  table_name, 
  cnt_rows(table_schema, table_name)
from information_schema.tables
where 
  table_schema not in ('pg_catalog', 'information_schema') 
  and table_type='BASE TABLE'
order by 3 desc;

5

すべてのテーブルを含めるための小さなバリエーションを作成しました。また、非パブリックテーブルについても同様です。

CREATE TYPE table_count AS (table_schema TEXT,table_name TEXT, num_rows INTEGER); 

CREATE OR REPLACE FUNCTION count_em_all () RETURNS SETOF table_count  AS '
DECLARE 
    the_count RECORD; 
    t_name RECORD; 
    r table_count%ROWTYPE; 

BEGIN
    FOR t_name IN 
        SELECT table_schema,table_name
        FROM information_schema.tables
        where table_schema !=''pg_catalog''
          and table_schema !=''information_schema''
        ORDER BY 1,2
        LOOP
            FOR the_count IN EXECUTE ''SELECT COUNT(*) AS "count" FROM '' || t_name.table_schema||''.''||t_name.table_name
            LOOP 
            END LOOP; 

            r.table_schema := t_name.table_schema;
            r.table_name := t_name.table_name; 
            r.num_rows := the_count.count; 
            RETURN NEXT r; 
        END LOOP; 
        RETURN; 
END;
' LANGUAGE plpgsql; 

select count_em_all();それを呼び出すために使用します。

この情報がお役に立てば幸いです。ポール


エラー:「r.table_schema」は既知の変数ではありません
slashdottir


1

ダニエルベリテの答えが好きです。ただし、CREATEステートメントを使用できない場合は、bashソリューションを使用するか、Windowsユーザーの場合はPowerShell ソリューションを使用できます。

# You don't need this if you have pgpass.conf
$env:PGPASSWORD = "userpass"

# Get table list
$tables = & 'C:\Program Files\PostgreSQL\9.4\bin\psql.exe' -U user -w -d dbname -At -c "select table_name from information_schema.tables where table_type='BASE TABLE' AND table_schema='schema1'"

foreach ($table in $tables) {
    & 'C:\path_to_postresql\bin\psql.exe' -U root -w -d dbname -At -c "select '$table', count(*) from $table"
}

0

すべてのテーブルの合計+テーブルとそのカウントのリストが必要でした。ほとんどの時間が費やされた場所のパフォーマンスチャートのようなもの

WITH results AS ( 
  SELECT nspname AS schemaname,relname,reltuples
    FROM pg_class C
    LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE 
      nspname NOT IN ('pg_catalog', 'information_schema') AND
      relkind='r'
     GROUP BY schemaname, relname, reltuples
)

SELECT * FROM results
UNION
SELECT 'all' AS schemaname, 'all' AS relname, SUM(reltuples) AS "reltuples" FROM results

ORDER BY reltuples DESC

もちろんLIMIT、このバージョンの結果にも句を付けて、最大の結果を得ることができますnて、合計だけでなく違反者。

これについて注意すべき1つの点は、一括インポート後にしばらく待機する必要があることです。実際のインポートデータを使用して、いくつかのテーブルにまたがるデータベースに5000行を追加するだけで、これをテストしました。約1分間に1800レコードを表示しました(おそらく構成可能なウィンドウ)

これはhttps://stackoverflow.com/a/2611745/1548557の作業に基づいているので、CTE内でクエリを使用することを感謝し、認識しています。

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