如何在Postgres中查找所有表的行数


395

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

SELECT count(*) FROM table_name;

但我想查看所有表的行数,然后按顺序进行排序,以了解所有表的大小。

Answers:


582

可以通过三种方法进行这种计数,每种方法都有自己的权衡。

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

第二种方法指出,统计信息收集器随时可以大致跟踪“活动”(未被删除或以后的更新不会删除)多少行。在繁忙的活动中,此值可能会略有下降,但通常是一个不错的估计:

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

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

第三种方式是要注意,系统ANALYZE命令(它在PostgreSQL 8.3以后由autovacuum进程定期执行,以更新表统计信息)也计算行估计。您可以像这样抓住那个:

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的派生产品。
伊恩·塞缪尔·麦克莱恩

1
“第二种方法”查询(使用pg_stat_user_tablesn_live_tup为我返回的大部分为零,因为ANALYZE从未运行过。我没有ANALYZE在每个架构/表上运行并永远等待答案,我首先使用“第三种方法”检查了结果,然后使用(第三种方法pg_class)返回了非常准确的计数。
Brian D

@BrianD,可以使用
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

@a_horse_with_no_name,执行时会在繁忙的大型表上产生任何性能问题吗?

@Spike:相比性能问题是什么?主要的性能瓶颈正在select count(*)每个表上运行。
a_horse_with_no_name

@a_horse_with_no_name,通过对一亿条记录执行x_path函数。
秒杀

@Spike:该xpath()函数仅应用于单行 - count(*)
a_horse_with_no_name

24

要获取估计值,请参阅格雷格·史密斯的答案

为了获得准确的计数,到目前为止,其他答案都存在一些问题,其中一些问题很严重(请参阅下文)。这是一个更好的版本:

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和另外一个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亿行的表(但是对它们运行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 2012年

17

对于试图评估所需的Heroku计划而又等不及要等待heroku的慢行计数器刷新的人们来说,这是一种实用的实用答案:

基本上你想运行\dtpsql,将结果复制到您喜爱的文本编辑器(它看起来就像这样:

 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

在凌动,我只好正则表达式搜索和替换这样的: select '$1', count(*) from $1 union/g

另外,该帖子还说:“您需要删除并集并在末尾添加分号。” 这是一个错字。您需要删除/g(保留union)并;在最后添加一个分号()。不要忘记删除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

我不记得我收集的网址。但是希望这对您有帮助:

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

之后将其删除:DROP FUNCTION count_em_all();
Aalex Gabi

遇到错误:选择count_em_all(); 错误:“组”处或附近的语法错误线1:来自组^的COUNT()AS“计数” QUERY:来自组的“ COUNT”的SELECT COUNT()AS上下文:PL / pgSQL函数count_em_all()第18行位于FOR以上执行声明
Aalex Gabi

大!要选择和排序- SELECT * FROM count_em_all() as r ORDER BY r.num_rows DESC;
Ken4scholars

6

简单的两个步骤:(
注意:无需更改任何内容-只需复制粘贴)
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

2

这对我有用

从pg_stat_user_tables中选择Schemaname,relname,n_live_tup ORDER BY n_live_tup DESC;


1

我喜欢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"
}

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既罪犯,也罪犯。

需要注意的一件事是,在批量导入后,您需要将其放置一会儿。我通过使用真实的导入数据将5000个行添加到跨多个表的数据库中进行了测试。它显示了大约一分钟的1800条记录(可能是可配置的窗口)

这是基于https://stackoverflow.com/a/2611745/1548557的工作,因此感谢您并感谢您在CTE中使用该查询

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.