GROUP BY и HAVING
Содержание курса
В прошлом уроке агрегатные функции — COUNT, SUM, AVG и другие — сворачивали всю таблицу в одну итоговую строку. Это полезно, но часто нужно не одно число на всю базу, а отдельный результат для каждой категории товаров, каждого клиента или каждого месяца. Именно это делает GROUP BY: он разбивает строки на группы, и агрегатная функция вычисляется внутри каждой группы независимо.
GROUP BY: разбивка строк на группы и агрегация каждой группы
Когда вы добавляете GROUP BY column к запросу с агрегатной функцией, база данных сначала раскладывает все строки по «стопкам» — по уникальным значениям указанного столбца, — а затем применяет функцию к каждой стопке отдельно. Результат содержит ровно одну строку на группу.
Предположим, есть таблица products со столбцами title, category и price. Запрос:
SELECT category, COUNT(*) AS количество
FROM products
GROUP BY category;
вернёт столько строк, сколько уникальных значений есть в столбце category. Для каждого значения COUNT(*) считает, сколько товаров в эту группу попало.
Группировать можно и по нескольким столбцам — тогда группа формируется по уникальной комбинации значений. Запрос:
SELECT category, price, COUNT(*) AS количество
FROM products
GROUP BY category, price;
создаёт отдельную группу для каждой пары (category, price). Если два товара стоят одинаково и лежат в одной категории, они попадут в одну группу; если хотя бы одно значение отличается — в разные.
Теперь о главном ограничении. В SELECT рядом с агрегатными функциями разрешено указывать только те столбцы, которые перечислены в GROUP BY. Смысл простой: каждая группа содержит несколько строк, и у этих строк могут быть разные значения в «лишнем» столбце — базе данных нечего вернуть, кроме произвольного выбора.
SQLite в этой ситуации не выдаёт ошибку: он молча берёт значение из какой-то строки группы (так называемый bare column). Но это значение непредсказуемо — оно может быть первым, последним или любым другим в зависимости от внутреннего порядка хранения. Рассчитывать на него нельзя:
-- Опасный запрос: name не входит в GROUP BY
SELECT category, title, COUNT(*) AS количество
FROM products
GROUP BY category;
Здесь title для каждой категории вернётся непредсказуемым. Исправление зависит от вопроса. Добавьте столбец в GROUP BY, если он задаёт зерно результата; применяйте MIN или MAX только когда действительно нужен минимум или максимум; иначе уберите столбец из SELECT либо перестройте запрос.

