快速发现PostgreSQL中表格的行数
我需要知道表中的行数来计算一个百分比。 如果总数大于某个预定义常数,我将使用常数值。 否则,我将使用实际的行数。
我可以使用SELECT count(*) FROM table
。 但是,如果我的常数值是50 万 ,我的表中有50万行,那么计算所有的行就会浪费很多时间。
一旦我的常数值被超过,是否有可能停止计数?
我只需要确切的行数,只要它低于给定的限制。 否则,如果计数超过限制,我将使用极限值,并希望尽可能快地回答。
像这样的东西:
SELECT text,count(*), percentual_calculus() FROM token GROUP BY text ORDER BY count DESC;
在PostgreSQL中,计算大表中的行的速度已经很慢了。 为了得到一个精确的数字,由于MVCC的性质,它必须做一个完整的行数。 如果计数不必像你的情况那么精确 ,那么有一种方法可以大大加快速度 。
而不是得到确切的数字(大表缓慢 ):
SELECT count(*) AS exact_count FROM myschema.mytable;
你得到这样一个接近的估计( 非常快 ):
SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';
估计值有多接近取决于您是否足够运行ANALYZE
。 它通常非常接近。
查看PostgreSQL Wiki FAQ 。
或者计数(*)性能的专用wiki页面 。
更好
PostgreSQL Wiki中的文章有点草率 。 它忽略了在一个数据库中可能有多个相同名称的表的可能性 – 在不同的模式中。 为了说明这一点:
SELECT c.reltuples::bigint AS estimate FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = 'mytable' AND n.nspname = 'myschema'
还是更好
SELECT reltuples::bigint AS estimate FROM pg_class WHERE oid = 'myschema.mytable'::regclass;
更快,更简单,更安全,更优雅。 请参阅对象标识符types手册。
在Postgres 9.4+中使用to_regclass('myschema.mytable')
来避免无效表名的exception:
- 如何检查给定模式中是否存在表格
在Postgres 9.5+中的TABLESAMPLE SYSTEM (n)
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
就像@a_horse所说的那样 ,如果pg_class
中的统计信息由于某种原因不够用,那么为SELECT
命令新添加的子句可能会很有用。 例如:
- 没有自动
autovacuum
运行。 - 紧接在一个大的
INSERT
或DELETE
。 -
TEMPORARY
表(不包含在autovacuum
)。
这只会看到一个随机的n %(在这个例子中是1
)块的select并对其中的行进行计数。 一个更大的样本增加了成本,减less了错误,你的select。 准确性取决于更多的因素:
- 行大小的分布。 如果一个给定的块恰好比普通的行更宽,则计数比平常低
- 死元组或
FILLFACTOR
占用每块的空间。 如果不均衡地分布在桌子上,估计可能是closures的。 - 一般舍入错误。
在大多数情况下,来自pg_class
的估计将会更快,更准确。
回答实际问题
首先,我需要知道该表中的行数,如果总数大于某个预定义的常数,
不pipe…
…计数通过我的常数值的时候是可能的,它会停止计数(而不是等待完成计数通知行数更大)。
是。 您可以使用带有LIMIT
的子查询 :
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres 实际上停止计数超出给定的限制,你得到一个精确的和当前计数多达n行(例如500000),否则n 。 尽pipe如此,几乎不像pg_class
的估计那么快。
我曾经在postgres应用程序中运行过一次:
EXPLAIN SELECT * FROM foo;
然后用正则expression式或类似的逻辑来检查输出。 对于一个简单的SELECT *,输出的第一行应该是这样的:
Seq Scan on uids (cost=0.00..1.21 rows=8 width=75)
您可以使用rows=(\d+)
作为粗略估计的行数,然后仅在估计值小于阈值的1.5倍的情况下执行实际的SELECT COUNT(*)
无论您认为适合您的应用程序的数量)。
根据查询的复杂程度,这个数字可能变得越来越不准确。 事实上,在我的申请中,由于我们join了连接和复杂的条件,所以变得如此不准确,完全没有价值,甚至不知道在100的权力范围内我们会返回多less行,所以我们不得不放弃这个策略。
但是如果你的查询很简单,那么Pg可以在一定的合理的误差范围内预测它将返回多less行,它可能适用于你。
在Oracle中,可以使用rownum
来限制返回的行数。 我猜测其他SQL也存在类似的结构。 因此,对于您给出的示例,可以将返回的行数限制为500001,然后应用一个count(*)
:
SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)
文本列有多宽?
使用GROUP BY,可以避免数据扫描(至less是索引扫描)。
我build议:
-
如果可能,更改模式以删除文本数据的重复。 这样计数将发生在“多”表中的一个狭窄的外键字段上。
-
或者,创build一个带有文本HASH的生成列,然后GROUP BY哈希列。 再次,这是为了减less工作量(扫描窄列索引)
编辑:
您的原始问题与您的编辑不太匹配。 我不确定是否知道COUNT与GROUP BY一起使用时,将返回每个组的项目数,而不是整个表中的项目数。
对于SQL Server(2005或更高版本),快速可靠的方法是:
SELECT SUM (row_count) FROM sys.dm_db_partition_stats WHERE object_id=OBJECT_ID('MyTableName') AND (index_id=0 or index_id=1);
有关sys.dm_db_partition_stats的详细信息,请参阅MSDN
查询添加(可能)分区表的所有部分的行。
index_id = 0是一个无序表(Heap),而index_id = 1是一个有序表(聚集索引)
甚至更快(但不可靠)的方法在这里详述。
从这个博客引用。
您可以使用下面的查询来查找行数。
使用pg_class:
SELECT reltuples::bigint AS EstimatedCount FROM pg_class WHERE oid = 'public.TableName'::regclass;
使用pg_stat_user_tables:
SELECT schemaname ,relname ,n_live_tup AS EstimatedCount FROM pg_stat_user_tables ORDER BY n_live_tup DESC;