MySQL: самый быстрый способ подсчета количества строк

117

Какой способ подсчета количества строк должен быть быстрее в MySQL?

Это:

SELECT COUNT(*) FROM ... WHERE ...

Или альтернатива:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

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

Franz
источник
1
О, я нашел похожий вопрос ( stackoverflow.com/questions/1855226/… ). Но тогда пользуюсь SELECT 1и нет SELECT *. Есть разница?
Franz
я не знаю, но вполне возможно, что эти два ответа идентичны - оптимизатор запросов mysql может делать то же самое с каждым из них. при этом сказано, что первое менее двусмысленно, чем второе. почему бы вам не написать несколько тестов и не протестировать их?
Джесси Коэн,
Хм, предположим, я пытаюсь улучшить видимость SO в поисковой системе, задавая аналогичный вопрос другими словами;)
Franz
1
Разница заключается в количестве данных, отправляемых на сторону PHP. Чем больше у вас столбцов, тем медленнее будет SELECT * по сравнению с SELECT 1, потому что извлекаются все столбцы, а не только номер 1. Когда вы запускаете mysql_query(), например, весь набор результатов отправляется в PHP из MySQL, независимо от того, что вы делать с этими данными.
toon81
Такой вопрос - отличный способ получить понимание или новые идеи, но в конечном итоге, если у вас действительно есть конкретный сценарий, в котором вам нужно больше скорости, вам придется запускать тесты, чтобы увидеть, что является самым быстрым.
still_dreaming_1

Ответы:

124

Когда вы COUNT(*)учитываете индексы столбцов, это будет лучший результат. Mysql с движком MyISAM фактически хранит количество строк, он не считает все строки каждый раз, когда вы пытаетесь подсчитать все строки. (на основе столбца первичного ключа)

Использование PHP для подсчета строк не очень разумно, потому что вам нужно отправлять данные из mysql в php. Зачем это нужно, если вы можете добиться того же на стороне mysql?

Если COUNT(*) выполняется медленно, вам следует выполнить EXPLAINзапрос и проверить, действительно ли используются индексы и куда их следует добавлять.


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

Решение есть SQL_CALC_FOUND_ROWS. Обычно это используется, когда вы выбираете строки, но вам все равно нужно знать общее количество строк (например, для разбиения на страницы). Когда вы выбираете строки данных, просто добавьте SQL_CALC_FOUND_ROWSключевое слово после SELECT:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

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

SELECT FOUND_ROWS();

FOUND_ROWS() должен вызываться сразу после запроса выбора данных.


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

Мартиньш Бриедис
источник
14
Исправление: MyISAMсохраняет количество строк. Другие механизмы хранения, например InnoDB , не хранят количество строк и каждый раз подсчитывают все строки .
The Scrum Meister
1
Вы знаете, какая из них будет самой быстрой, если вы просто хотите узнать, есть ли строка: SELECT 1 FROM ... LIMIT 1или SELECT COUNT(*) FROM ...?
Franz
1
Вероятно, полезно отметить, что если вам все равно нужны данные и нужен только счетчик для разбивки на страницы и т. Д. более эффективно получать данные, чем подсчитывать строки в вашей программе.
Tyzoid 01
6
Не имеет значения, хранит ли движок счетчики строк. В вопросе четко указано, что есть WHEREпункт.
Álvaro González
1
@Franz SELECT COUNT(*) FROM ...может занять значительное время, в зависимости от того, что нужно сканировать (например, очень большая таблица или индекс из миллионов / миллиардов / триллионов строк). SELECT 1 FROM ... LIMIT 1немедленно возвращается, потому что вы ограничиваете его первой строкой.
jbo5112
59

После разговора с моими товарищами по команде Рикардо сказал нам, что более быстрый способ:

show table status like '<TABLE NAME>' \G

Но нужно помнить, что результат может быть неточным.

Вы также можете использовать его из командной строки:

$ mysqlshow --status <DATABASE> <TABLE NAME>

Больше информации: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

И вы можете найти полное обсуждение в mysqlperformanceblog

MagMax
источник
2
Для InnoDB это приближение.
Мартин Турной,
2
Это полезно знать, когда вам нужно приблизительное представление о количестве строк в очень больших таблицах, где count (*) может занять буквально часы!
Марк Хансен
Это спасло меня от выдергивания всех волос. COUNT (*) требовалось много времени, чтобы подсчитать все 33 миллиона с лишним строк в моей базе данных. В любом случае, я только хотел знать, работает ли моя функция параллельного удаления строк или нет. Мне не нужно было точное число.
joemar.ct
1
+1 Использование статуса таблицы вместо «COUNT (*)» должно быть правильным ответом на этот вопрос, так же как и о «самой быстрой», а не «точности».
lepe
2
Использование SHOW TABLE STATUS(или эквивалент SELECTв information_schema) выполняется быстро, но не обрабатывает WHEREпредложения. Это точно для MyISAM, но неточно (иногда с коэффициентом 2) для InnoDB.
Рик Джеймс
29

Отличный вопрос, отличные ответы. Вот быстрый способ повторить результаты, если кто-то читает эту страницу и пропускает эту часть:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");
Дан Хорват
источник
5
mysql_query является устаревшей функцией с PHP 5.5.0.
Омар Тарик
8
А почему бы и нет as count? idсбивает с толку на первый взгляд.
Орхан Алиханов
Не отвечает на вопрос
mentalic
17

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

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

