Самый быстрый способ подсчета точного количества строк в очень большой таблице?

Я столкнулся с статьями, в которых указано, что SELECT COUNT(*) FROM TABLE_NAME будет медленным, если в таблице много строк и много столбцов.

У меня есть таблица, которая может содержать даже миллиарды строк [она имеет приблизительно 15 столбцов]. Есть ли лучший способ подсчета количества EXACT количества строк таблицы?

Примите к сведению следующее:

  • Я ищу поставщика базы данных независимое решение. Это нормально, если это охватывает MySQL, Oracle, MS SQL Server. Но если есть действительно нет базы данных независимое от поставщика решение, то я будут соглашаться на различные решения для разных поставщиков баз данных.

  • Я не могу использовать какой-либо другой внешний инструмент сделать это. Я в основном ищу SQL-решение.

  • Я не могу нормализовать дизайн своей базы данных в дальнейшем. Это уже в 3NF, и, кроме того, много кода уже написано вокруг него.

Ответ 1

Простой ответ:

  • Решение, не зависящее от поставщика базы данных = используйте стандарт = COUNT(*)
  • Существуют приблизительные решения SQL Server, но не используйте COUNT (*) = вне области

Заметки:

COUNT (1) = COUNT (*) = COUNT (PrimaryKey) на всякий случай

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

Пример SQL Server (1,4 миллиарда строк, 12 столбцов)

SELECT COUNT(*) FROM MyBigtable WITH (NOLOCK)
-- NOLOCK here is for me only to let me test for this answer: no more, no less

1 пробежка, 5:46 минут, счет = 1 401 659 700

--Note, sp_spaceused uses this DMV
SELECT
   Total_Rows= SUM(st.row_count)
FROM
   sys.dm_db_partition_stats st
WHERE
    object_name(object_id) = 'MyBigtable' AND (index_id < 2)

2 прогона, оба менее 1 секунды, количество = 1 401 659 670

Второй имеет меньше строк = неправильно. Будет одинаковым или более в зависимости от записи (удаление выполняется в нерабочее время)

Ответ 2

Самый быстрый способ MySQL - это:

SHOW TABLE STATUS;

Вы сразу получите все свои таблицы с подсчетом строк (который является итогом) вместе с большим количеством дополнительной информации, если вы хотите.

Ответ 3

Я столкнулся с статьями, в которых указано, что SELECT COUNT (*) FROM TABLE_NAME будет медленным, если в таблице много строк и много столбцов.

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

Обратите внимание, что вы обычно можете извлечь хорошую оценку с помощью инструментов оптимизации запросов, статистики таблиц и т.д. Например, в PostgreSQL можно проанализировать вывод explain count(*) from yourtable и получить достаточно хорошую оценку числа строк. Это подводит меня к вашему второму вопросу.

У меня есть таблица, которая может содержать даже миллиарды строк [она имеет приблизительно 15 столбцов]. Есть ли лучший способ получить ТОЧНОЕ количество строк в таблице?

Серьезно?:-) Вы действительно имеете в виду точное число из таблицы с миллиардами строк? Вы действительно уверены?: -)

Если вы действительно это делаете, вы можете сохранить след общего использования триггеров, но ум concurrency и взаимоблокировки, если вы это сделаете.

Ответ 4

Вы можете попробовать sp_spaceused (Transact-SQL)

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

Ответ 5

Есть ли лучший способ получить ТОЧНОЕ количество строк в таблице?

Чтобы ответить на ваш вопрос просто, Нет.

Если вам нужен независимый способ СУБД, это самый быстрый способ:

SELECT COUNT(*) FROM TableName

Некоторые поставщики СУБД могут иметь более быстрые способы, которые будут работать только для их систем. Некоторые из этих параметров уже размещены в других ответах.

COUNT(*) должен быть оптимизирован СУБД (по крайней мере, любой PROD достойный DB), так что не пытайтесь обойти их оптимизацию.

На боковой ноте:
Я уверен, что многие из ваших других запросов также занимают много времени, чтобы закончить из-за вашего размера таблицы. Любые проблемы с производительностью, вероятно, должны быть решены, если подумать о своем дизайне схемы с учетом скорости. Я понимаю, вы сказали, что это не вариант изменения, но может оказаться, что 10 + минута запросов тоже не вариант. 3-й NF не всегда лучший подход, когда вам нужна скорость, а иногда данные могут быть разбиты на несколько таблиц, если записи не имеют и. Что-то подумать...

Ответ 6

Я использую

select /*+ parallel(a) */  count(1) from table_name a;

