我正在寻找一种方法来查找我在 Postgres 中所有表的行数。我知道我可以一次做一张桌子:
SELECT count(*) FROM table_name;
但我想查看所有表的行数,然后按此排序以了解我所有的表有多大。
我正在寻找一种方法来查找我在 Postgres 中所有表的行数。我知道我可以一次做一张桌子:
SELECT count(*) FROM table_name;
但我想查看所有表的行数,然后按此排序以了解我所有的表有多大。
有三种方法可以获得这种计数,每种方法都有自己的权衡。
如果你想要一个真实的计数,你必须像对每个表使用的那样执行 SELECT 语句。这是因为 PostgreSQL 将行可见性信息保存在行本身中,而不是其他任何地方,因此任何准确的计数只能与某些事务相关。您正在计算该事务在执行时看到的内容。您可以自动执行此操作以针对数据库中的每个表运行,但您可能不需要那种级别的准确性或想要等待那么长时间。
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 rows_n DESC;
第二种方法指出,统计信息收集器随时大致跟踪有多少行是“活动的”(未删除或被以后的更新淘汰)。在繁重的活动下,这个值可能会有所偏差,但通常是一个很好的估计:
SELECT schemaname,relname,n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
这也可以显示死了多少行,这本身就是一个值得监控的有趣数字。
第三种方法是注意 system 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 内部使用来做出决定。对于基本的计数目的,只是为了看看总体上有多大,两者都应该足够准确。
这是一个不需要函数来获得每个表的准确计数的解决方案:
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 的回答。
为了获得准确的数字,到目前为止,其他答案都受到一些问题的困扰,其中一些问题很严重(见下文)。这是一个希望更好的版本:
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
of 格式会自动执行此操作。
information_schema.tables
列出除表之外的自定义复合类型,即使 table_type 为'BASE TABLE'
(!)。因此,我们不能在 上进行迭代information_schema.tables
,否则我们可能select count(*) from name_of_composite_type
会面临失败的风险。OTOHpg_class where relkind='r'
应该总是可以正常工作。
COUNT() 的类型是bigint
,不是int
。可能存在超过 21.5 亿行的表(不过,对它们运行 count(*) 是个坏主意)。
不需要为函数创建永久类型以返回具有多列的结果集。RETURNS TABLE(definition...)
是更好的选择。
对于那些试图评估他们需要的 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
[..]
如果您不介意潜在的陈旧数据,您可以访问查询优化器使用的相同统计信息。
就像是:
SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;
简单的两个步骤:(
注意:无需更改任何内容 - 只需复制粘贴)
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;
不确定您是否可以接受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
我通常不依赖统计数据,尤其是在 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;
这对我有用
SELECT schemaname,relname,n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC;
我不记得我收集这个的 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();
应该让你所有表的行数。
摘自我在 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
我做了一个小改动来包括所有表,也包括非公共表。
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();
调用它。
希望你觉得这很有用。保罗
您可以使用此查询生成所有表名及其计数
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 **;**
跑 !!!
这是一个更简单的方法。
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 中可能不起作用,您可能只是在那里使用不同的实现
在 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
我喜欢 Daniel Vé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"
}
我想要所有表格的总数+带有计数的表格列表。有点像花费最多时间的性能图表
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 中使用的查询
如果您在psql
shell 中,using\gexec
允许您执行syed 的答案和Aur 的答案中描述的语法,而无需在外部文本编辑器中手动编辑。
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 语句。
下面的查询将为我们提供每个表的行数和大小
select table_schema, table_name, 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), false, true,'')))[1]::text::int AS rows_n from information_schema.tables order by大小_MB 描述;
你可以做select from
select from table_name;