关于postgresql:如何在Postgres中找到所有表的行数

How do you find the row count for all your tables in Postgres

我正在寻找一种方法来查找Postgres中所有表的行数。 我知道我可以一次做一张桌子:

1
SELECT COUNT(*) FROM TABLE_NAME;

但我希望看到所有表格的行数,然后按顺序排列,以了解我所有表格的大小。


有三种方法可以获得这种计数,每种方法都有自己的权衡。

如果需要真正的计数,则必须像对每个表使用的那样执行SELECT语句。这是因为PostgreSQL将行可见性信息保留在行本身,而不是其他任何地方,因此任何准确的计数只能与某个事务相关。您将获得该事务在执行时所看到的内容的计数。您可以自动执行此操作以针对数据库中的每个表运行,但您可能不需要那么高的准确度或者想要等待那么久。

第二种方法指出,统计信息收集器随时跟踪大约有多少行是"活动的"(未被更新后删除或废弃)。在重度活动下,这个值可能有点偏差,但通常是一个很好的估计:

1
2
3
SELECT schemaname,relname,n_live_tup
  FROM pg_stat_user_tables
  ORDER BY n_live_tup DESC;

这也可以显示有多少行已死,这本身就是一个有趣的数字。

第三种方法是注意系统ANALYZE命令,它定期执行autovacuum进程,从PostgreSQL 8.3开始更新表统计信息,也可以计算行估计值。你可以像这样抓住那个:

1
2
3
4
5
6
7
8
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中使用。出于基本的计数目的,只是为了看一般情况有多大,要么都应该足够准确。


这是一个解决方案,不需要函数来获得每个表的准确计数:

1
2
3
4
5
6
7
8
9
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()函数。


要获得估算,请参阅Greg Smith的回答。

为了得到确切的数字,到目前为止的其他答案都存在一些问题,其中一些是严重的(见下文)。这是一个希望更好的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

要在不修改函数的情况下使用特定的模式列表或来自查询的列表,可以在查询中调用它,如下所示:

1
2
3
4
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或使用带有%I格式字符串的更现代的format()函数。否则,某些恶意的人可能将其表tablename;DROP TABLE other_table命名为完全有效的表名。

  • 即使没有SQL注入和有趣的字符问题,表名也可能存在于大小写不同的变体中。如果表名为ABCD而另一个ABCD,则SELECT count(*) FROM...必须使用带引号的名称,否则将跳过ABCD并计算ABCD两次。格式的%I会自动执行此操作。

  • information_schema.tables除表之外还列出自定义复合类型,即使table_type为'BASE TABLE'(!)。因此,我们不能迭代information_schema.tables,否则我们冒险select count(*) from name_of_composite_type而且会失败。 OTOH pg_class where relkind='r'应始终正常。

  • COUNT()的类型是bigint,而不是int。可能存在超过21.5亿行的表(虽然对它们运行计数(*)是个坏主意)。

  • 不需要为函数创建永久类型以返回具有多个列的结果集。 RETURNS TABLE(definition...)是更好的选择。


如果您不介意可能过时的数据,则可以访问查询优化程序使用的相同统计信息。

就像是:

1
SELECT relname, n_tup_ins - n_tup_del AS rowcount FROM pg_stat_all_tables;


对于那些试图评估他们需要哪个Heroku计划并且不能等待heroku的慢行计数器刷新的人来说,这是一个非常实用的答案:

基本上你想在psql中运行\dt,将结果复制到你喜欢的文本编辑器(它看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
 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

),然后运行正则表达式搜索并替换如下:

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

至:

1
SELECT '\1', COUNT(*) FROM \1 UNION/g

这会产生一些与此类似的东西:

1
2
3
4
5
6
7
8
9
10
11
12
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中运行它就完成了。

1
2
3
4
5
6
7
8
9
10
            ?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
[..]


不确定bash中的答案是否可以接受,但FWIW ......

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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


我通常不依赖统计数据,特别是在PostgreSQL中。

1
2
3
4
5
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;
1
2
3
4
5
6
7
8
9
10
11
12
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;


我不记得我收集它的URL。但希望这可以帮助你:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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();来调用它。

希望你发现这很有用。
保罗


简单的两个步骤:
(注意:无需更改任何内容 - 只需复制粘贴)
1.创造功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.运行此查询以获取所有表的行数

1
2
3
4
5
6
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;

以表格方式获取行计数

1
2
3
4
5
6
7
8
9
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;

这对我有用

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


我喜欢DanielVérité的回答。
但是当你不能使用CREATE语句时,你可以使用bash解决方案,或者如果你是windows用户,可以使用一个PowerShell:

1
2
3
4
5
6
7
8
9
# 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"
}