Какие поля можно использовать в SELECT при наличии GROUP BY
Что проверяют
Знаете ли вы правило «в SELECT только то, что однозначно для группы», и понимаете ли, откуда оно берётся. Следом часто спрашивают, почему запрос работал на старом MySQL и падает на PostgreSQL, и как группировка выглядит в JPQL.
Как отвечать
После GROUP BY каждая строка результата представляет группу, поэтому в SELECT допустимо только то, что имеет одно значение на группу:
- колонки из
GROUP BYи выражения над ними, напримерdate_trunc('month', created_at)при группировке по тому же выражению; - агрегатные функции:
COUNT,SUM,AVG,MIN,MAX, в PostgreSQL ещёstring_aggиarray_agg; - константы и параметры;
- функционально зависимые колонки: если группировка идёт по первичному ключу таблицы, остальные её колонки тоже однозначны. PostgreSQL это разрешает,
GROUP BY u.idпозволяет вывестиu.name.
Про MySQL скажите отдельно: режим ONLY_FULL_GROUP_BY включён по умолчанию с версии 5.7.5, и неагрегированная колонка вне группы даёт ошибку. До этого MySQL молча брал значение из произвольной строки. Если значение действительно не важно, есть ANY_VALUE().
Фильтр по агрегату пишется в HAVING, фильтр по строкам до группировки в WHERE, и второй дешевле: в группировку попадает меньше данных.
В Java это всплывает в JPQL и Spring Data JPA: @Query("select o.status, count(o) from Order o group by o.status") с проекцией в интерфейс или record через select new. Если нужны и детали строки, и итог по группе, не расширяйте GROUP BY, а возьмите оконную функцию count(*) over (partition by status): в нативном запросе она работает везде, в HQL её поддерживает Hibernate 6.
Где спотыкаются
Добавляют в GROUP BY все колонки из SELECT, лишь бы запрос прошёл. Группы дробятся, и итог считается уже не по тому, что спрашивали.
Пишут условие по агрегату в WHERE (WHERE count(*) > 10) и получают ошибку: WHERE выполняется до группировки.
Используют алиас из SELECT в GROUP BY и удивляются, что где-то это работает, а где-то нет. PostgreSQL и MySQL это допускают, SQL Server нет, Oracle научился только в последних версиях.