Как ускорить подсчет строк в таблице PostgreSQL?

Нам нужно подсчитать количество строк в таблице PostgreSQL. В нашем случае не нужно выполнять никаких условий, и было бы вполне приемлемо получить оценку строки, если бы это значительно повысило скорость запроса.

По сути, мы хотим, чтобы select count(id) from <table> выполнялось как можно быстрее, даже если это подразумевает получение неточных результатов.


person Juan Carlos Coto    schedule 28.01.2013    source источник
comment
wiki.postgresql.org/wiki/Slow_Counting   -  person Pavel Stehule    schedule 29.01.2013
comment
@PavelStehule спасибо за эту ссылку, я не знал, что изменения в PG9.2 улучшили производительность для count()   -  person thaJeztah    schedule 29.01.2013
comment
Возможный дубликат Быстрый способ узнать количество строк таблица   -  person Kristján    schedule 28.10.2015


Ответы (6)


Для очень быстрой оценки:

SELECT reltuples FROM pg_class WHERE relname = 'my_table';

Однако есть несколько предостережений. Во-первых, relname не обязательно уникален в pg_class. Может быть несколько таблиц с одним и тем же relname в нескольких схемах базы данных. Чтобы быть однозначным:

SELECT reltuples::bigint FROM pg_class WHERE oid = 'my_schema.my_table'::regclass;

Если вы не уточняете имя таблицы по схеме, приведение к regclass наблюдает текущую search_path, чтобы выбрать наилучшее соответствие. И если таблица не существует (или не видна) ни в одной из схем в search_path, вы получите сообщение об ошибке. См. Типы идентификаторов объектов в руководстве. .

Приведение к bigint прекрасно форматирует число real, особенно для больших чисел.

Кроме того, reltuples может быть более или менее устаревшим. Есть способы компенсировать это в некоторой степени. См. этот более поздний ответ с новыми и улучшенными параметрами:

И запрос на pg_stat_user_tables во много раз медленнее (хотя все же намного быстрее полного подсчета), так как это просмотр пары таблиц.

person Erwin Brandstetter    schedule 28.01.2013
comment
Хороший +1. Любая идея о том, как мы можем ускорить подсчет строк в postgres с поисковым запросом? - person varunvlalan; 19.02.2015
comment
@varunvlalan: Пожалуйста, задавайте вопрос как вопрос (с необходимыми подробностями, чтобы было понятно). Комментарии не к месту. Вы всегда можете сослаться на это для контекста. - person Erwin Brandstetter; 19.02.2015
comment
Этот запрос дает оценку. Если я хочу построить отчет, в котором мне нужно точное количество записей, которые можно запустить в таблице из нескольких сотен тысяч записей? видел, что postgre занимает до 13 секунд для подсчета 20000 записей!! Я могу в это поверить - person Giox; 01.04.2016
comment
@Giox: я тоже не могу в это поверить. 13 с для 20 000 строк — это намного больше нормы, что указывает на проблему в вашей БД. Не должно занимать более пары мс. Пожалуйста, задайте вопрос, предоставив необходимую информацию. Вы всегда можете сослаться на это для контекста. Комментарии не к месту. Вы можете оставить короткий комментарий здесь (или, может быть, здесь?), чтобы сослаться на соответствующий вопрос и привлечь мое внимание. - person Erwin Brandstetter; 01.04.2016
comment
Этот запрос дал мне 415K, однако COUNT(*) возвращает 8M. - person Nikolay Kuznetsov; 17.03.2017
comment
@NikolayKuznetsov: Это может произойти сразу после больших записей в таблицу, или если autovacuum не работает, или плохо настроена, или для временных таблиц, которые не покрыты автоочисткой. Запустите ANALYZE tbl; и повторите попытку. Затем найдите причину, по которой таблица не была проанализирована автоматически. Следуйте связанному ответу выше, чтобы узнать больше. - person Erwin Brandstetter; 18.03.2017

