Почему нужно использовать функцию LEAD для поиска будущих значений по id в SQL
Что проверяют
Владеете ли вы оконными функциями и понимаете ли роль PARTITION BY. Слова «по id» в вопросе это подсказка: строки надо смотреть внутри своей сущности, а не подряд по всей таблице. Заодно проверяют, знаете ли альтернативу и почему она хуже.
Как отвечать
LEAD возвращает значение указанного выражения из следующей строки окна, LAG из предыдущей. Окно задаётся разбиением и сортировкой:
select
user_id,
event_ts,
lead(event_ts) over (partition by user_id order by event_ts) as next_ts
from eventsБез оконной функции то же самое пришлось бы писать коррелированным подзапросом «минимальная дата, которая больше текущей» или соединением таблицы с собой. Такой запрос длиннее, сложнее читается и обычно дороже: оконная функция проходит отсортированный набор один раз.
Скажите, для чего это нужно на практике: интервал между событиями через next_ts - event_ts, поиск разрывов и нарезка на сессии, следующее действие пользователя, дошёл ли он до очередного шага воронки, сравнение значения с будущим периодом.
Полезные детали, которые отличают уверенный ответ. Второй аргумент задаёт смещение, третий значение вместо NULL на границе окна: lead(x, 1, 0). Фильтровать по результату в том же запросе нельзя, оконные функции считаются после where, поэтому условие на next_ts выносится в подзапрос или CTE.
Где спотыкаются
Пишут окно без разбиения. Формально запрос работает, но следующее значение приезжает от соседней сущности, и на маленькой выборке это незаметно. В отчёте появляются тихо неверные цифры, которые никто не перепроверяет, потому что ошибки нет.
Сортируют по неуникальному ключу. Если у двух строк одинаковая метка времени, порядок между ними не определён, и LEAD вернёт то одну, то другую от запуска к запуску. Спасает второй ключ сортировки, например идентификатор строки.
Путают поведение с рамкой окна. LEAD и LAG не подчиняются указанию rows или range, попытка ограничить рамку на них не влияет. Зато на first_value и last_value влияет, и именно забытая рамка даёт классический сюрприз, когда last_value возвращает текущую строку вместо последней в разделе.