GROUP BY - группировка или источник факапов
Все знают GROUP BY.
Тот самый оператор, который превращает кучу строк в аккуратную табличку с суммами и средними.
Но можно и по-другому взглянуть на GROUP BY
А пока подписывайся на мой канал На связи: SQL Там я публикую посты про особенности и нюансы SQL. Этот канал про то, как не бояться баз данных, понимать, что такое JOIN, GROUP BY и почему NULL ≠ 0. Его я веду с нуля подписчиков. Присоединяйся!
В большинстве случаев GROUP BY используют вместе с агрегирующими функциями SUM, COUNT или AVG.
Но есть и другие возможности использования группировки.
- В качестве изящной замены DISTINCT
SELECT department FROM employees GROUP BY department;
работает так же, как
SELECT DISTINCT department FROM employees; - Группировать можно по выражениям, а не только по столбцам
Например, хочешь посчитать заказы по годам:
SELECT EXTRACT(YEAR FROM created_at) AS year, COUNT(*)
FROM orders
GROUP BY EXTRACT(YEAR FROM created_at);
Или сгруппировать товары по тысячам рублей:
SELECT (price / 1000)::int AS price_group, COUNT(*)
FROM products
GROUP BY (price / 1000)::int; - GROUP BY умеет строить иерархии
ROLLUP, CUBE, GROUPING SETS — три команды богов:
SELECT region, city, SUM(sales)
FROM orders
GROUP BY ROLLUP (region, city);
→ покажет суммы по городам, по регионам и общий итог.
И всё это одним запросом. - NULL — это тоже группа
Если у тебя несколько строк с NULL в поле department, то GROUP BY department соберёт их все в одну группу NULL.
SELECT department, COUNT(*)
FROM employees
GROUP BY department;
Логичней использовать COALESCE, чтобы потом не работать с пустыми строками
SELECT COALESCE(department, 'Unknown') AS department, COUNT(*)
FROM employees
GROUP BY COALESCE(department, 'Unknown'); - SELECT vs GROUP BY — всё, что не агрегат, должно быть в GROUP BY
SELECT department, name, COUNT(*)
FROM employees
GROUP BY department;
Запрос упадёт, потому что name не в агрегате и не в GROUP BY.
В PostgreSQL есть хитрости: можно использовать array_agg(name) или string_agg(name, ', ') - GROUP BY и оконные функции — не конкуренты
GROUP BY сжимает таблицу.
OVER(PARTITION BY) — сохраняет строки, но добавляет агрегат.
SELECT name, department, SUM(salary) OVER (PARTITION BY department) AS dep_total
FROM employees; - SQL сам решает, как группировать
PostgreSQL может выбрать:
HashAggregate — если данных много
Sort + GroupAggregate — если их мало или мало уникальных значений
То есть одна и та же команда GROUP BY под капотом работает по-разному.
Вот почему один и тот же запрос на 10k строк работает мгновенно, а на 10M — вечность.
PostgreSQL не просто тупо группирует строки, а выбирает стратегию (план выполнения) — как именно эту группировку реализовать.
Это можно отследить в EXPLAIN и уже потом контролировать включением/выключением конкретных алгоритмов.
SET enable_hashagg = off;
SET enable_sort = off;
Это полезно для тестирования или отладки - посмотреть, как изменится план.
GROUP BY — это не просто «посчитать среднюю зарплату по отделу».Это мощный инструмент, который может:
- имитировать DISTINCT
- строить иерархические отчёты
- объединяться с оконными функциями
- …и при этом легко устроить тебе день боли, если ты не знаешь, что делаешь 😅
Начать дискуссию