3 回答
TA贡献1820条经验 获得超10个赞
在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。
或用于count(*)性能的专用Wiki页面。
更好了
PostgreSQL的维基文章的是一个有点草率。它忽略了一个数据库中可能存在多个具有相同名称的表的可能性-处于不同的模式。要说明这一点:
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;
更快,更简单,更安全,更优雅。请参阅《对象标识符类型》手册。
to_regclass('myschema.mytable')在Postgres 9.4+中使用可避免无效表名的例外情况:
如何检查给定架构中是否存在表
TABLESAMPLE SYSTEM (n) 在Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
就像@a_horse commented一样,SELECT如果pg_class由于某些原因当前的统计信息不足,则为该命令新添加的子句可能会很有用。例如:
没有autovacuum运行。
大INSERT或DELETE。
TEMPORARY表格(未被涵盖autovacuum)。
这只会查看随机的n%(1在示例中)选择的块并计算其中的行。您可以选择更大的样本来增加成本,并减少误差。准确性取决于更多因素:
行大小分布。如果给定的块恰好比平常的行宽,则计数比平常的低,等等。
死元组或FILLFACTOR每个块占用空间。如果整个表分布不均,则估计值可能会不正确。
一般舍入错误。
在大多数情况下,来自的估计pg_class会更快,更准确。
回答实际问题
首先,我需要知道该表中的行数,如果总计数大于某个预定义常量,
以及是否...
...在计数超过我的常数值时是可能的,它将停止计数(而不是等待完成计数以告知行计数更大)。
是。您可以将子查询与结合使用LIMIT:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres 实际上停止计数超过给定的限制,您将获得多达n行(在本示例中为500000)的精确和当前计数,否则为n。但是,速度不及中的估算速度。pg_class
TA贡献1850条经验 获得超11个赞
我通过运行在postgres应用程序中执行了一次:
EXPLAIN SELECT * FROM foo;
然后使用正则表达式或类似的逻辑检查输出。对于简单的SELECT *,输出的第一行应如下所示:
Seq Scan on uids (cost=0.00..1.21 rows=8 width=75)
您可以将该rows=(\d+)值用作将要返回的行数的粗略估计,然后仅SELECT COUNT(*)在估计值小于阈值的1.5倍(或您认为对应用程序有意义的任何数目)时才进行实际计算。
根据查询的复杂程度,此数字可能会越来越少。实际上,在我的应用程序中,当我们添加联接和复杂条件时,它变得如此不准确,以至于完全不值钱,甚至不知道在100的幂内将返回多少行,因此我们不得不放弃该策略。
但是,如果您的查询足够简单,Pg可以在合理的误差范围内预测它将返回多少行,那么它可能对您有用。
TA贡献1799条经验 获得超8个赞
在Oracle中,您可以rownum用来限制返回的行数。我猜想其他SQL中也存在类似的构造。因此,对于您给出的示例,可以将返回的行数限制为500001,count(*)然后应用a :
SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)
- 3 回答
- 0 关注
- 2132 浏览
添加回答
举报