Отчёты любят два вопроса: «сколько набежало к этому дню» и «насколько изменилось по сравнению с прошлым разом». Первый называют нарастающим итогом, второй разницей соседних строк. Оба решаются оконными функциями в одну строку, и на собеседовании проверяют, знаешь ли ты, как это сделать, и понимаешь ли ловушку с одинаковыми значениями.
Как спрашивают
Посчитай выручку нарастающим итогом по датам заказов. А теперь для каждого заказа покажи, на сколько он больше или меньше предыдущего.
Уточнение для сильных кандидатов: «Что изменится, если у двух заказов одна и та же дата?»
Короткий ответ
Нарастающий итог это SUM(amount) OVER (ORDER BY created_at): окно с сортировкой суммирует от первой строки до текущей. Для отдельного итога по каждому клиенту добавляю PARTITION BY customer_id. Разница с предыдущей строкой это amount - LAG(amount) OVER (ORDER BY created_at). Ловушка: по умолчанию рамка окна RANGE, и строки с одинаковым значением сортировки получают одинаковый итог. Если нужен счёт строго построчно, пишу ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW или добавляю уникальную колонку в ORDER BY.
Разбор: копилка
Представь копилку, в которую ты каждый день кладёшь деньги и записываешь в тетрадь две вещи: сколько положил сегодня и сколько всего лежит в копилке. Вторая колонка и есть нарастающий итог: вчерашнее «всего» плюс сегодняшний взнос.
В SQL эту вторую колонку считает агрегат с окном. Обычный SUM с GROUP BY схлопывает строки в одну, а SUM ... OVER оставляет каждую строку на месте и дописывает к ней сумму по её окну, как мы видели в уроке про LAG, LEAD и SUM OVER.
SELECT id, created_at, amount,
sum(amount) OVER (ORDER BY created_at) AS running
FROM orders
ORDER BY created_at;
id | created_at | amount | running
---+------------+--------+--------
1 | 2026-01-05 | 5000 | 5000
2 | 2026-01-20 | 3000 | 8000
3 | 2026-02-02 | 7000 | 15000
4 | 2026-02-15 | 3000 | 18000
5 | 2026-03-01 | 1200 | 19200
ORDER BY внутри окна превращает сумму «по всей таблице» в сумму «от начала до этой строки». Без него sum(amount) OVER () вернул бы 19200 в каждой строке.
Итог по каждому клиенту
Если нужна отдельная копилка у каждого клиента, добавляем PARTITION BY. Счёт начинается заново в каждой группе:
SELECT id, customer_id, created_at, amount,
sum(amount) OVER (PARTITION BY customer_id ORDER BY created_at) AS running
FROM orders
ORDER BY customer_id, created_at;
id | customer_id | created_at | amount | running
---+-------------+------------+--------+--------
1 | 1 | 2026-01-05 | 5000 | 5000
2 | 1 | 2026-01-20 | 3000 | 8000
4 | 1 | 2026-02-15 | 3000 | 11000
3 | 2 | 2026-02-02 | 7000 | 7000
5 | 3 | 2026-03-01 | 1200 | 1200
дальше после входа
Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.
Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.