Ответ 7

Я не так эксперт, как другие, которые ответили, но у меня возникла проблема с процедурой, которую я использовал для выбора случайной строки из таблицы (не слишком релевантной), но мне нужно было знать количество строк в моя справочная таблица для вычисления случайного индекса. Используя традиционные функции Count (*) или Count (1), я иногда получал до 2 секунд для запуска моего запроса. Поэтому вместо этого (для моей таблицы с именем "tbl_HighOrder" ) я использую:

Declare @max int

Select @max = Row_Count
From sys.dm_db_partition_stats
Where Object_Name(Object_Id) = 'tbl_HighOrder'

Он отлично работает, и время запросов в Management Studio равно нулю.

Ответ 8

Хорошо, опаздывает на 5 лет и не уверен, помогает ли это:

Я пытался подсчитать "нет". строк в таблице SQL Server с помощью MS SQL Server Management Studio и столкнулся с некоторой ошибкой переполнения, затем я использовал следующее:

выберите count_big (1) FROM [dbname]. [dbo]. [FactSampleValue];

Результат:

24296650578 строк

Ответ 9

Если версия SQL Server - 2005/2008, вы можете использовать DMV для вычисления количества строк в таблице:

-- Shows all user tables and row counts for the current database 
-- Remove is_ms_shipped = 0 check to include system objects 
-- i.index_id < 2 indicates clustered index (1) or hash table (0) 
SELECT o.name, 
 ddps.row_count 
FROM sys.indexes AS i 
 INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID 
 INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID 
 AND i.index_id = ddps.index_id 
WHERE i.index_id < 2 
 AND o.is_ms_shipped = 0 
ORDER BY o.NAME 

Для ядра базы данных SQL Server 2000 sysindexes будут работать, но настоятельно рекомендуется избегать использования его в будущих выпусках SQL Server, поскольку он может быть удален в ближайшем будущем.

Пример кода, взятого из: Как быстро получить таблицы строк и без изменений

Ответ 10

Я не думаю, что существует общее всегда быстрое решение: некоторые версии RDBMS/версии имеют определенную оптимизацию для SELECT COUNT(*), которые используют более быстрые параметры, в то время как другие просто сканируют таблицу. Вам нужно будет перейти на сайты документации/поддержки для второго набора, для чего, вероятно, потребуется написать более конкретный запрос, обычно тот, который каким-то образом попадает в индекс.

EDIT:

Вот мысль, которая может работать, в зависимости от вашей схемы и распределения данных: есть ли у вас индексный столбец, который ссылается на увеличивающееся значение, числовое увеличение идентификатора, скажем, или даже метку времени или дату? Затем, предполагая, что удаление не происходит, должно быть возможно сохранить счет до некоторого недавнего значения (вчерашняя дата, наивысшее значение идентификатора в некоторой недавней точке выборки) и добавить счет за пределами этого, который должен очень быстро разрешаться в индексе, Конечно, очень зависит от значений и индексов, но применима практически ко всем версиям любой СУБД.

Ответ 11

Я опаздываю на этот вопрос, но вот что вы можете сделать с MySQL (поскольку я использую MySQL). Я делюсь своими наблюдениями здесь:

1) SELECT COUNT(*) AS TOTAL_ROWS FROM <TABLE_NAME>

Результат
Количество строк: 508534
Выход консоли: Затронутые строки: 0 Найдено строк: 1 Предупреждения: 0 Длительность для 1 запроса: 0,125 секунды.
Занимает некоторое время для таблицы с большим количеством строк, но количество строк очень точное.

2) SHOW TABLE STATUS or SHOW TABLE STATUS WHERE NAME="<TABLE_NAME>"

Результат
Количество строк: 511235
Консоль: Затронутые строки: 0 Найдено строк: 1 Предупреждения: 0 Длительность для 1 запроса: 0.250 с Описание: Количество строк не является точным.

3) SELECT * FROM information_schema.tables WHERE table_schema = DATABASE();

Результат
Количество строк: 507806
Выход консоли: Затронутые строки: 0 Найдено строк: 48 Предупреждения: 0 Длительность для 1 запроса: 1.701 сек.
Количество строк не является точным.

Я не эксперт по MySQL или базе данных, но я обнаружил, что для очень больших таблиц вы можете использовать опцию 2 или 3 и получить "справедливую идею" о том, сколько строк присутствует.

Мне нужно было получить эти подсчеты строк для отображения некоторой статистики в пользовательском интерфейсе. С вышеперечисленными запросами я знал, что полные строки составляют более 500 000, поэтому я придумал статистику, например "Более 500 000 строк", не показывая точное количество строк.

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

