При группировке по столбцу с NULL несколько строк с NULL попадут в одну группу или в разные?
Несколько строк со значением NULL попадут в одну группу. Это особое правило группировки: хотя выражение NULL = NULL не является истинным, GROUP BY объединяет все строки с NULL в одной позиции группировочного ключа.
Важно не путать это с обычным сравнением. Для проверки NULL в фильтре используют IS NULL, а не сравнение через =.
NULL был введён в SQL для представления отсутствующего или неизвестного значения. Обычная логика сравнения не может считать два неизвестных значения равными, поэтому сравнение с NULL даёт не TRUE, а UNKNOWN.
Однако агрегированные отчёты должны разбивать все строки на конечное число групп. Поэтому механизм группировки обрабатывает NULL как единое значение группировочного ключа, позволяя отдельно посчитать строки с отсутствующими данными.
Предположим, отчёт группирует продажи по региону. Если у нескольких продаж регион не заполнен, все они попадут в группу с ключом NULL, а не будут распределены по отдельным группам.
Это удобно для подсчёта пропусков, но опасно при интерпретации результата: одна группа NULL не означает один неизвестный регион. Она означает, что у всех этих строк регион отсутствует или неизвестен.
Результат содержит три группы: Север, Юг и NULL с двумя строками. При этом условие WHERE region = NULL не найдёт такие строки; корректная проверка — WHERE region IS NULL.
GROUP BY формирует группы по значениям выражений группировки. Для NULL все строки с NULL в соответствующем ключе считаются одной группой. Поэтому в запросе с группировкой по одному столбцу будет не отдельная группа на каждую строку с NULL, а одна общая группа.
Это правило не меняет поведения агрегатов. COUNT(*) считает все строки группы, включая строки с NULL. COUNT(region) считает только строки, где region не равен NULL, поэтому для группы NULL его результат будет равен нулю. Большинство обычных агрегатов, например SUM и AVG, игнорируют NULL, а результат для группы без единого ненулевого значения может быть NULL.
При группировке по нескольким столбцам NULL относится только к своей позиции ключа. Например, пары (Север, NULL) и (Север, NULL) образуют одну группу, а (Север, NULL) и (Юг, NULL) — разные, поскольку различается первый компонент.
Для отображения понятной подписи можно использовать COALESCE, но это меняет только вывод или ключ группировки. Если в данных уже существует настоящий регион с названием вроде «Не указан», замена NULL на такую строку может слить две смысловые категории. Безопаснее сначала агрегировать по исходному ключу, а подпись формировать отдельно.
В отчёте по качеству справочника требовалось показать продажи по регионам и отдельно контролировать записи без региона. Рассматривались два варианта: заменить NULL на текст «Не указан» прямо в GROUP BY или оставить NULL как отдельный ключ.
Первый вариант проще для пользовательского отчёта, но может ошибочно объединить отсутствующее значение с реально сохранённой категорией «Не указан». Второй вариант сохраняет семантику данных, однако требует отдельного форматирования заголовка группы.
Был выбран второй вариант: группировка выполнялась по исходному столбцу, а NULL преобразовывался в подпись только на слое представления. В результате число пропущенных регионов считалось корректно, а возможное значение «Не указан» не смешивалось с техническим отсутствием данных.
Будет ли группа NULL создана, если в таблице нет строк с NULL?
Нет. Группа создаётся только при наличии хотя бы одной строки, попавшей в неё. Если NULL-значений нет, в результате не появится пустая группа NULL.
Это отличается от некоторых ожиданий о «полном справочнике категорий»: обычный GROUP BY показывает только реально присутствующие группы. Чтобы отображать категории без продаж, обычно нужен отдельный справочник и внешнее соединение до или после агрегации.
Чем отличается COUNT(*) от COUNT(столбец) внутри NULL-группы?
COUNT(*) считает все строки, поэтому в группе, сформированной из строк с NULL в ключе, он покажет количество этих строк. COUNT(столбец) считает только ненулевые значения указанного столбца.
Например, если группировка выполнена по region, то для группы region IS NULL выражение COUNT(region) даст ноль. Это не означает отсутствие строк — это означает, что сам столбец region во всех строках группы имеет NULL.
Почему фильтр столбец = NULL не отбирает строки с NULL?
NULL означает неизвестное или отсутствующее значение, поэтому результат сравнения NULL = NULL не является TRUE. В предложении WHERE проходят только строки, для которых условие истинно, а UNKNOWN отбрасывается.
Для проверки отсутствия значения применяют предикаты IS NULL и IS NOT NULL. Это правило действует независимо от того, что при последующей группировке все строки с NULL будут объединены в одну группу.