Короткий ответ
Нужно получить значение предыдущего месяца функцией LAG(revenue) OVER (ORDER BY month) и вычислить (revenue - prev_revenue) / prev_revenue * 100, аккуратно обработав случай, когда предыдущего месяца нет или его выручка равна нулю.
Как это работает подробнее
LAG(column, n) возвращает значение этого столбца из строки, которая находится на n позиций раньше в заданном порядке окна (по умолчанию n = 1); для первой строки окна результат LAG — NULL, так как «предыдущей» строки не существует.
Пример кода
question-39.sql
WITH monthly AS (
SELECT DATE_TRUNC('month', order_date) AS month, SUM(amount) AS revenue
FROM orders
GROUP BY 1
), with_previous AS (
SELECT month, revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue
FROM monthly
)
SELECT month, revenue, revenue - prev_revenue AS diff,
ROUND(100.0 * (revenue - prev_revenue) / NULLIF(prev_revenue, 0), 1) AS pct_change
FROM with_previous
ORDER BY month;Что использовать на практике
- перед делением обязательно нужно защититься от деления на ноль или на
NULL— например, черезNULLIF(prev_revenue, 0), иначе запрос либо упадёт с ошибкой деления на ноль, либо (при NULL в знаменателе) просто тихо вернётNULL. Также важно убедиться, что ряд месяцев не содержит пропущенных месяцев — иначеLAG«перепрыгнет» через отсутствующий месяц, не заметив разрыва. Проверка: для второго месяца в выборке вручную вычислить(revenue2 - revenue1) / revenue1 * 100и сравнить сpct_change.
Подводные камни
- не проверяют наличие пропущенных периодов в данных (например, если в каком-то месяце не было продаж и строка отсутствует) — тогда
LAGсравнит не соседние, а произвольно удалённые по времени месяцы, и расчёт % роста будет формально верным, но содержательно обманчивым.