web-dev-qa-db-ja.com

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

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

SELECT count(*) FROM table_name;

しかし、私はすべてのテーブルの行数を見て、それから私のすべてのテーブルがどれくらい大きいかのアイデアを得るためにそれによって注文したいです。

337
mmrobins

この種のカウントを取得するには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プロセスによって定期的に実行されてテーブル統計を更新するsystem 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の内部でも使用したいもっと有用な情報があるかどうかに基づいて判断します。基本的なカウントの目的のためだけに、一般的なものの大きさを確認するためには、どちらも十分正確である必要があります。

487
Greg Smith

これは、各テーブルの正確なカウントを得るために関数を必要としない解決策です。

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からcount情報を抽出してそれを数に変換します

派生テーブルは必ずしも必要ではありませんが、xpath()を理解しやすくするために必要です。そうしないと、query_to_xml()全体をxpath()関数に渡す必要があります。

36

見積もりを取得するには、 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列の出力が生成されます。

これが、この関数が回避する他の答えの中のいくつかの問題です。

  • テーブル名とスキーマ名は、quote_identまたはその%Iフォーマット文字列を含む最新のformat()関数を使用して、引用符なしで実行可能SQLにインジェクトすることはできません。そうでなければ、悪意のある人が自分のテーブルにtablename;DROP TABLE other_tableという名前を付けることがあり、これは完全にテーブル名として有効です。

  • SQLインジェクションや変な文字の問題がなくても、テーブル名は大文字と小文字が異なるバリアントで存在する可能性があります。テーブルの名前がABCDで、別のテーブルの名前がabcdである場合、SELECT count(*) FROM...は引用符で囲まれた名前を使用する必要があります。それ以外の場合、ABCDをスキップしてabcdを2回カウントします。 %I of formatはこれを自動的に行います。

  • information_schema.tablesは、table_typeが'BASE TABLE'(!)の場合でも、テーブルに加えてカスタム複合型をリストします。結果として、oninformation_schema.tablesを繰り返すことはできません、そうでなければselect count(*) from name_of_composite_typeを持つ危険性があり、それは失敗するでしょう。 OTOHのpg_class where relkind='r'は常にうまくいくはずです。

  • COUNT()の型はbigintではなくintです。 21.5億行を超えるテーブルが存在する可能性があります(ただし、カウント(*)を実行することはお勧めできません)。

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

21
Daniel Vérité

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

何かのようなもの:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;
16
ig0774

どのHerokuのプランが必要かを評価しようとしていて、herokuの遅いローカウンタがリフレッシュされるのを待つことができない人々のための、ハックで実用的な答え:

基本的にあなたはpsql\dtを実行したい、あなたの好きなテキストエディタに結果をコピーしなさい(それはこのように見えるでしょう:

 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
[..]
13
Aur Saraf

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
11
Stew-au

特に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;
7
Yuri Levinsky

これを収集した場所の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();を実行すると、すべてのテーブルの行数が得られます。

6
Gnanam

非公開テーブルについても、すべてのテーブルを含めるように少しバリエーションを作りました。

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();を使います。

あなたがこれが役に立つことを願っています。ポール

5
Paul

簡単な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;
4
Raju Sah

DanielVéritéの 答え が好きです。しかし、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"
}
1
CFreitas

これは私のために働きました

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

0
Pradeep Maurya