GROUP BY vs PARTITION BY — в чём разница

Проверь себя · 1/3разбор после ответа
Запрос 1: 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...
Типичная задача Подсчёт метрик Сравнение строки с группой
Прокачай SQL для собеса
500+ задач по SQL: оконные функции, JOIN, CTE — с разбором каждой
Тренировать SQL в Telegram

Когда использовать 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.