Как SQL трактует выборку, где вместе с агрегатом указан обычный столбец без группировки?
Такая выборка неоднозначна: агрегат вычисляется по набору строк, а обычному столбцу нужно соответствующее конкретное значение. В стандартном SQL запрос обычно отклоняется, если обычный столбец не входит в группировку и не определяется однозначно группой.
Например, нельзя однозначно выбрать одновременно общее количество строк и отдельное значение status, если в таблице встречаются разные статусы. СУБД может сообщить об ошибке, а в некоторых режимах или диалектах допустить произвольное значение, что опасно для отчётов.
Агрегация появилась для получения сводных результатов: количества, сумм, минимумов и максимумов по множеству строк. Когда добавилась группировка, SQL потребовалось явно определить, какие строки образуют одну группу и как из каждой группы получить итоговые значения.
Требование группировки устраняет неоднозначность реляционной модели. Оно не позволяет молча выбрать одно из нескольких значений обычного столбца, когда агрегат относится ко всей группе.
Предположим, в таблице заказов есть несколько строк с разными статусами. Запрос одновременно хочет получить количество всех заказов и один status, но не объясняет, какой именно статус должен быть показан.
Если СУБД примет такой запрос и выберет значение случайно или зависящее от плана выполнения, результат может измениться после добавления индекса, изменения порядка чтения или обновления версии СУБД. Это делает ошибку особенно опасной: запрос иногда выглядит рабочим, но его результат не имеет устойчивого смысла.
Каждое выражение в списке выборки должно быть связано с группой одним из допустимых способов:
GROUP BY;Пример корректного запроса:
Здесь каждая группа содержит строки с одним статусом, поэтому status имеет единственное значение внутри группы, а COUNT(*) вычисляется отдельно для каждой группы.
Если нужен один общий итог, обычный столбец нужно убрать либо явно агрегировать. Например, MIN(status) технически задаёт правило выбора, но означает именно минимальный статус по правилам сравнения, а не «связанный со строкой итогового заказа».
Нельзя универсально рассчитывать на поведение без строгой группировки. PostgreSQL обычно отклоняет неоднозначный запрос; MySQL при включённом ONLY_FULL_GROUP_BY ведёт себя аналогично, а в других режимах его диалекта допускается выбор неагрегированного значения. Переносимый код должен выражать требуемое правило явно.
В отчёте требовалось вывести число заказов клиента и его регион. Разработчик использовал агрегат для количества, но не включил регион в группировку. Запрос либо завершался ошибкой, либо в менее строгой конфигурации показывал один из регионов, если исторические данные клиента были противоречивыми.
Рассматривались три варианта. Добавление региона в GROUP BY было корректным, но могло вернуть несколько строк на клиента. Использование MIN(region) давало одну строку, но скрывало конфликт данных. Отдельное получение региона из справочника клиентов отражало предметную модель, если регион действительно хранится как единственное текущее свойство клиента.
Выбрали третий вариант и отдельно добавили проверку качества данных для исторической таблицы. В результате агрегат не зависел от случайного значения, а противоречивые записи выявлялись явно, а не маскировались отчётом.
GROUP BY, запрещён?Не всегда. Некоторые СУБД могут принять его, если доказывают функциональную зависимость от группирующих столбцов, например регион однозначно определяется первичным ключом клиента. Однако поддержка и правила такого вывода зависят от СУБД и конкретного запроса. Для переносимости и ясности лучше явно учитывать это поведение, а не полагаться на неочевидный вывод оптимизатора.
GROUP BY от группировки по пустому набору?Запрос с агрегатом без GROUP BY рассматривает весь отфильтрованный набор как одну группу. Поэтому агрегат обычно возвращает одну строку даже при отсутствии исходных строк: например, COUNT(*) возвращает ноль.
Обычный столбец при этом всё равно не имеет единственного значения. Наличие одной итоговой группы не превращает произвольное значение столбца в корректный результат.
GROUP BY может изменить число строк?Потому что группировка строится по комбинации всех указанных столбцов. Если раньше строки объединялись только по клиенту, а затем добавился статус, один клиент может распасться на несколько групп — по одной на каждую комбинацию клиента и статуса.
Это не просто способ устранить ошибку синтаксиса: он меняет гранулярность результата. Перед изменением группировки нужно определить, должна ли отчётная строка описывать клиента, клиента со статусом или другую сущность.