Я знаю, что обычно делать такие запросы - плохая идея:
SELECT * FROM `group_relations`
Но когда мне просто нужен счет, следует ли мне обратиться к этому запросу, поскольку он позволяет изменять таблицу, но по-прежнему дает те же результаты.
SELECT COUNT(*) FROM `group_relations`
Или более конкретный
SELECT COUNT(`group_id`) FROM `group_relations`
Я чувствую, что последний потенциально может быть быстрее, но есть ли еще что-нибудь, что нужно учитывать?
Обновлять: В этом случае я использую InnoDB, извините за то, что не стал более конкретным.
Хороший вопрос, я тоже задумывался об этом раньше.






Если я правильно помню, в MYSQL COUNT (*) подсчитывает все строки, тогда как COUNT (column_name) считает только строки, которые имеют значение, отличное от NULL в данном столбце.
Если столбец, о котором идет речь, НЕ ПУСТО, оба ваших запроса эквивалентны. Когда group_id содержит нулевые значения,
select count(*)
будет считать все строки, тогда как
select count(group_id)
будет подсчитывать только те строки, в которых group_id не равно нулю.
Кроме того, некоторые системы баз данных, такие как MySQL, используют оптимизацию, когда вы запрашиваете count (*), что делает такие запросы немного быстрее, чем конкретный.
Лично, когда я просто считаю, я делаю count (*), чтобы обезопасить себя от нулей.
То, как mysql обрабатывает счетчик, сильно различается в зависимости от того, какой движок базы данных вы используете. В этом ответе предполагается MyISAM, но в вопросе указано INNODB. В первом случае это быстро и кэшируется, во втором - довольно медленно.
правда, но а) примечания InnoDB не было, когда я написал ответ, и б) пока InnoDB не может оптимизировать счетчик, count (*) не быстрее и не медленнее, чем count (group_id). И даже в MyISAM вы потеряете преимущество в скорости в тот момент, когда добавите предложение WHERE.
также, если вы хотите узнать, существует ли строка, выберите SELECT COUNT (group_id) FROM group_relations WHERE ROWNUM = 1
если вы попробуете SELECT COUNT(1) FROMgroup_relations, он будет немного быстрее, потому что он не будет пытаться получить информацию из ваших столбцов.
Обновлено: я только что провел небольшое исследование и обнаружил, что это происходит только в некоторых db. В sqlserver использовать 1 или * то же самое, но в Oracle быстрее использовать 1.
По-видимому, в mysql нет разницы между ними, как и sqlserver, парсер, похоже, меняет запрос на select (1). Извините, если я ввел вас в заблуждение.
Быстрее COUNT ()? Почему COUNT () нужно извлекать информацию из столбцов?
Если MySQL извлекает информацию о столбце для count (*), это будет серьезным поводом для отказа от нее как от СУБД. Не могу поверить, что разработчики были настолько глупы.
Это должно зависеть от того, чего вы на самом деле пытаетесь достичь, как уже сказал Себастьян, т.е. проясните свои намерения! Если вы находятся просто подсчитываете строки, используйте COUNT (*) или подсчитывая один столбец, используйте COUNT (столбец).
Возможно, стоит также проверить вашего поставщика БД. Когда я использовал Informix, у него была оптимизация для COUNT (*), у которого стоимость выполнения плана запроса была равна 1 по сравнению с подсчетом одного или нескольких столбцов, что привело бы к более высокому значению.
COUNT (*) подсчитывает все строки, а COUNT (имя_столбца) подсчитывает только строки без значений NULL в указанном столбце.
Важное примечание в MySQL:
COUNT () работает очень быстро в таблицах MyISAM для столбцов * или ненулевых столбцов, поскольку счетчик строк кэшируется. InnoDB не имеет кэширования количества строк, поэтому нет разницы в производительности для COUNT (*) или COUNT (имя_столбца), независимо от того, может ли столбец иметь значение NULL или нет. Вы можете узнать больше о различиях в эта почта в блоге о производительности MySQL.
может быть, глупый вопрос ... как мне узнать, поддерживается ли мой mysql db innodb или myisam? Я работаю на общем сервере, поэтому сам не настраивал ...
InnoDB и MyISAM - это механизмы хранения таблиц, в MySQL их несколько - dev.mysql.com/doc/refman/5.0/en/storage-engines.html. Ваша база данных может иметь несколько механизмов хранения для разных таблиц. Загляните в свой phpMyAdmin, чтобы проверить механизм хранения ваших различных таблиц.
Таблицы MySQL ISAM должны иметь оптимизацию для COUNT (*), пропуская полное сканирование таблицы.
if you try SELECT COUNT(1) FROM group_relations it will be a bit faster because it will not try to retrieve information from your columns.
COUNT (1) раньше был быстрее, чем COUNT (*), но это уже не так, поскольку современные СУБД достаточно умны, чтобы знать, что вы не хотите знать о столбцах
Звездочка в COUNT не имеет отношения к звездочке для выбора всех полей таблицы. Это полная чушь говорить, что COUNT (*) медленнее, чем COUNT (поле)
Мне кажется, что выбрать COUNT (*) быстрее, чем выбрать COUNT (поле). Если СУБД обнаружила, что вы указываете «*» в COUNT вместо поля, ей не нужно ничего оценивать для увеличения счетчика. Принимая во внимание, что если вы укажете поле в COUNT, СУБД всегда будет оценивать, является ли ваше поле нулевым или не подсчитывать его.
Но если ваше поле допускает значение NULL, укажите поле в COUNT.
COUNT (*) фактов и мифов:
МИФ: «InnoDB плохо обрабатывает запросы count (*)»:
Большинство запросов count (*) выполняются одинаково всеми механизмами хранения, если у вас есть предложение WHERE, в противном случае вам InnoDB придется выполнить полное сканирование таблицы.
ФАКТ: InnoDB не оптимизирует запросы count (*) без предложения where
Мне самому это было любопытно. Можно читать документацию и теоретические ответы, но мне нравится уравновешивать их эмпирическими данными.
У меня есть таблица MySQL (InnoDB), в которой 5 607 997 записей. Таблица находится в моей частной песочнице, поэтому я знаю, что ее содержимое статично, и никто другой не использует сервер. Я думаю, что это эффективно устраняет все внешние факторы, влияющие на производительность. У меня есть таблица с полем первичного ключа auto_increment (Id), которое, как я знаю, никогда не будет нулевым, и я буду использовать его для проверки предложения where (WHERE Id IS NOT NULL).
Единственный другой возможный сбой, который я вижу при запуске тестов, - это кеш. Первый запуск запроса всегда будет медленнее, чем последующие запросы, использующие те же индексы. Я буду называть это ниже вызовом заполнения кеша. Чтобы немного смешать, я запустил его с предложением where, которое, как я знаю, всегда будет оценивать как истинное независимо от каких-либо данных (TRUE = TRUE).
Тем не менее, вот мои результаты:
QueryType
| 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 (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
СЧЁТ (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 резко сокращает время доступа, даже если оно, как вы знаете, оценивается как истинное. Лучшее место - COUNT (Id) ... ГДЕ Id НЕ ПУСТО.
Мне бы хотелось увидеть результаты других людей, возможно, с меньшими таблицами или с предложениями where для полей, отличных от поля, которое вы подсчитываете. Я уверен, что есть и другие варианты, которые я не учел.
Интересный тест. Я попытался продублировать ваши результаты, используя таблицу из более чем 80 миллионов записей на Mysql 5.1.45 с использованием Innodb. Мои результаты были очень разными, подсчитал () без предложения Where был на 20% быстрее, чем любой другой метод. Добавление предложения Where всегда выполнялось медленнее. Мой сервер мог посчитать () 80M строк за 15,2 секунды. У меня тоже был беззнаковый int auto-inc в качестве первичного ключа.
Совет, который я получил от MySQL по поводу подобных вещей, заключается в том, что в целом попытка оптимизировать запрос на основе подобных уловок может быть проклятием в долгосрочной перспективе. В истории MySQL есть примеры, когда чья-то высокопроизводительная техника, основанная на том, как работает оптимизатор, оказывается узким местом в следующей версии.
Напишите запрос, который отвечает на вопрос, который вы задаете - если вы хотите подсчитать все строки, используйте COUNT (*). Если вы хотите подсчитать ненулевые столбцы, используйте COUNT (col) WHERE col IS NOT NULL. Индексируйте соответствующим образом и оставьте оптимизацию оптимизатору. Попытка сделать собственную оптимизацию на уровне запроса иногда может сделать встроенный оптимизатор менее эффективным.
Тем не менее, есть вещи, которые вы можете сделать в запросе, чтобы оптимизатору было проще его ускорить, но я не верю, что COUNT является одним из них.
Обновлено: статистика в ответе выше интересна. Я не уверен, действительно ли что-то работает в оптимизаторе в этом случае. Я просто говорю об оптимизации на уровне запросов в целом.
Лучше всего производить подсчет по индексированному столбцу, например по первичному ключу.
SELECT COUNT(`group_id`) FROM `group_relations`
I know it's generally a bad idea to do queries like this:
SELECT * FROM `group_relations`But when I just want the count, should I go for this query since that allows the table to change but still yields the same results.
SELECT COUNT(*) FROM `group_relations`
Как следует из вашего вопроса, причина, по которой SELECT * не рекомендуется, заключается в том, что изменения в таблице могут потребовать изменений в вашем коде. Это не относится к COUNT(*). Довольно редко требуется специализированное поведение, которое дает вам SELECT COUNT('group_id') - обычно вам нужно знать количество записей. Для этого нужен COUNT(*), так что используйте его.
Как вы видели, когда таблицы становятся большими, запросы COUNT замедляются. Я думаю, что самое важное - это учитывать природу проблемы, которую вы пытаетесь решить. Например, многие разработчики используют запросы COUNT при создании разбивки на страницы для больших наборов записей, чтобы определить общее количество страниц в наборе результатов.
Зная, что запросы COUNT будут расти медленно, вы могли бы рассмотреть альтернативный способ отображения элементов управления разбиением на страницы, который просто позволяет вам обходить медленный запрос. Пагинация Google - отличный пример.
Если вам абсолютно необходимо знать количество записей, соответствующих определенному количеству, рассмотрите классический метод денормализации данных. Вместо подсчета количества строк во время поиска рассмотрите возможность увеличения счетчика при вставке записи и уменьшения этого счетчика при удалении записи.
Если вы решите это сделать, подумайте об использовании идемпотентных транзакционных операций, чтобы синхронизировать эти денормализованные значения.
BEGIN TRANSACTION;
INSERT INTO `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;
В качестве альтернативы вы можете использовать триггеры базы данных, если ваша СУБД поддерживает их.
В зависимости от вашей архитектуры может иметь смысл использовать слой кэширования, такой как memcached, для хранения, увеличения и уменьшения денормализованного значения и просто переходить к медленному запросу COUNT, когда ключ кеша отсутствует. Это может уменьшить общую конкуренцию за запись, если у вас очень изменчивые данные, хотя в таких случаях вам стоит рассмотреть решения для эффекта собачьей груды.
Вы действительно должны указать, говорите ли вы о MyISAM или InnoDB (или о чем-то другом)