Быстрый способ узнать количество строк таблицы в PostgreSQL - PullRequest
82 голосов
/ 30 октября 2011

Мне нужно знать количество строк в таблице, чтобы вычислить процент.Если общее число больше некоторой предопределенной константы, я буду использовать значение константы.В противном случае я буду использовать фактическое количество строк.

Я могу использовать 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 ]

180 голосов
/ 30 октября 2011

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

Вместо того, чтобы точный счет ( медленный с большими таблицами):

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

Вы получаете близкую оценку как это ( очень быстро ):

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

Насколько близка оценка, зависит от того, достаточно ли вы наберете ANALYZE.Обычно это очень близко.
См. FAQ по вики PostgreSQL .
или выделенную вики-страницу для подсчета (*) производительности .

Еще лучше

Статья в Вики PostgreSQL - это , была немного небрежно .При этом игнорируется возможность того, что в одной базе данных может быть несколько таблиц с одним и тем же именем - в разных схемах.Чтобы объяснить это:

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;

Быстрее, проще, безопаснее, элегантнее.См. Руководство по типам идентификаторов объектов .

Используйте to_regclass('myschema.mytable') в Postgres 9.4+, чтобы избежать исключений для недопустимых имен таблиц:

TABLESAMPLE SYSTEM (n) в Postgres 9,5 +

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

Как и @ a_horse прокомментировал , недавно добавленное предложение для команды SELECT можетбудет полезно, если статистика по pg_class по какой-то причине недостаточно актуальна.Например:

  • Нет autovacuum работает.
  • Сразу после больших INSERT или DELETE.
  • TEMPORARY таблиц (которые не являютсяохватывается autovacuum).

Это только смотрит на случайный n % (1 в примере) выбор блоков и подсчитывает количество строк в нем.Большая выборка увеличивает стоимость и уменьшает ошибку, ваш выбор.Точность зависит от большего количества факторов:

  • Распределение по размерам строк.Если в данном блоке оказывается больше строк, чем обычно, то количество меньше, чем обычно, и т. Д.
  • Мертвые кортежи или FILLFACTOR занимают место на блок.При неравномерном распределении по таблице оценка может быть отключена.
  • Общие ошибки округления.

В большинстве случаев оценка из pg_class будет быстрее и точнее.

Ответ на фактический вопрос

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

И возможно ли это ...

... возможно в тот момент, когда счетчик передаст мое постоянное значение, он остановит подсчет (и не будет ждать окончания подсчета, чтобы сообщить счетчику строкбольше).

Да. Вы можете использовать подзапрос с LIMIT:

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

Postgres на самом делепрекращает считать сверх указанного предела, вы получаете точный и текущий счет для до n строк (500000 в примере) и n в противном случае,Не так быстро, как оценка в pg_class.

10 голосов
/ 30 октября 2011

Я сделал это один раз в приложении postgres, запустив:

EXPLAIN SELECT * FROM foo;

Затем проверил вывод с помощью регулярного выражения или аналогичной логики.Для простого SELECT * первая строка вывода должна выглядеть примерно так:

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

Вы можете использовать значение rows=(\d+) в качестве приблизительной оценки числа строк, которые будут возвращены, только тогдавыполните действительное SELECT COUNT(*), если оценка, скажем, менее чем в 1,5 раза превышает ваш порог (или любое число, которое, по вашему мнению, имеет смысл для вашего приложения).

В зависимости от сложности вашего запроса, это число может статьвсе менее и менее точно.Фактически, в моем приложении, когда мы добавляли объединения и сложные условия, оно стало настолько неточным, что было совершенно бесполезным, даже если бы знать, как при силе в 100, сколько строк мы бы вернули, нам пришлось отказаться от этой стратегии.

Но если ваш запрос достаточно прост, чтобы Pg мог с некоторой разумной погрешностью предсказать, сколько строк он вернет, он может работать для вас.

1 голос
/ 17 августа 2018

Вы можете получить счет по следующему запросу (без * или каких-либо имен столбцов).

select from table_name;
1 голос
/ 30 октября 2011

В 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)
0 голосов
/ 13 февраля 2016

Ссылка взята из этого блога.

Вы можете использовать ниже, чтобы запросить, чтобы найти количество строк.

Использование 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;
0 голосов
/ 29 октября 2014

Для 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 - упорядоченная таблица (кластеризованный индекс)

Более быстрые (но ненадежные) методы подробно описаны здесь .

0 голосов
/ 30 октября 2011

Какова ширина текстового столбца?

С GROUP BY вы мало что можете сделать, чтобы избежать сканирования данных (по крайней мере, сканирования индекса).

Я бы порекомендовал:

  1. Если возможно, измените схему, чтобы удалить дублирование текстовых данных.Таким образом, подсчет будет происходить в узком поле внешнего ключа в таблице «многие».

  2. В качестве альтернативы можно создать сгенерированный столбец с HASH-текстом, а затем GROUP BY хэш-столбец,Опять же, это уменьшает рабочую нагрузку (сканирование по узкому столбцу индекса)

Редактировать:

Ваш исходный вопрос не совсем соответствует вашему редактированию.Я не уверен, известно ли вам, что COUNT при использовании с GROUP BY будет возвращать количество элементов в группе, а не количество элементов во всей таблице.

...