Как посчитать Sessions в SQL
Содержание:
Зачем Sessions
DAU считают активных за день, но один юзер может зайти 10 раз и закрыть. Это не engagement — это поломанный onboarding. Sessions показывают, сколько отдельных «визитов» юзер делает.
Что такое Session
Session — последовательность событий одного пользователя без пауз длиннее session_timeout (обычно 30 минут).
Sessionization через SQL
Данные: events(user_id, event_time).
WITH events_with_gap AS (
SELECT
user_id,
event_time,
LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_event,
CASE
WHEN event_time - LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time)
> INTERVAL '30 minutes'
OR LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) IS NULL
THEN 1
ELSE 0
END AS is_new_session
FROM events
),
sessions AS (
SELECT
user_id,
event_time,
SUM(is_new_session) OVER (PARTITION BY user_id ORDER BY event_time) AS session_num
FROM events_with_gap
)
SELECT
user_id,
session_num,
MIN(event_time) AS session_start,
MAX(event_time) AS session_end,
COUNT(*) AS events_count,
EXTRACT(EPOCH FROM (MAX(event_time) - MIN(event_time))) AS duration_sec
FROM sessions
GROUP BY user_id, session_num
ORDER BY user_id, session_num;Логика: если gap > 30 минут — новая сессия. Кумулятивная сумма флагов даёт session_id.
Sessions per user
WITH sessions AS (
SELECT user_id, COUNT(DISTINCT session_num) AS sessions
FROM (
SELECT
user_id,
SUM(is_new_session) OVER (PARTITION BY user_id ORDER BY event_time) AS session_num
FROM (
SELECT
user_id,
event_time,
CASE
WHEN event_time - LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time)
> INTERVAL '30 minutes'
OR LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) IS NULL
THEN 1 ELSE 0
END AS is_new_session
FROM events
WHERE event_time >= CURRENT_DATE - INTERVAL '30 days'
) x
) y
GROUP BY user_id
)
SELECT
AVG(sessions) AS avg_sessions_per_user,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY sessions) AS median_sessions
FROM sessions;Avg session duration
-- Используя CTE sessions из примера выше
SELECT
AVG(duration_sec) AS avg_duration_sec,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration_sec) AS median_duration_sec
FROM sessions
WHERE duration_sec > 0;Частые ошибки
Ошибка 1. Timeout 30 минут — это не закон. B2B SaaS — 30 минут ок. Стримы — 4+ часа. Игры — 60 минут. Договоритесь.
Ошибка 2. Cross-device sessions. Юзер начал на телефоне, продолжил на ПК — две разные сессии или одна? Зависит от unification user_id.
Ошибка 3. Не считать singleton-сессии. Юзер зашёл, 1 event, ушёл. Это валидная сессия с duration = 0. Не выбрасывайте.
Ошибка 4. NULL-event_time. Очистите перед sessionization.
Ошибка 5. Игнорировать timezone. Сессия в полночь UTC может разбиться по часовому поясу.
Связанные темы
- Как посчитать DAU в SQL
- Как посчитать MAU в SQL
- Как посчитать bounce rate в SQL
- Оконные функции в SQL — шпаргалка
FAQ
Какой timeout считается стандартом?
Google Analytics default — 30 минут. Большинство аналитиков следуют.
Sessions per user — норма?
Зависит от продукта. Соцсеть — 5-15/нед. SaaS — 2-5/нед. E-com — 1-3/мес.
Avg или median duration?
Обе. Avg чувствителен к outliers (юзер не закрыл вкладку).
Cross-device — как считать?
Только если есть unified user_id (логин). Иначе устройства разные.
Singleton-сессии — выбрасывать?
Нет, считайте отдельно. Высокий % singleton — bounce rate.