You can easily instruct Postgres to count up to a given LIMIT
- a maximum number of rows, not an elapsed time:
SELECT count(*)
FROM (
SELECT 1 FROM tbl
WHERE something = 'x'
LIMIT 100000 -- stop counting at 100k
) sub;
If count()
takes a very long time, you either have huge tables or some other problems with your setup. Either way, an estimated count be good enough for your purpose:
It is not possible per se to stop counting after a maximum elapsed time. You could partition the count with the above technique and check the elapsed time after every step. But this adds a lot of overhead. Skipping rows with OFFSET
is not that much cheaper than counting them. I don't think I would use it. Just as proof of concept:
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$;
You could wrap this in a plpgsql function and pass parameters. Even make it work for any given table / column with EXECUTE
...
If you have an ID column with few gaps, the technique would make a lot more sense. You could partition by ID with a lot less overhead ...