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
строить иерархические отчёты
объединяться с оконными функциями
…и при этом легко устроить тебе день боли, если ты не знаешь, что делаешь 😅
