Вопрос

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

SELECT * FROM `group_relations`

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

SELECT COUNT(*) FROM `group_relations`

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

SELECT COUNT(`group_id`) FROM `group_relations`

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

Обновлять:В данном случае я использую InnoDB, извините за неточность.

Это было полезно?

Решение

Если рассматриваемый столбец НЕ НЕДЕЙСТВИТЕЛЕН, оба ваших запроса эквивалентны. Когда идентификатор_группы содержит нулевые значения,

select count(*)

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

select count(group_id)

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

Тем не менее, вот мои результаты:

Тип запроса

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

СЧИТАТЬ()

      |  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(идентификатор)

      |  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

СЧЕТ(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)...ГДЕ Id НЕ НУЛЬ.

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

Искать альтернативы

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

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

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

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

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

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

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

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

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

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

.

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

Но если ваше поле можно обнулять, укажите это поле в COUNT.

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

MYTH : " InnoDB плохо обрабатывает количество (*) запросов " ;:

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

FACT : InnoDB не оптимизирует количество (*) запросов без предложения where

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

SELECT COUNT(`group_id`) FROM `group_relations`

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

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

  

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

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

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

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

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

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

  

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

SELECT * FROM `group_relations`
     

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

SELECT COUNT(*) FROM `group_relations`

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top