Подтвердить что ты не робот

Должен ли я COUNT (*) или нет?

Я знаю, что в целом плохая идея делать такие запросы:

SELECT * FROM `group_relations`

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

SELECT COUNT(*) FROM `group_relations`

Или более конкретный

SELECT COUNT(`group_id`) FROM `group_relations`

У меня такое чувство, что последнее потенциально может быть быстрее, но есть ли другие вещи, которые нужно учитывать?

Обновление. Я использую InnoDB в этом случае, извините за то, что не был более конкретным.

4b9b3361

Ответ 1

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

select count(*)

будет считать все строки, тогда как

select count(group_id)

будет считать только строки, где group_id не является нулевым.

Кроме того, некоторые системы баз данных, такие как MySQL, используют оптимизацию при запросе счетчика (*), который делает такие запросы немного быстрее, чем конкретные.

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

Ответ 2

Если я правильно помню, в MYSQL COUNT (*) подсчитывается все строки, тогда как COUNT (имя_столбца) подсчитывает только строки с не-NULL значением в данном столбце.

Ответ 3

COUNT (*) подсчитывает все строки, в то время как COUNT (column_name) будет считать только строки без значений NULL в указанном столбце.

Важно отметить в MySQL:

COUNT() очень быстро работает в таблицах MyISAM для столбцов * или не-нуль, поскольку количество строк кэшируется. В InnoDB нет кеширования строк, поэтому нет никакой разницы в производительности для COUNT (*) или COUNT (имя_столбца), независимо от того, может ли этот столбец быть нулевым или нет. Вы можете больше узнать о различиях в этом сообщении в блоге производительности MySQL.

Ответ 4

если вы попробуете SELECT COUNT(1) FROM group_relations, он будет немного быстрее, потому что он не будет пытаться извлекать информацию из ваших столбцов.

Изменить: я только что провел некоторое исследование и выяснил, что это происходит только в некоторых битках. В sqlserver он одинаково использует 1 или *, но на оракуле быстрее использовать 1.

http://social.msdn.microsoft.com/forums/en-US/transactsql/thread/9367c580-087a-4fc1-bf88-91a51a4ee018/

По-видимому, нет никакой разницы между ними в mysql, например sqlserver, по-видимому, синтаксический анализатор меняет запрос на выбор (1). Извините, если я каким-то образом обманываю вас.

Ответ 5

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

У меня есть таблица MySQL (InnoDB), которая содержит 5 607 997 записей. Таблица находится в моей собственной песочнице, поэтому я знаю, что содержимое статично, а никто другой не использует сервер. Я думаю, что это эффективно устраняет все внешние воздействия на производительность. У меня есть таблица с полем первичного ключа auto_increment (Id), которое, как я знаю, никогда не будет равным null, которое я буду использовать для теста where where (ИД ГДЕ НЕ НЕТ).

Единственный возможный глюк, который я вижу при выполнении тестов, - это кеш. При первом запуске запроса всегда будет медленнее, чем последующие запросы, которые используют одни и те же индексы. Я буду ссылаться на это ниже в качестве запроса кэширования Seeding. Просто, чтобы немного смешать его, я запустил его с предложением where, которое, как я знаю, всегда будет оцениваться как истинное независимо от любых данных (TRUE = TRUE).

Вот мои результаты:

QueryType

      |  w/o WHERE          | where id is not null |  where true=true

COUNT()

      |  9 min 30.13 sec ++ | 6 min 16.68 sec ++   | 2 min 21.80 sec ++
      |  6 min 13.34 sec    | 1 min 36.02 sec      | 2 min 0.11 sec 
      |  6 min 10.06 se     | 1 min 33.47 sec      | 1 min 50.54 sec

COUNT (Id)

      |  5 min 59.87 sec    | 1 min 34.47 sec      | 2 min 3.96 sec 
      |  5 min 44.95 sec    | 1 min 13.09 sec      | 2 min 6.48 sec

COUNT (1)

      | 6 min 49.64 sec    | 2 min 0.80 sec       | 2 min 11.64 sec
      | 6 min 31.64 sec    | 1 min 41.19 sec      | 1 min 43.51 sec

++ Это считается кешированием. Ожидается, что он будет медленнее остальных.

