在时间限制内获取COUNT()结果

时间:2016-06-08 19:44:11

标签: sql postgresql count postgresql-performance

PostgreSQL中是否有办法中止COUNT(*)语句的执行并返回其当前结果?

我想跑:

SELECT COUNT(*) FROM table WHERE something=x;

有些查询几乎没有时间完成,但有些查询需要花费很多时间。我想:

  • 如果声明在时限内完成,则返回final 结果,
  • 否则它会中止执行但返回当前结果。

获得退出状态也是很好的(无论是完成执行还是中止)。

我找到statement_timeout设置,但它不返回任何结果,只是中止。

2 个答案:

答案 0 :(得分:0)

我不相信您会在查询完成之前获得带有计数的结果集,并使其对最终用户(即您)可见。这是ACID数据库基本规则的方式。从启动SELECT命令开始,您要求提供当时行数的快照。

从另一个角度查看问题可能会更好,并通过对查询执行EXPLAIN然后调查结果来查看为什么某些查询需要很长时间。

答案 1 :(得分:0)

您可以轻松指示Postgres计算到给定的LIMIT - 最大行数,而不是已用时间:

SELECT count(*)
FROM  (
   SELECT 1 FROM tbl
   WHERE  something = 'x'
   LIMIT  100000  -- stop counting at 100k
   ) sub;

如果count()需要很长时间,您可能会有巨大的表或其他一些设置问题。无论哪种方式,估计计数都足以满足您的目的:

本身不可能在最长经过时间后停止计数。您可以 使用上述技术对计数进行分区,并检查每个步骤后的经过时间。但这会增加很多的开销。使用OFFSET跳过行并不比计算它们便宜得多。我不认为我会用它。正如概念证明:

DO
$do$
DECLARE
   _partition bigint := 100000;  -- size of count partition
   _timeout   timestamptz := clock_timestamp() + interval '1s';  -- max time allowed
   _round     int := 0;
   _round_ct  bigint;
BEGIN

LOOP
   SELECT count(*)
   FROM (
      SELECT 1 FROM tbl
      WHERE  something = 'x'
      LIMIT  _partition
      OFFSET _partition * _round
      ) sub
   INTO   _round_ct;

   IF _round_ct < _partition THEN
      RAISE NOTICE 'count: %; status: complete', _partition * _round + _round_ct;
      RETURN;
   ELSIF clock_timestamp() > _timeout THEN
      RAISE NOTICE 'count: %; status: timeout', _partition * _round + _round_ct;
      RETURN;
   END IF;

   _round := _round + 1;
END LOOP;

END
$do$;

您可以将其包装在plpgsql函数中并传递参数。甚至使其适用于任何给定的表/列EXECUTE ...

如果您的ID列间隙很小,那么该技术会更有意义。您可以通过ID进行分区,但开销更少......