В чем разница между COUNT(*) и COUNT(1) в SQL
Что проверяют
Знаете ли вы, что эти два варианта дают одинаковый результат, и не повторяете ли миф про «COUNT(1) быстрее». Настоящая цель обычно в уточнении: чем от них отличается COUNT(column) и что станет с подсчётом после LEFT JOIN.
Как отвечать
Начните с семантики, потом переходите к скорости:
- *`COUNT()`**: считает строки результата, значения колонок не проверяет. По стандарту это отдельная форма агрегата без аргумента;
COUNT(1): считает строки, где выражение1неNULL. Константа1никогда не бываетNULL, поэтому результат всегда совпадает сCOUNT(*);COUNT(column): пропускает строки сNULLв колонке, и вот здесь результат уже другой;COUNT(DISTINCT column): число уникальных значений безNULL, заметно дороже из-за сортировки или хэширования.
По скорости: документация MySQL прямо пишет, что InnoDB обрабатывает COUNT(*) и COUNT(1) одинаково. В PostgreSQL count(*) реализован как агрегат без аргумента, а count(1) формально проверяет выражение на каждой строке, но разница на уровне погрешности. Дорогим подсчёт делает объём, а не форма записи: из-за MVCC ни PostgreSQL, ни InnoDB не хранят точное число строк, и COUNT(*) по большой таблице читает таблицу или индекс. Для примерной цифры в PostgreSQL есть pg_class.reltuples и оценка из EXPLAIN.
Python-разработчику стоит добавить, что ORM генерируют именно COUNT(*): Django QuerySet.count() и SQLAlchemy select(func.count()).select_from(User). А len(queryset) в Django сначала загрузит все строки в память.
Где спотыкаются
Говорят, что COUNT(1) быстрее, потому что не читает все колонки. COUNT(*) их тоже не читает, звёздочка здесь не означает «все поля».
Путают COUNT(1) с подсчётом по первой колонке. Единица в скобках это константа, а не номер колонки, как в ORDER BY 1.
Считают COUNT(*) после LEFT JOIN и получают единицу у групп без связанных записей. Для подсчёта дочерних строк нужен COUNT(child.id), он пропустит NULL из несовпавших строк.