Есть ли практическая разница между COUNT(*) и COUNT(1) при подсчёте строк группы?
В стандартной семантике SQL COUNT(*) и COUNT(1) возвращают одинаковое число строк группы: выражение 1 не равно NULL ни для одной строки. COUNT(*) явно считает строки, поэтому обычно предпочтительнее как более понятная запись; COUNT(1) не даёт гарантированного преимущества по производительности.
Отличие появляется при сравнении с COUNT(столбец): он считает только строки, где значение указанного столбца не равно NULL.
Агрегат COUNT должен решать две разные задачи: считать количество строк и считать количество присутствующих значений. Поэтому SQL различает специальную форму COUNT(*) и форму COUNT(выражение), которая игнорирует результаты выражения, равные NULL.
COUNT(1) относится ко второй форме: он считает результаты выражения 1. Поскольку константа 1 вычисляется для каждой строки и не является NULL, результат совпадает с подсчётом строк.
Ошибка возникает, когда разработчик переносит свойства COUNT(столбец) на COUNT(1) или считает, что COUNT(*) подсчитывает только непустые значения. Это может привести к неверному выбору агрегата в отчёте, особенно если данные содержат NULL.
Также не следует выбирать COUNT(1) ради предполагаемого ускорения. Современная СУБД обычно оптимизирует обе формы одинаково, но конкретный план зависит от реализации, статистики и запроса; семантического преимущества у COUNT(1) нет.
COUNT(*) возвращает количество строк, попавших в группу после применения фильтрации уровня WHERE. Он не проверяет значения отдельных столбцов на NULL.
COUNT(1) вычисляет выражение 1 для каждой строки и считает ненулевые результаты. Поэтому при обычной константе результат равен COUNT(*).
COUNT(столбец) работает иначе: строки, в которых столбец имеет значение NULL, не учитываются. Например, если в группе три строки, но в двух из них amount равен NULL, то COUNT(*) и COUNT(1) дадут 3, а COUNT(amount) — 1.
В этом примере первые два результата совпадают при любых строках, включая строки с NULL в amount. Третий результат показывает только количество платежей с известной суммой.
Пустая группа в агрегатном запросе без GROUP BY сохраняет важное различие: COUNT(*) и COUNT(1) возвращают 0, тогда как SUM обычно возвращает NULL. Это связано с тем, что COUNT определён как количество элементов, а сумма пустого набора не имеет числового результата.
В отчёте нужно посчитать число записей в таблице платежей. Рассматривались три варианта: COUNT(*), COUNT(1) и COUNT(amount). Последний вариант был отвергнут, потому что пропускал платежи с неизвестной суммой; COUNT(1) давал правильный результат, но не объяснял намерение лучше, чем COUNT(*).
Выбрали COUNT(*): он непосредственно выражает требование «посчитать строки» и не зависит от заполненности отдельных столбцов. После добавления группировки по месяцу каждая строка результата показывала число платежей соответствующего месяца, включая платежи с NULL в amount.
Может ли COUNT(1) дать другой результат, если в таблице есть NULL?
Нет, NULL в столбцах таблицы не влияет на константу 1. COUNT(1) исключает только строки, где само выражение равно NULL; обычная числовая константа таким не является. Другой результат появится у COUNT(столбец), если именно этот столбец содержит NULL.
Чем отличается COUNT(*) от COUNT(выражение), если выражение вычисляется из нескольких столбцов?
COUNT(выражение) считает строку только тогда, когда итог выражения не равен NULL. В SQL арифметика с NULL обычно даёт NULL, поэтому выражение вроде суммы двух столбцов может исключить строку, если хотя бы один операнд неизвестен. COUNT(*) такую строку всё равно учитывает.
Может ли оптимизатор гарантированно выполнить COUNT(1) быстрее, чем COUNT(*)?
Нет. Стандартная семантика этих форм различается способом записи, но при константе, не равной NULL, результат одинаков. Конкретная СУБД может построить одинаковый или эквивалентный план; полагаться на преимущество COUNT(1) без измерений нельзя. Для читаемости обычно выбирают COUNT(*), когда требуется именно количество строк.