Как посчитать Sessions в SQL

Закрепи формулу sessions в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать sessions в Telegram

Зачем 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;
Закрепи формулу sessions в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать sessions в Telegram

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 может разбиться по часовому поясу.

Связанные темы

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.