快速发现PostgreSQL中表的行数

时间:2011-10-30 03:58:25

标签: sql postgresql count row

我需要知道表中的行数来计算百分比。如果总计数大于某个预定义常量,我将使用常量值。否则,我将使用实际的行数。

我可以使用SELECT count(*) FROM table。但是如果我的常量值是 500,000 并且我的表中有 5,000,000,000 行,那么计算所有行会浪费很多时间。

一旦超过常数值,是否可以停止计数?

只要它低于给定的限制,我才需要确切的行数。否则,如果计数高于限制,我会使用限制值,并希望尽快得到答案。

这样的事情:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;

7 个答案:

答案 0 :(得分:174)

计算 big 表中的行在PostgreSQL中很慢。要获得精确的数字,由于MVCC的性质,它必须完整计算行数。如果计数必须完全,就像你的情况一样,有一种方法可以显着提高

而不是使用大表获取完全计数():

SELECT count(*) AS exact_count FROM myschema.mytable;

你得到这样的近似估计(极快):

SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';

估算的接近程度取决于您是否运行ANALYZE。它通常非常接近 请参阅PostgreSQL Wiki FAQthe dedicated wiki page for count(*) performance

更好

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;

更快,更简单,更安全,更优雅。请参阅Object Identifier Types上的手册。

在Postgres 9.4+中使用to_regclass('myschema.mytable')以避免无效表名的例外:


Postgres 9.5 + 中的

TABLESAMPLE SYSTEM (n)
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);

@a_horse commented类似,如果SELECT中的统计信息由于某种原因不够充分,pg_class命令的新添加的子句可能会有用。例如:

  • 没有autovacuum正在运行。
  • 在大INSERTDELETE
  • 之后立即
  • TEMPORARY表(autovacuum未涵盖)。

这只会查看随机 n %(示例中为1)块的选择并计算其中的行数。更大的样本会增加成本并减少错误。准确性取决于更多因素:

  • 行大小的分布。如果给定的块碰巧比通常的行保持更宽,则计数低于通常的等等。
  • 每个块有一个死元组或FILLFACTOR占用空间。如果在整个表格中分布不均,估计可能会被取消。
  • 一般舍入错误。

在大多数情况下,pg_class的估算值会更快,更准确。

回答实际问题

  

首先,我需要知道该表中的行数(如果是总数)   count大于某个预定义常量

是否...

  

...当计数通过我的常数值时,它是可能的   停止计数(而不是等到完成计数通知   行数更多)。

是。您可以将{strong>子查询与LIMIT 一起使用:

SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;

Postgres 实际上停止计数超出给定限制,您获得 n 行的精确和当前计数(示例中为500000) ),否则 n 。但不如pg_class中的估算速度快。

答案 1 :(得分:9)

我在postgres应用中执行了一次运行:

EXPLAIN SELECT * FROM foo;

然后用正则表达式或类似逻辑检查输出。对于简单的SELECT *,第一行输出应如下所示:

Seq Scan on uids  (cost=0.00..1.21 rows=8 width=75)

您可以使用rows=(\d+)值作为返回行数的粗略估算值,如果估算值低于您的阈值的1.5倍,则只能使用实际值SELECT COUNT(*) (或您认为对您的申请有意义的任何数字)。

根据查询的复杂程度,此数字可能会变得越来越不准确。事实上,在我的应用程序中,当我们添加连接和复杂条件时,它变得非常不准确,它完全没有价值,甚至知道如何在100的幂内我们返回多少行,所以我们不得不放弃该策略。

但是如果你的查询很简单,Pg可以在一定的合理误差范围内预测它会返回多少行,那么它可能适合你。

答案 2 :(得分:1)

在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)

答案 3 :(得分:1)

您可以通过以下查询获得计数(不带*或任何列名)。

select from table_name;

答案 4 :(得分:0)

文本列有多宽?

使用GROUP BY,您无法避免数据扫描(至少是索引扫描)。

我建议:

  1. 如果可能,请更改架构以删除文本数据的重复。这样,计数将发生在'many'表中的狭窄外键字段上。

  2. 或者,使用文本的HASH创建生成的列,然后使用哈希列GROUP BY。 同样,这是为了减少工作量(扫描窄列索引)

  3. 编辑:

    您的原始问题与您的编辑不完全匹配。我不确定您是否知道COUNT与GROUP BY一起使用时,将返回每组的项目数,而不是整个表中的项目数。

答案 5 :(得分:0)

Reference taken from this Blog.

您可以使用以下查询来查找行数。

使用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;

答案 6 :(得分:-1)

对于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是有序表(聚簇索引)

更快(但不可靠)的方法详细here.