【问题标题】:How do you find the row count for all your tables in Postgres您如何在 Postgres 中找到所有表的行数
【发布时间】:2011-02-05 12:03:45
【问题描述】:

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

SELECT count(*) FROM table_name;

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

【问题讨论】:

    标签: postgresql count database-table


    【解决方案1】:

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

    如果你想要一个真实的计数,你必须像对每个表使用的那样执行 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 内部使用来做出决定。对于基本的计数目的,只是为了查看一般情况下有多大,两者都应该足够准确。

    【讨论】:

    • 为了完成,请在第一个选项中添加这个(感谢@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;
    • @Greg Smith 哪个版本引入了n_live_tup?我的 Redshift 数据库缺少该列。它是 Postgres 8.0.2 的衍生版本。
    • “第二种方法”查询(使用pg_stat_user_tables)为我返回了n_live_tup 中的大部分零,因为ANALYZE 从未运行过。我没有在每个模式/表上运行ANALYZE 并永远等待答案,而是首先使用“第三种方法”检查结果,并且那个(使用pg_class)返回了非常准确的计数。
    • @BrianD,可以使用analyzedb实用程序作为“analyzedb -d dbname”在数据库级别执行分析
    • @estani,谢谢!你的 sql 是这个答案中唯一一个我没有发现明显错误的。例如,Gregs 第二种方法显示所有表为零,第三种方法仅在我恢复转储的新数据库中准确(与原始数据库表中的建议查询输出实际计数不同)
    【解决方案2】:

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

    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() 函数。

    【讨论】:

    • 非常聪明。可惜没有query_to_jsonb()
    • @a_horse_with_no_name,它会在执行时对繁忙且巨大的表产生任何性能问题吗?
    • @Spike:性能问题相比什么?主要的性能瓶颈是在每个表上运行select count(*)
    • @a_horse_with_no_name,通过对一亿条记录执行 x_path 函数。
    • 这给出了一个 TRUE 计数,而接受的答案不是预期的。谢谢!
    【解决方案3】:

    要获取估算值,请参阅Greg Smith's answer

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

    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 还是使用带有 %I 格式字符串的更现代的 format() 函数。否则一些恶意的人可能会将他们的表命名为tablename;DROP TABLE other_table,这作为表名是完全有效的。

    • 即使没有 SQL 注入和有趣的字符问题,表名也可能存在大小写不同的变体。如果一个表被命名为ABCD 和另一个abcdSELECT 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...) 是更好的选择。

    【讨论】:

      【解决方案4】:

      如果您不介意可能过时的数据,您可以access the same statistics used by the query optimizer

      类似:

      SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;
      

      【讨论】:

      • @mlissner:如果您的自动清理间隔太长或者您没有在桌子上运行手动ANALYZE,则统计信息可能会消失。这是一个数据库负载以及如何配置数据库的问题(如果统计信息更新得更频繁,统计信息会更准确,但会降低运行时性能)。最终,获得准确数据的唯一方法是对所有表运行select count(*) from table
      【解决方案5】:

      对于那些试图评估他们需要哪个 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
      [..]
      

      【讨论】:

      • 我喜欢这个主意
      • 在 Atom 中,我必须像这样进行正则表达式搜索和替换:select '$1', count(*) from $1 union/g
      • 另外,帖子说:“您需要删除并集并在末尾添加分号。”这是一个错字。您需要删除/g(保留union)并在最后添加一个分号(;)。不要忘记删除分号前的最后一个union
      • “不要忘记删除分号前的最后一个union”是我的意思:) 添加了“last”这个词来澄清
      • 对于 VSCode,对我有用的是select '$1', count(*) from $1 union
      【解决方案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;
      

      【讨论】:

        【解决方案7】:

        不确定您是否可以接受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
        

        【讨论】:

        • 在本质上,这只是归结为 OP 中相同的 select count(*) from table_name;
        【解决方案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) 或者更好的是它可以变成它自己的函数。
        【解决方案9】:

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

        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(); 应该会得到所有表的行数。

        【讨论】:

        • 最好引用列名(如quote_ident(t_name.relname))以确保正确支持不寻常的名称(例如“列名”)。
        • 之后删除它:DROP FUNCTION count_em_all();
        • 出现错误:选择 count_em_all();错误:“组”第 1 行或附近的语法错误:SELECT COUNT() AS “count” FROM group ^ QUERY:SELECT COUNT() AS “count” FROM group CONTEXT:PL/pgSQL 函数 count_em_all() 第 18 行 FOR over执行语句
        • 太棒了!选择和排序 - SELECT * FROM count_em_all() as r ORDER BY r.num_rows DESC;
        【解决方案10】:

        这对我有用

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

        【讨论】:

        • 这给出了一些有趣的数字,但它不是(总是?)行数。好的,文档说这是估计的:postgresql.org/docs/9.3/…(如何更新这些统计信息?)
        【解决方案11】:

        我做了一个小改动以包括所有表格,也包括非公共表格。

        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”不是已知变量
        【解决方案12】:

        摘自我在 GregSmith 的回答中的评论,使其更具可读性:

        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;
        

        感谢@a_horse_with_no_name

        【讨论】:

          【解决方案13】:

          您可以使用此查询生成所有表名及其计数

          select ' select  '''|| tablename  ||''', count(*) from ' || tablename ||' 
          union' from pg_tables where schemaname='public'; 
          

          上述查询的结果将是

          select  'dim_date', count(*) from dim_date union 
          select  'dim_store', count(*) from dim_store union
          select  'dim_product', count(*) from dim_product union
          select  'dim_employee', count(*) from dim_employee union
          

          您需要删除最后一个并集并在末尾添加分号!!

          select  'dim_date', count(*) from dim_date union 
          select  'dim_store', count(*) from dim_store union
          select  'dim_product', count(*) from dim_product union
          select  'dim_employee', count(*) from dim_employee  **;**
          

          运行!!!

          【讨论】:

            【解决方案14】:

            这是一个更简单的方法。

            tables="$(echo '\dt' | psql -U "${PGUSER}" | tail -n +4 | head -n-2 | tr -d ' ' | cut -d '|' -f2)"
            for table in $tables; do
            printf "%s: %s\n" "$table" "$(echo "SELECT COUNT(*) FROM $table;" | psql -U "${PGUSER}" | tail -n +3 | head -n-2 | tr -d ' ')"
            done
            

            输出应该是这样的

            auth_group: 0
            auth_group_permissions: 0
            auth_permission: 36
            auth_user: 2
            auth_user_groups: 0
            auth_user_user_permissions: 0
            authtoken_token: 2
            django_admin_log: 0
            django_content_type: 9
            django_migrations: 22
            django_session: 0
            mydata_table1: 9011
            mydata_table2: 3499
            

            您可以根据需要更新psql -U "${PGUSER}" 部分以访问您的数据库

            请注意,head -n-2 语法在 macOS 中可能不起作用,您可能只使用 a different implementation 那里

            在 CentOS 7 下的 psql (PostgreSQL) 11.2 上测试


            如果你想按表格排序,那么只需用sort包装它

            for table in $tables; do
            printf "%s: %s\n" "$table" "$(echo "SELECT COUNT(*) FROM $table;" | psql -U "${PGUSER}" | tail -n +3 | head -n-2 | tr -d ' ')"
            done | sort -k 2,2nr
            

            输出;

            mydata_table1: 9011
            mydata_table2: 3499
            auth_permission: 36
            django_migrations: 22
            django_content_type: 9
            authtoken_token: 2
            auth_user: 2
            auth_group: 0
            auth_group_permissions: 0
            auth_user_groups: 0
            auth_user_user_permissions: 0
            django_admin_log: 0
            django_session: 0
            

            【讨论】:

              【解决方案15】:

              我喜欢 Daniel Vérité 的 answer。 但是,当您不能使用 CREATE 语句时,您可以使用 bash solution,或者,如果您是 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"
              }
              

              【讨论】:

                【解决方案16】:

                我想要所有表格的总数 + 表格列表及其计数。有点像花费最多时间的性能图表

                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 中使用的查询

                【讨论】:

                  【解决方案17】:

                  如果您在psql shell 中,使用\gexec 允许您执行syed's answerAur's answer 中描述的语法,而无需在外部文本编辑器中进行手动编辑。

                  with x (y) as (
                      select
                          'select count(*), '''||
                          tablename||
                          ''' as "tablename" from '||
                          tablename||' '
                      from pg_tables
                      where schemaname='public'
                  )
                  select
                      string_agg(y,' union all '||chr(10)) || ' order by tablename'
                  from x \gexec
                  

                  注意,string_agg() 既用于分隔语句之间的union all,也用于将分隔的数据行压缩成一个单元以传递到缓冲区。

                  \gexec

                  将当前查询缓冲区发送到服务器,然后将查询输出的每一行的每一列(如果有)视为要执行的 SQL 语句。

                  【讨论】:

                    【解决方案18】:

                    下面的查询将为我们提供每个表的行数和大小

                    选择表模式,表名, pg_relation_size('"'||table_schema||'"."'||table_name||'"')/1024/1024 size_MB, (xpath('/row/c/text()', query_to_xml(format('select count(*) AS c from %I.%I', table_schema, table_name), 假,真,'')))[1]::text::int AS rows_n 来自 information_schema.tables 按 size_MB desc 排序;

                    【讨论】:

                      【解决方案19】:

                      你可以select from

                      select from table_name;

                      【讨论】:

                        猜你喜欢
                        • 2021-09-05
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2010-09-19
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-02-05
                        • 2011-07-05
                        相关资源
                        最近更新 更多