Сессии пользователя в SQL: как собрать sessionization из событий
Пошаговый разбор sessionization в SQL: таймаут, LAG, флаг новой сессии, накопительная сумма и проверка активных сессий.
Содержание статьи
Сессия редко приходит из продукта готовой. Для анализа пути пользователя нужно определить, когда новая группа событий начинается после паузы, и не выдать технический таймаут за реальный уход. Как восстановить сессии, если в трекинге есть только user_id и время события?
Рабочий вопрос
Как восстановить сессии, если в трекинге есть только user_id и время события?
Сессия редко приходит из продукта готовой. Для анализа пути пользователя нужно определить, когда новая группа событий начинается после паузы, и не выдать технический таймаут за реальный уход.
Для игры 30 минут может быть слишком коротким окном, а для checkout — слишком длинным. Поэтому таймаут нужно проверить по распределению пауз и действию, которое команда хочет понять.
Определение и grain
Сырая строка — событие. После разметки добавляется session_number, но grain не меняется. Для отчёта по сессиям нужно отдельно агрегировать до user_id × session_id.
Отсортируй события пользователя, посчитай LAG времени, поставь флаг при первом событии или большой паузе, затем пронумеруй сессии накопительной суммой.
Если показатель строится по событиям, отдельно проверь повторную отправку, идентичность пользователя и часовой пояс. Если данные приходят из бизнес-системы, сопоставь техническое поле с тем, как команда реально принимает решение.
вопрос → объект → период → расчёт → проверка → решениеЛюбой пропущенный шаг может изменить смысл итогового числа.
Разбор примера
Для игры 30 минут может быть слишком коротким окном, а для checkout — слишком длинным. Поэтому таймаут нужно проверить по распределению пауз и действию, которое команда хочет понять.
Не копируй пример в рабочий отчёт вслепую. Сначала замени названия таблиц и полей, проверь кардинальность связей и запиши, какие строки должны попасть в результат. Хорошая адаптация сохраняет логику, но делает допущения видимыми.
| Шаг | Поле | Результат |
|---|---|---|
| Сортировка | user_id, event_at | последовательность |
| Пауза | event_at - lag_at | минуты бездействия |
| Новая сессия | gap >= timeout | 0/1 |
| Номер | sum(new_session) | session_id |
WITH ordered AS (\n SELECT e.*, LAG(event_at) OVER (PARTITION BY user_id ORDER BY event_at) AS previous_at\n FROM events AS e\n), flagged AS (\n SELECT *, CASE WHEN previous_at IS NULL OR event_at - previous_at >= INTERVAL '30 minutes' THEN 1 ELSE 0 END AS new_session\n FROM ordered\n)\nSELECT *, SUM(new_session) OVER (PARTITION BY user_id ORDER BY event_at) AS session_number\nFROM flagged;Где расчёт ломается
Ошибки возникают из-за одинаковых timestamp, UTC против локального времени, событий без user_id и расчёта паузы после фильтрации важных событий.
Отдельно протестируй пустой результат, NULL, дубль, граничную дату и объект без связанной записи. Эти случаи не являются редкими исключениями: именно они чаще всего превращают рабочий показатель в красивую, но неверную цифру.
- Не смешивай зрелые и незрелые периоды без отдельной подписи.
- Не заменяй проверку качества тем, что запрос просто выполнился.
- Не делай причинный вывод из одного разреза.
Что показывает график
График здесь нужен не для украшения статьи. Он помогает увидеть форму сигнала: тренд, разрыв между сегментами, хвост распределения или этап, на котором теряется объект. Сначала прочитай оси и единицы, затем сравни с таблицей и только после этого формулируй вывод.
На визуализации «Разрывы событий формируют сессии» сравнивай не только максимальное значение. Проверь, одинаковы ли базы, не скрыта ли неполная дата и не меняется ли знаменатель от категории к категории.
Условный пример: одна длинная пауза создаёт новый блок событий.
Проверь на своих данных
Сделай маленький набор из одного пользователя с паузами 5, 29, 30 и 31 минуту. Проверь, как меняется число сессий при разных правилах границы.
После упражнения напиши вывод в двух версиях. Первая — техническая: какие строки и условия дали результат. Вторая — для команды: что изменилось, насколько это надёжно и какое действие стоит проверить. Если эти версии противоречат друг другу, вернись к определению показателя.
- Собери контрольный пример из пяти-десяти строк.
- Сверь итог с независимым способом расчёта.
- Проверь хотя бы один крайний случай.
- Зафиксируй дату, версию схемы и ограничение.
Решение и продолжение
Используй sessionization для анализа последовательности, но не называй число сессий вовлечённостью без проверки качества идентификатора и таймаута.
Сильный аналитический материал не обещает абсолютной уверенности. Он показывает, как получить число, где оно может ошибиться и какое решение можно принять уже сейчас. Такой формат хорошо переносится в дашборд, SQL-тренажёр, собеседование и рабочее обсуждение с командой.
Материалы по теме

JSONB в PostgreSQL: операторы, типы и запросы к свойствам событий
Как читать JSONB в аналитике: операторы ->, ->>, @> и ?, приведение типов, три вида пропущенного значения, разворачивание массивов и индексы под реальные запросы.

Как посчитать воронку в SQL: шаги, конверсия и drop-off
Пошагово собираем продуктовую воронку в SQL: считаем пользователей на каждом этапе, выбираем знаменатель конверсии и находим место наибольшего отвала.

Оконные функции SQL: ROW_NUMBER, LAG и накопительный итог
Как работают оконные функции SQL на задачах аналитика: найти первое событие пользователя, сравнить день с предыдущим и посчитать накопительную выручку без потери строк.