Условие
Какие функции SQL умеют возвращать значения из предыдущих/последующих строк для заданной строки таблицы?
Решение
LAG() и LEAD() — оконные функции для смещения по упорядоченному окну.
SELECT
user_id,
event_date,
revenue,
LAG(revenue) OVER (PARTITION BY user_id ORDER BY event_date) AS prev_day,
LEAD(revenue) OVER (PARTITION BY user_id ORDER BY event_date) AS next_day
FROM revenue;LAG(col, n, default)— значение из строки наnпозиций назад в окне (по умолчаниюn=1, для первой строки —defaultилиNULL).LEAD(col, n, default)— то же, но вперёд.
Зачем это нужно
- Разница с предыдущим днём:
revenue - LAG(revenue) OVER (...). - Доля от предыдущего:
revenue / NULLIF(LAG(revenue) OVER (...), 0). - Длительность сессии:
next_event_ts - LAG(event_ts) OVER (...). - Разрыв в стрике: вычислить gaps в последовательности дат через
event_date - LAG(event_date).
Родственные функции
| Функция | Смысл |
|---|---|
FIRST_VALUE(col) |
Первое значение в окне |
LAST_VALUE(col) |
Последнее значение в окне (но осторожно с фреймом — по умолчанию ограничен текущей строкой) |
NTH_VALUE(col, n) |
n-е значение |
ROW_NUMBER() / RANK() / DENSE_RANK() |
Нумерация строк |
SUM/AVG OVER (...) |
Бегущие агрегаты |
Подводные камни
- Забыть
PARTITION BY— окно станет глобальным, и предыдущей строкой для пользователяBокажется последняя строка пользователяA. Считать «изменение от предыдущего дня этого пользователя» — нуженPARTITION BY user_id. - Забыть
ORDER BYвнутриOVER— некоторые СУБД дадут ошибку, другие — недетерминированный результат. LAST_VALUEбез расширения фрейма возвращает текущую строку (потому что фрейм по умолчаниюRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW). ЛечитсяRANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.
Эталонный ответ
LAG и LEAD (оконные функции).