Что такое GROUP BY в SQL
Что проверяют
Базовое понимание агрегации и того, что после группировки строка результата это группа, а не исходная запись. Для Python-разработчика вопрос почти всегда уходит в ORM: как написать то же в Django или SQLAlchemy и почему запрос вернул не те числа.
Как отвечать
GROUP BY схлопывает строки с одинаковыми значениями указанных колонок в одну и считает по каждой группе агрегаты: COUNT, SUM, AVG, MIN, MAX. Пример: SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id.
Поверх определения стоит проговорить:
- порядок выполнения: логически
FROM,WHERE,GROUP BY,HAVING,SELECT,ORDER BY. Отсюда разница:WHEREотсекает строки до группировки,HAVINGфильтрует уже группы и может ссылаться на агрегат; - правило для SELECT: каждая колонка вне агрегата обязана стоять в
GROUP BY. PostgreSQL делает исключение, если группировка идёт по первичному ключу таблицы; - NULL: все
NULLв колонке группировки попадают в одну группу, аCOUNT(col)их не считает, в отличие отCOUNT(*); - когда группировка не нужна: если нужны и строки, и итог по группе, берут оконную функцию
SUM(amount) OVER (PARTITION BY customer_id).
В Django группировку задаёт values() перед annotate(): Order.objects.values("customer_id").annotate(total=Count("id")). В SQLAlchemy 2.x это select(Order.customer_id, func.count()).group_by(Order.customer_id). Если вакансия про данные, упомяните pandas: df.groupby("customer_id")["amount"].sum() устроен так же.
Где спотыкаются
Пишут в SELECT колонку, которой нет в GROUP BY. SQLite такой запрос пропускает и берёт значение из произвольной строки группы, поэтому тесты на SQLite зелёные, а на PostgreSQL запрос падает.
Путают порядок values() и annotate() в Django. annotate() без предшествующего values() посчитает агрегат для каждого объекта, а не сгруппирует их. Поле из order_by() тоже попадает в GROUP BY и дробит группы.
Ставят два annotate(Count(...)) по разным связям в одном запросе. JOIN двух связей перемножает строки, и оба счётчика завышаются; помогает Count(..., distinct=True) или подзапрос.