В чем разница между COUNT(*) и COUNT(1) в SQL
Что проверяют
Знаете ли вы, как агрегат обходится с NULL, и не верите ли в миф о «быстром COUNT(1)». Следом почти всегда спрашивают про COUNT(column) и о том, почему подсчёт строк на большой таблице бывает медленным, даже если запрос написан «правильно».
Как отвечать
Начните с семантики, она важнее производительности:
- *`COUNT()`**: считает строки целиком, значения колонок не проверяются;
COUNT(1): считает строки, для которых выражение1неNULL. КонстантаNULLне бывает никогда, поэтому результат всегда совпадает сCOUNT(*);COUNT(column): пропускает строки, где в колонкеNULL. Вот здесь результат действительно другой;COUNT(DISTINCT column): уникальные значения безNULL, и это заметно дороже, потому что нужна сортировка или хэширование.
Про скорость: в Oracle, SQL Server и MySQL оптимизатор строит для COUNT(*) и COUNT(1) один и тот же план. В PostgreSQL count(*) устроен как отдельный агрегат без аргумента, а count(1) формально проверяет аргумент для каждой строки, поэтому на больших объёмах он даже чуть медленнее. Отсюда рекомендация: писать COUNT(*), это и понятнее, и не хуже.
Настоящая цена подсчёта в другом. В PostgreSQL и InnoDB из-за MVCC база не хранит готовое число строк: каждая транзакция видит свою версию данных, поэтому COUNT(*) читает таблицу или индекс целиком. Для примерной оценки есть статистика (reltuples в pg_class), а для частых точных подсчётов заводят отдельную таблицу со счётчиком.
В Java-коде это всплывает в Spring Data: метод count() репозитория и countBy... считают на стороне базы, а в JPQL вместо звёздочки пишут count(e).
Где спотыкаются
Утверждают, что COUNT(1) быстрее, потому что «не читает колонки». COUNT(*) колонки тоже не читает.
Путают COUNT(*) и COUNT(column) и получают расхождение в отчёте, как только в колонке появляются NULL.
Проверяют наличие записей через count() > 0. Для этого есть EXISTS и в Spring Data методы existsBy...: база останавливается на первой найденной строке, а не пересчитывает всё.