Ответ 12

Я получил этот скрипт из другого вопроса/ответа StackOverflow:

SELECT SUM(p.rows) FROM sys.partitions AS p
  INNER JOIN sys.tables AS t
  ON p.[object_id] = t.[object_id]
  INNER JOIN sys.schemas AS s
  ON s.[schema_id] = t.[schema_id]
  WHERE t.name = N'YourTableNameHere'
  AND s.name = N'dbo'
  AND p.index_id IN (0,1);

В моей таблице 500 миллионов записей, а приведенное выше возвращается менее чем за 1 мс. В то же время,

SELECT COUNT(id) FROM MyTable

занимает целых 39 минут, 52 секунды!

Они дают одинаковое количество строк (в моем случае, точно 519326012).

Я не знаю, будет ли это всегда так.

Ответ 13

Не совсем одно решение DBMS-agnostic, но по крайней мере ваш код клиента не увидит разницу...

Создайте еще одну таблицу T только с одним строком и одним целым полем N 1 и создайте INSERT TRIGGER, который просто выполняет:

UPDATE T SET N = N + 1

Также создайте DELETE TRIGGER, который выполняет:

UPDATE T SET N = N - 1

СУБД, достойная его соли, гарантирует атомарность операций над 2 а N будет содержать точное количество строк во все времена, что затем супер-быстро, чтобы получить просто:

SELECT N FROM T

В то время как триггеры специфичны для СУБД, выбор из T не выполняется, и ваш клиентский код не нужно изменять для каждой поддерживаемой СУБД.

Однако это может иметь некоторые проблемы с масштабируемостью, если таблица INSERT или DELETE-интенсивна, особенно если вы не COMMIT сразу после INSERT/DELETE.


1 Эти имена являются просто заполнителями - используйте что-то более значимое в производстве.

2 I.e. N не может быть изменено параллельной транзакцией между чтением и записью в N, если оба чтения и записи выполняются в одном выражении SQL.

Ответ 14

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

По большей части вы должны разбить запросы на ведомые устройства на основе лучшего ключа (или основного ключа, которого я предполагаю) таким образом (мы будем использовать 250000000 в качестве наших строк/подчиненных):

-- First slave
SELECT COUNT(pk) FROM t WHERE pk < 250000000
-- Ith slave where 2 <= I <= N - 1
SELECT COUNT(pk) FROM t WHERE pk >= I*250000000 and pk < (I+1)*250000000
-- Last slave
SELECT COUNT(pk) FROM t WHERE pk > (N-1)*250000000

Но вам нужен только SQL. Какой бюст. Хорошо, позвольте сказать, что вы садомазохист. На главном (или ближайшем подчиненном) вам, скорее всего, потребуется создать таблицу для этого:

CREATE TABLE counter_table (minpk integer, maxpk integer, cnt integer, slaveid integer)

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

INSERT INTO counter_table VALUES (I*25000000, (I+1)*250000000, (SELECT COUNT(pk) FROM ... ), @@SLAVE_ID)

У вас могут возникнуть проблемы с записью ведомых в таблицу на master. Вам может потребоваться еще больше садиса - я имею в виду, творческий:

-- A table per slave!
INSERT INTO counter_table_slave_I VALUES (...)

В конце концов вы должны иметь ведомое устройство, которое существует последним в пути, пройденном графом репликации, относительно первого ведомого. Этот раб должен теперь иметь все другие значения счетчика и должен иметь свои собственные значения. Но к тому времени, когда вы закончите, там, вероятно, добавлены строки, поэтому вам придется вставить еще один, компенсирующий записанный max pk в вашей counter_table и текущий max pk.

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

Если вы находитесь в ситуации, когда у вас есть отдельные таблицы в ведомых устройствах, вы можете UNION получить все нужные строки.

SELECT SUM(cnt) FROM (
    SELECT * FROM counter_table_slave_1
      UNION
    SELECT * FROM counter_table_slave_2
      UNION
    ...
  )

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

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

Но если у вас хорошая репликация, то ваши выигрыши в скорости должны быть напрямую связаны с числом или ведомыми. Фактически, если вам нужно всего 10 минут, чтобы запустить счетный запрос, и у вас есть 8 подчиненных, вы сократите время до нескольких минут. Может быть, час, чтобы сгладить детали этого решения.

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