Пример:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |
Лепе
источник
Это дает мне результат. Но результаты count (1) и этого разные. Этот способ всегда дает меньшее число, чем запрос count. Есть предположения?
Ayyappan Sekar
3
Просто на заметку читателям. Этот метод очень быстрый, но он применим только тогда, когда вы можете работать с приблизительным количеством строк, поскольку значение, хранящееся в information_schema, не совпадает с тем, которое возвращает SELECT count(*) FROMв случае использования InnoDB. Если вам нужно строгое значение, имейте в виду, что этот метод дает строгое значение только для таблиц MyISAM. В InnoDB количество строк является грубым приближением.
Бартош Фирин
13

Я всегда понимал, что нижеследующее даст мне самое быстрое время ответа.

SELECT COUNT(1) FROM ... WHERE ...
adarshr
источник
1
Не было бы SELECT 1 FROM ... WHERE ... еще быстрее?
Патрик
3
@patrick - SELECT 1 ...вернет столько строк, сколько WHEREи LIMITзапросит, и все они будут равны «1».
Рик Джеймс
1
show table status like '<TABLE NAME>' Это будет намного быстрее.
deep
@deep - но не актуально, если у вас есть WHEREпункт. И для InnoDB это только оценка.
Рик Джеймс
@RickJames, да, правда!
deep
6

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

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

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

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

Алексей Рашков
источник
Межжала. Работает ли это с наиболее распространенными системами баз данных? MySQL, Postgres, SQLite ...?
Franz
4
На самом деле это часто не быстрее, чем использование COUNT (*). См stackoverflow.com/questions/186588/...
toon81
2
Вы должны быть ОЧЕНЬ осторожны при использовании этой функции. Однажды его безрассудное использование привело к полной остановке всей нашей производственной среды. Это ОЧЕНЬ ресурсоемко, поэтому используйте его с осторожностью.
Янис Пейсениекс
6

Я сделал несколько тестов, чтобы сравнить время выполнения COUNT(*)vsCOUNT(id) (id - это первичный ключ таблицы - индексируется).

Количество проб: 10 * 1000 запросов

Полученные результаты: COUNT(*) быстрее на 7%

СМОТРЕТЬ ГРАФИК: эталонный график

Мой совет - использовать: SELECT COUNT(*) FROM table

SamuelCarreira
источник
1
К вашему сведению, есть также общий способ подсчета COUNT(1), было бы интересно увидеть там некоторые тесты ...
Sliq
4

Попробуй это:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";
bayuah
источник
@lepe мне очень жаль. Я имел в виду, что действительно приятно, если кто-то, кто голосовал против, дает какое-то объяснение, почему он / она делает это, чтобы каждый мог узнать что-то об этом.
bayuah 04
1
Это быстро даст вам приблизительный ответ. Если нужен точный ответ, нужно выполнить select count(*) from table_nameили еще что-то. dba.stackexchange.com/questions/151769/…
Программист
@Programster Спасибо. Это лучше, чем оставлять меня в темноте почти на год.
bayuah
1
@bayuah Я не совсем понимаю, что вы имели в виду в своем последнем комментарии. Могу только предположить, что вы думаете, что это я проголосовал против вашего ответа, но это не так.
Программист
1
@Programster Нет, извини, я не это имел в виду. Я имел в виду спасибо за ваше объяснение, поэтому я могу предположить, что, возможно, думает Downvoter, когда он / она это делает.
bayuah
3

Возможно, вы захотите рассмотреть возможность создания файла SELECT max(Id) - min(Id) + 1. Это будет работать, только если ваши идентификаторы являются последовательными и строки не удаляются. Однако это очень быстро.

небесно-DEV
источник
3
Будьте осторожны: серверы иногда используют значение автоматического увеличения> 1 (для резервного копирования), поэтому это решение хорошее, но сначала вам следует проверить конфигурацию своей БД.
Alex
1

EXPLAIN SELECT id FROM ....сделал трюк для меня. и я мог видеть количество строк в rowsстолбце результата.

SSRP
источник
0

Я обрабатывал таблицы для правительства Германии, иногда с 60 миллионами записей.

И нам нужно было много раз узнать общее количество строк.

Поэтому мы, программисты баз данных, решили, что в каждой таблице всегда есть запись, в которой хранится общее количество записей. Мы обновили это число в зависимости от строк INSERT или DELETE.

Мы пробовали все другие способы. Это, безусловно, самый быстрый способ.

Scoobeedo Cool
источник
1
и каковы подробности того, как вы обновляли эту строку? Это означает, хотя и неправильный дизайн таблицы, где все строки потребуют потраченного впустую int для поездки.
Дрю
5
Да, это действительно глупо, ха-ха. При каждом запросе нужно игнорировать первую строку. Я бы просто создал итоговую таблицу и заполнил ее на основе триггера. Таблица пользователей на вставке, таблица итогов обновлений. Таблица пользователей при удалении, таблица итогов обновлений.
HTMLGuy
-1

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

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

Для меня это было намного быстрее, чем

SELECT COUNT(*) FROM ...
ayakout
источник