GROUP BY vs PARTITION BY — в чём разница
SELECT * FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE o.amount > 1000. Запрос 2: SELECT * FROM users u LEFT JOIN orders o ON o.user_id = u.id AND o.amount > 1000. Чем отличаются результаты?Коротко
GROUP BY схлопывает строки в группы — на выходе одна строка на группу. PARTITION BY разбивает данные на группы для оконных функций, но сохраняет все исходные строки.
Как работает GROUP BY
GROUP BY объединяет строки с одинаковыми значениями в группы. Для каждой группы можно вычислить агрегат (COUNT, SUM, AVG), но увидеть отдельные строки внутри группы нельзя.
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department;Результат: одна строка на отдел. Информация об отдельных сотрудниках потеряна.
| department | avg_salary |
|---|---|
| Sales | 85000 |
| Engineering | 120000 |
Как работает PARTITION BY
PARTITION BY используется внутри оконных функций (OVER). Он разбивает данные на группы для вычислений, но не схлопывает строки — каждая исходная строка остаётся в результате.
SELECT name, department, salary,
AVG(salary) OVER (PARTITION BY department) AS avg_salary
FROM employees;Результат: все сотрудники на месте, плюс у каждого — средняя зарплата по его отделу.
| name | department | salary | avg_salary |
|---|---|---|---|
| Анна | Sales | 90000 | 85000 |
| Борис | Sales | 80000 | 85000 |
| Вика | Engineering | 130000 | 120000 |
| Глеб | Engineering | 110000 | 120000 |
Ключевые отличия
| GROUP BY | PARTITION BY | |
|---|---|---|
| Количество строк | Уменьшается (одна на группу) | Сохраняется |
| Где используется | В основном запросе | Внутри OVER() |
| Доступ к отдельным строкам | Нет | Да |
| Можно комбинировать | С HAVING | С ROW_NUMBER, RANK, LAG, LEAD, SUM... |
| Типичная задача | Подсчёт метрик | Сравнение строки с группой |
Когда использовать GROUP BY
Когда нужен агрегированный отчёт — итоги по группам без деталей:
-- Выручка по месяцам
SELECT DATE_TRUNC('month', order_date) AS month,
SUM(amount) AS total_revenue
FROM orders
GROUP BY DATE_TRUNC('month', order_date);Когда использовать PARTITION BY
Когда нужно сравнить строку с её группой или вычислить что-то в контексте группы, сохраняя все строки:
-- Доля каждого заказа от общей выручки клиента
SELECT order_id, customer_id, amount,
amount * 100.0 / SUM(amount) OVER (PARTITION BY customer_id) AS pct_of_total
FROM orders;-- Разница с предыдущим значением (динамика)
SELECT DATE, revenue,
revenue - LAG(revenue) OVER (PARTITION BY product_id ORDER BY DATE) AS change
FROM daily_revenue;Комбинация GROUP BY + оконные функции
Можно использовать оба подхода в одном запросе. Сначала GROUP BY агрегирует, потом оконная функция работает над результатом:
-- Выручка по месяцам + доля от годовой
SELECT month,
monthly_revenue,
monthly_revenue * 100.0 / SUM(monthly_revenue) OVER () AS pct_of_year
FROM (
SELECT DATE_TRUNC('month', order_date) AS month,
SUM(amount) AS monthly_revenue
FROM orders
WHERE order_date >= '2025-01-01'
GROUP BY DATE_TRUNC('month', order_date)
) t;На собеседовании часто просят добавить колонку «доля от общего» или «сравнение с предыдущим периодом» к уже сгруппированным данным. Это классический паттерн GROUP BY + оконная функция.
Вопросы с собеседований
- «Можно ли использовать PARTITION BY без ORDER BY?» — Да. ORDER BY внутри OVER нужен только для функций, зависящих от порядка (ROW_NUMBER, LAG, LEAD, накопительный SUM).
- «Что будет, если не указать PARTITION BY?» — Оконная функция будет работать по всем строкам как по одной группе.
- «Как посчитать процент от общего без подзапроса?» — SUM(amount) OVER () даст общую сумму, делите на неё.
FAQ
Можно ли заменить GROUP BY на PARTITION BY?
Нет. GROUP BY схлопывает данные — это другой результат. Если нужна одна строка на группу, используйте GROUP BY. Если нужны все строки плюс агрегат — PARTITION BY.
PARTITION BY медленнее GROUP BY?
Не обязательно. PARTITION BY обрабатывает больше строк (все, а не по одной на группу), но не требует отдельного прохода. На практике скорость зависит от объёма данных и индексов.
Можно ли использовать несколько PARTITION BY в одном запросе?
Да, в каждой оконной функции может быть свой PARTITION BY. Например: AVG(salary) OVER (PARTITION BY department) и AVG(salary) OVER (PARTITION BY city) в одном SELECT.