Задачи на оконные функции SQL
Оконные функции·junior

Выручка по месяцам и рост к предыдущему месяцу

Классика собеседований на аналитика: месячная динамика с ростом в процентах. Простая по идее, но в ней спрятаны три аккуратности, на которых валятся.

Задача

Есть таблица заказов orders(order_id, amount, created_at). Посчитай выручку по месяцам и рост к предыдущему месяцу в процентах.

Схема данных

CREATE TABLE orders (
  order_id int,
  amount numeric,
  created_at timestamp
);

Заказы разбросаны по нескольким месяцам — их нужно сгруппировать по месяцу и посчитать сумму, а потом сравнить каждый месяц с предыдущим.

Решение

WITH monthly AS (
  SELECT date_trunc('month', created_at) AS month, SUM(amount) AS revenue
  FROM orders
  GROUP BY 1
)
SELECT to_char(month, 'YYYY-MM') AS month, revenue,
       ROUND(100.0 * (revenue - LAG(revenue) OVER (ORDER BY month))
             / NULLIF(LAG(revenue) OVER (ORDER BY month), 0), 1) AS growth_pct
FROM monthly
ORDER BY month;

Разбор

Сначала сводим заказы к месячной выручке. date_trunc('month', created_at) обрезает дату до первого числа месяца — так все заказы одного месяца попадают в одну группу, и SUM(amount) даёт месячную выручку. Во внешнем запросе результат оборачиваем в to_char(month, 'YYYY-MM') — это только для читаемого вывода, а группировка, LAG и ORDER BY внутри работают с исходной датой.

Дальше рост считает оконная функция LAG(revenue) OVER (ORDER BY month): она подтягивает выручку предыдущего по порядку месяца. Формула роста — (текущий − предыдущий) / предыдущий, в процентах.

И вот три аккуратности, ради которых задачу и дают:

  • 100.0, а не 100. Если умножать на целое и делить numeric на numeric, в некоторых движках получишь целочисленное деление и потеряешь дробную часть. 100.0 гарантирует дробную арифметику.
  • NULLIF(..., 0) в знаменателе. Если у предыдущего месяца выручка вдруг ноль — деление упадёт. NULLIF превращает ноль в NULL, и вместо ошибки получаем NULL.
  • Первый месяц. У него нет предыдущего, LAG вернёт NULL, и рост честно окажется NULL — сравнивать не с чем.

ORDER BY month во внешнем запросе выводит месяцы по порядку. Про то, как окно смотрит в соседнюю строку, — в вопросе что такое оконные функции.

Ожидаемый результат

month | revenue | growth_pct
--- | --- | ---
2026-01 | 1000 | NULL
2026-02 | 1500 | 50.0
2026-03 | 1200 | -20.0
2026-04 | 1800 | 50.0
2026-05 | 900 | -50.0

Прорешай такие задачи сам

Читать разбор полезно, а настоящая прокачка — самому писать запросы и получать обратную связь. В симуляторе живая база и проверка запросов, лёгкий режим — бесплатно.

Прорешать в бесплатной песочнице

Смежные задачи

Вопросы по теме