На самом деле это кажется невозможным, так как вы в основном застряли в решении SQL-only, и я не думаю, что вам предоставлен механизм для запуска тайного и заблокированного запроса через несколько подчиненных. Может быть, если бы вы контролировали файл журнала репликации... что означает, что вы буквально разворачиваете ведомые для этой цели, что, без сомнения, медленнее, чем просто запуск запроса на подсчет на одной машине.

Итак, мои две копейки в 2013 году.

Ответ 15

Если триггер вставки слишком дорог для использования, но может быть предоставлен триггер удаления, и есть автоматическое приращение id, затем после подсчета всей таблицы один раз и помня счетчик как last-count и last-counted-id,

а затем каждый день нужно просто подсчитать id > last-counted-id, добавить это в last-count и сохранить новый last-counted-id.

Триггер удаления уменьшит счетчик последнего времени, если идентификатор удаленной записи <= last-counted-id.

Ответ 16

Я нашел эту хорошую статью SQL Server - HOW-TO: быстро получить точное количество строк для таблицы из martijnh1 которая дает хорошее резюме для каждого сценария.

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

А пока вот подробности из статьи:

Способ 1:

Запрос:

SELECT COUNT(*) FROM Transactions 

Комментарии:

Выполняет полное сканирование таблицы. Медленно на больших столах.

Способ 2:

Запрос:

SELECT CONVERT(bigint, rows) 
FROM sysindexes 
WHERE id = OBJECT_ID('Transactions') 
AND indid < 2 

Комментарии:

Быстрый способ получить количество строк. Зависит от статистики и неточно.

Запустите DBCC UPDATEUSAGE (База данных) с COUNT_ROWS, что может занять значительное время для больших таблиц.

Способ 3:

Запрос:

SELECT CAST(p.rows AS float) 
FROM sys.tables AS tbl 
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and
idx.index_id < 2 
INNER JOIN sys.partitions AS p ON p.object_id=CAST(tbl.object_id AS int) 
AND p.index_id=idx.index_id 
WHERE ((tbl.name=N'Transactions' 
AND SCHEMA_NAME(tbl.schema_id)='dbo')) 

Комментарии:

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

Способ 4:

Запрос:

SELECT SUM (row_count) 
FROM sys.dm_db_partition_stats 
WHERE object_id=OBJECT_ID('Transactions')    
AND (index_id=0 or index_id=1); 

Комментарии:

Быстрая (хотя и не такая быстрая, как метод 2) операция и, что не менее важно, надежная

Ответ 17

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

SELECT TOP(1) <primarykeyfield> FROM <table> ORDER BY <primarykeyfield> DESC;

Я работаю с таблицами MS SQL, содержащими миллиарды строк, для которых требуется время ответа второй секунды, включая количество записей. Аналогичный SELECT COUNT (*) потребует минут для обработки путем сравнения.

Ответ 18

Для сервера Sql попробуйте это

SELECT T.name, 
       I.rows AS [ROWCOUNT] 
FROM   sys.tables AS T 
       INNER JOIN sys.sysindexes AS I 
               ON T.object_id = I.id AND I.indid < 2 
WHERE T.name = 'Your_Table_Name'
ORDER  BY I.rows DESC 

Ответ 19

Если вы используете Oracle, как насчет этого (при условии, что статистика таблицы обновляется):

select <TABLE_NAME>, num_rows, last_analyzed from user_tables

last_analyzed покажет время, когда последние были собраны.

Ответ 20

выберите строки из sysindexes где id = Object_ID ('TableName') и indid < 2

Ответ 21

Поместите индекс в некоторый столбец. Это должно позволить оптимизатору выполнить полное сканирование индексных блоков вместо полного сканирования таблицы. Это сократит затраты на IO. Посмотрите план выполнения до и после. Затем измерьте настенные часы в обоих направлениях.

Ответ 22

С PostgreSQL:

SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = 'table_name'

Ответ 23

В SQL Server 2016 я могу просто проверить свойства таблицы и затем выбрать вкладку "Хранилище" - это дает мне количество строк, дисковое пространство, используемое таблицей, индексное пространство и т.д.

Ответ 24

Если у вас есть первичный ключ (уникальное значение) где-то в вашей таблице, вы можете использовать MAX(yourId) чтобы по существу дать вам общее количество строк. Ниже приведен образец фрагмента:

SELECT MAX(yourId)
FROM YourTable

Ответ 25

Возможно, немного поздно, но это может помочь другим пользователям MSSQL

; WITH RecordCount AS (SELECT ROW_NUMBER() OVER (ORDER BY COLUMN_NAME) AS [RowNumber] FROM TABLE_NAME) SELECT MAX (RowNumber) FROM RecordCount