Что можно выводить в SELECT при использовании GROUP BY и есть ли ограничения
Что проверяют
Понимаете ли вы, что после группировки строка результата это уже не строка таблицы, а группа, и знаете ли, чем стандарт отличается от поведения конкретной базы. Вопрос попадается и бэкендеру на Go: сервис ходит в базу сам, и кривой запрос лечится не кодом.
Как отвечать
Выводить можно три вещи: выражения, перечисленные в GROUP BY, агрегаты (COUNT, SUM, AVG, MIN, MAX, string_agg, array_agg), константы и любые выражения, построенные из первых двух. Неагрегированный столбец, которого нет в группировке, недопустим: для группы у него нет единственного значения.
Дальше стоит показать знание диалектов:
- PostgreSQL разрешает выводить столбцы, функционально зависимые от первичного ключа: сгруппировали по
u.id, можно выбрать иu.name; - MySQL с режимом
ONLY_FULL_GROUP_BYведёт себя по стандарту, без него молча отдавал произвольное значение; сказать явно «любое из группы» позволяетANY_VALUE(); - SQLite такой запрос выполнит и возьмёт значение из произвольной строки.
Полезные детали рядом: WHERE фильтрует до группировки, HAVING после; COUNT(*) считает строки, а COUNT(col) пропускает NULL; все NULL попадают в одну группу. Если нужна одна запись на пользователя, а не агрегат, в PostgreSQL чище DISTINCT ON или оконная функция.
Где спотыкаются
Тянут в выборку лишний столбец и объясняют, что внутри группы он всё равно одинаковый. База этого не знает и не проверяет, а там, где выполнит, однажды вернёт значение из другой строки.
Пишут условие на агрегат в WHERE. Отбор групп по COUNT(*) > 1 живёт только в HAVING, потому что на момент WHERE групп ещё не существует. Туда же попытка сослаться в WHERE на алиас из SELECT.
Забывают про соединения. При связи один ко многим SUM по таблице заказов удваивается на каждой дополнительной строке справа, и отчёт расходится с реальностью. Лечится агрегацией в подзапросе до соединения или COUNT(DISTINCT ...), а не подгонкой цифр.