Я бы сказал, что результаты говорят сами за себя. COUNT (Id) обычно режет другие. Добавление предложения Where значительно сокращает время доступа, даже если это условие, которое вы знаете, будет оцениваться как true. Сладкое пятно кажется COUNT (Id)... WHERE Id NOT NULL.

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

Ответ 6

Ищите альтернативы

Как вы видели, когда таблицы растут большими, запросы COUNT становятся медленными. Я думаю, что самое главное - рассмотреть характер проблемы, которую вы пытаетесь решить. Например, многие разработчики используют запросы COUNT при создании разбивки на страницы для больших наборов записей, чтобы определить общее количество страниц в наборе результатов.

Зная, что запросы COUNT будут замедляться, вы можете рассмотреть альтернативный способ отображения элементов управления разбиением на страницы, что просто позволяет вам выполнить медленный запрос. Google pagination - отличный пример.

Денормализовать

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

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

BEGIN TRANSACTION;
INSERT INTO  `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;

В качестве альтернативы вы можете использовать триггеры базы данных, если ваша РСУБД поддерживает их.

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

Ответ 7

Таблицы MySQL ISAM должны иметь оптимизацию для COUNT (*), пропуская полное сканирование таблицы.

Ответ 8

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

Я хочу, чтобы выбрать COUNT (*) быстрее, чем выбрать COUNT (поле). Если СУРБД обнаружила, что вы указываете "*" на COUNT вместо поля, нет необходимости оценивать что-либо, чтобы увеличить счетчик. Если вы укажете поле в COUNT, то РСУБД всегда будет оценивать, является ли ваше поле нулевым или не считать его.

Но если ваше поле имеет значение NULL, укажите поле в COUNT.

Ответ 9

COUNT (*) факты и мифы:

MYTH: "InnoDB не обрабатывает запросы count (*)":

Большинство запросов count (*) выполняются одинаковым образом всеми механизмами хранения, если у вас есть предложение WHERE, иначе вам InnoDB придется выполнить полное сканирование таблицы.

ФАКТ: InnoDB не оптимизирует запросы count (*) без предложения where

Ответ 10

Лучше всего подсчитывать индексированный столбец, такой как первичный ключ.

SELECT COUNT(`group_id`) FROM `group_relations`

Ответ 11

Это должно зависеть от того, что вы на самом деле пытаетесь достичь, как сказал Себастьян, т.е. сделать ваши намерения ясными! Если вы просто подсчитываете строки, переходите к COUNT (*) или считаете один столбец для столбца COUNT (столбец).

Возможно, стоит проверить и поставщика БД. Назад, когда я использовал Informix, имел оптимизацию для COUNT (*), у которой была выполнена стоимость выполнения плана запроса 1 по сравнению с подсчетом одиночных или нескольких столбцов, что привело бы к более высокой цифре

Ответ 12

если вы попробуете SELECT COUNT (1) FROM group_relations, он будет немного быстрее, потому что он не будет пытаться извлекать информацию из ваших столбцов.

COUNT (1) используется быстрее, чем COUNT (*), но это не так, поскольку современные СУБД достаточно умны, чтобы знать, что вы не хотите знать о столбцах

Ответ 13

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

Напишите запрос, который отвечает на заданный вами вопрос - если вы хотите подсчитать все строки, используйте COUNT (*). Если вы хотите подсчет ненулевых столбцов, используйте COUNT (col) WHERE col NOT NOT. Индекс соответствующим образом и оставить оптимизацию оптимизатору. Попытка сделать собственную оптимизацию на уровне запросов может иногда сделать встроенный оптимизатор менее эффективным.

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

Изменить: статистика в ответе выше интересна. Я не уверен, есть ли что-то в работе в оптимизаторе в этом случае. Я просто говорю о оптимизации на уровне запросов в целом.

Ответ 14

Я знаю, что это вообще плохая идея сделать такие запросы:

SELECT * FROM `group_relations`

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

SELECT COUNT(*) FROM `group_relations`

Как следует из вашего вопроса, причина SELECT * не рекомендуется, так как изменения в таблице могут потребовать внесения изменений в ваш код. Это не относится к COUNT(*). Довольно редко требуется специализированное поведение, которое дает SELECT COUNT('group_id') - как правило, вы хотите узнать количество записей. Для чего нужен COUNT(*), поэтому используйте его.