快速发现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运行。
  • 紧接在一个大的INSERTDELETE
  • 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议:

  1. 如果可能,更改模式以删除文本数据的重复。 这样计数将发生在“多”表中的一个狭窄的外键字段上。

  2. 或者,创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;