Подсчет выполняется медленно для больших таблиц, поэтому вы можете получить точную оценку следующим образом:

SELECT reltuples::bigint AS estimate 
FROM pg_class 
WHERE relname='tableName';

и это очень быстро, результаты не плавающие, но все же близкие оценки.

  • reltuples — это столбец из таблицы pg_class, он содержит данные о «количестве строк в таблице. Это только оценка, используемая планировщиком. )
  • Каталог pg_class каталогизирует таблицы и большую часть всего остального, что имеет столбцы или иным образом похоже на таблицу. Сюда входят индексы (но см. также pg_index), последовательности, представления, составные типы и некоторые виды специальных отношений (вручную).
  • "Почему "SELECT count(*) FROM bigtable;" работает медленно?" : http://wiki.postgresql.org/wiki/FAQ#Why_is_.22SELECT_count.28.2A.29_FROM_bigtable.3B.22_slow.3F
person Ariel Grabijas    schedule 28.01.2013
comment
хорошо, часть ::bigint позволяет избежать переполнения с размером таблиц › 4B или каким-то порогом, спасибо! - person rogerdpack; 17.10.2015
comment
можно ли добавить предложение where, если я захочу? - person Naga; 17.12.2015

Помимо запуска COUNT() для индексированного поля (которым, надеюсь, является «id»), следующим лучшим способом было бы фактически кэшировать количество строк в какой-либо таблице с помощью триггера на INSERT. Естественно, вместо этого вы будете проверять кеш.

Для приближения вы можете попробовать это (от https://wiki.postgresql.org/wiki/Count_estimate):

select reltuples from pg_class where relname='tablename';
person PinnyM    schedule 28.01.2013

Вы можете запросить точное значение счетчика в таблице, просто используя триггер AFTER INSERT OR DELETE Что-то вроде этого

CREATE TABLE  tcounter(id serial primary key,table_schema text, table_name text, count serial);

insert into tcounter(table_schema, table_name,count) select 'my_schema', 'my_table', count(*) from my_schema.my_table;

и использовать триггер

CREATE OR REPLACE FUNCTION ex_count()
RETURNS trigger AS
$BODY$
BEGIN
    IF (TG_OP='INSERT') THEN
      UPDATE tcounter set count = count + 1 where table_schema = TG_TABLE_SCHEMA::TEXT and table_name = TG_TABLE_NAME::TEXT;
    ELSIF  (TG_OP='DELETE') THEN
      UPDATE tcounter set count = count - 1 where table_schema = TG_TABLE_SCHEMA::TEXT and table_name = TG_TABLE_NAME::TEXT;
    END IF;
RETURN NEW;
END$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

CREATE TRIGGER tg_counter  AFTER INSERT OR DELETE
  ON my_schema.my_table  FOR EACH ROW  EXECUTE PROCEDURE ex_count();

И попросите посчитать

select * from tcounter where table_schema =  'my_schema' and table_name = 'my_table'

это означает, что вы выбираете count(*) один раз для инициализации первой записи

person Maryna Krasnova    schedule 29.01.2015

Вы можете получить оценку из системной таблицы "pg_stat_user_tables".

select schemaname, relname, n_live_tup 
from pg_stat_user_tables 
where schemaname = 'your_schema_name'
and relname = 'your_table_name';
person Mike Sherrill 'Cat Recall'    schedule 28.01.2013

Если ваша база данных невелика, вы можете получить оценку всех ваших таблиц, например, предложенную @mike-sherrill-cat-recall. Однако эта команда выведет список всех таблиц.

SELECT schemaname,relname,n_live_tup 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;

Вывод будет примерно таким:

 schemaname |      relname       | n_live_tup
------------+--------------------+------------
 public     | items              |      21806
 public     | tags               |      11213
 public     | sessions           |       3269
 public     | users              |        266
 public     | shops              |        259
 public     | quantities         |         34
 public     | schema_migrations  |         30
 public     | locations          |          8
(8 rows)
person Jack    schedule 18.12.2016