Подсчет строк в таблице SQL в O(1)

Я понимаю, что лучший способ подсчитать количество строк в таблице SQL-это count (*) (или эквивалентно count (PrimaryKey)).

  1. Это O (1)?
  2. Если нет, то почему?

Почему бы просто не реализовать счетчик и не вернуть его для этого конкретного запроса? Это потому, что этот запрос не является обычным случаем использования?

Если ответы варьируются в зависимости от SQL engine, я хотел бы услышать различия , но в любом случае меня интересует фактическое внедрение в производство SQL-движков.

11 ответов


на некоторые RDBM Это O (1) (Прежде всего MySQL), поместите AFAIK, на который обычно хмурятся и считают "уродливым Хак производительности". Причина в том, что если у вас есть транзакции (которые должен иметь каждый реальный RDBM), общее количество строк в таблице может быть или не быть равно общему числу вы можете видеть из текущей транзакции. Вот почему сервер должен проверить, какие строки фактически видны вашей транзакции, делая ее более O(n) чем O(1).

Если вы хотите оптимизировать процесс получения количества строк и удовлетворены приблизительным результатом, большинство RDBM имеют специальные таблицы "info", которые содержат информацию о таблицах, включая приблизительное количество строк (опять же, это не точно количество строк из-за операции).


нет это не общий случай использования. Большинство подсчетов строк, которые я видел, имеют некоторые предложения where.

основная причина, по которой это не реализовано, хотя счетчик строк будет причиной разногласий в многопользовательской среде. Каждый раз, когда строка была вставлена или удалена, счетчик будет нуждаться в обновлении, эффективно блокируя всю таблицу для каждой вставки/удаления.


производительность COUNT (*) на основе индекса или таблицы действительно зависит от размера сегмента. У вас может быть таблица 1GB, в которой есть только одна строка, но Oracle все равно может сканировать все выделенное пространство. Вставка еще миллиона строк может вообще не повлиять на производительность, если она не изменит отметку прилива. Индексы работают аналогичным образом, когда различные шаблоны удалений могут оставлять разное пространство в структуре индекса и вызывать сканирование индекса чтобы дать лучшую или худшую производительность, чем O(N).

Итак, теоретически это O (N). В практике есть проблемы iplementation, что может привести к быть весьма различным.

например, в некоторых случаях хранилище данных Oracle может дать производительность выше, чем O(N). В частности, оптимизатор может сканировать растровый индекс, а размер растрового индекса слабо связан с размером таблицы, в отличие от индекса b-дерева. Это из-за сжатия методология, которая делает размер индекса зависимым от размера таблицы, количества уникальных значений, распределения значений по всей таблице и исторического шаблона загрузки, я считаю. Так, удвоение числа строк в таблице может увеличить размер индекса на 10%.

при наличии материализованного представления вы также можете получить O (1), прочитав сводную таблицу (триггер-это небезопасный способ сделать это).


в MS SQL server выполнение Count (*) в таблице всегда выполняет сканирование индекса (по первичному ключу) или сканирование таблицы (оба плохих). Для больших таблиц это может занять некоторое время.

вместо этого есть классный трюк, чтобы показать текущее количество записей почти мгновенно (тот же, который Microsoft использует, когда вы щелкаете правой кнопкой мыши по таблице и выбираете свойства):

--SQL 2005 or 2008
select sum (spart.rows)
from sys.partitions spart
where spart.object_id = object_id('YourTable')
and spart.index_id < 2

--SQL 2000
select max(ROWS) from sysindexes
where id = object_id('Resolve_Audit')

это число может быть немного выключено в зависимости от того, как часто SQL обновляет статистику индекса, но если вам нужно и не точное число, они отлично работают.


Это не постоянное время, потому что в транзакционных движках ему нужно проверить, сколько строк существует в текущей транзакции, которая обычно включает в себя сканирование всей таблицы.

оптимизация COUNT (*) без предложения where не является особенно полезной оптимизацией для базы данных за счет других вещей; пользователи больших таблиц редко выполняют такой запрос, и это не помогло бы вообще, если бы присутствовало предложение WHERE.

MyISAM в MySQL делает "чит", сохраняя точное количество строк, но это может сделать только потому, что у него нет MVCC, поэтому не нужно беспокоиться о том, в каких транзакциях находятся строки.


для Oracle это обычно O (N), если результат запроса не находится в кэше, поскольку он по существу должен перебирать все блоки или перебирать индекс, чтобы подсчитать их.


обычно это O (N).

Если требуется ответ O (1) на такой запрос, вы можете легко выполнить его, используя:

  • индексированное представление, которое подсчитывает количество запросов.
  • сохранить счетчик вручную в другой таблице и обновить количество строк с помощью триггера:

пример:

CREATE TABLE CountingTable ( Count int )

INSERT CountingTable VALUES(0)

CREATE TRIGGER Counter ON Table FOR INSERT, UPDATE, DELETE AS
BEGIN
   DECLARE @added int, @Removed int
   SET @added = SELECT COUNT(*) FROM inserted
   SET @removed = SELECT COUNT(*) FROM deleted
   UPDATE CountingTable SET Count = Count + @added - @removed
END

в SQL Server Есть (неточный) ярлык, где вы можете посмотреть количество в системе метаданных.разделы для определенного объекта, например индекса таблицы.

операция O (1), но является только оценкой.


база данных может хранить количество строк в таблице и отвечать за O(1) в select count(*) From MyTable

но, действительно, что это даст им? Любые отклонения от этого (скажем select count(*) from MyTable where Category = 5) потребует полного сканирования таблицы(или сканирования индекса) и будет O (N).


С Informix, при отсутствии осложняющих факторов, таких как LBAC (контроль доступа на основе меток), то SELECT COUNT(*) FROM SomeTable является O (1); он извлекает информацию из управляющей информации, которую он поддерживает. Если есть предложение WHERE или LBAC или таблица-это представление или любой из ряда других факторов, то оно перестает быть O(1).


видимо O (N) на PostgreSQL:

=> explain select count(*) from tests;
                         QUERY PLAN                              
---------------------------------------------------------------------
Aggregate  (cost=37457.88..37457.89 rows=1 width=0)
  ->  Seq Scan on tests  (cost=0.00..33598.30 rows=1543830 width=0)
(2 rows)

(Seq Scan означает, что он должен сканировать всю таблицу)