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

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

Зачем customer journey

Customer journey — упорядоченная последовательность touchpoints, через которые юзер прошёл до конверсии (или отвалился). В SQL обычно сводят к строке вида landing → blog → pricing → checkout. Используется для атрибуции, оптимизации воронки, поиска «странных» сценариев типа pricing → support → refund.

Структура данных

Минимум — user_id, event_name, event_timestamp. Иногда channel (email, organic, paid). Один юзер = много строк, упорядоченных по времени:

user_id | event_timestamp     | event_name | channel
42      | 2026-05-01 10:00:00 | landing    | organic
42      | 2026-05-01 10:03:00 | blog       | direct
42      | 2026-05-02 14:30:00 | pricing    | direct
42      | 2026-05-02 14:35:00 | checkout   | direct
42      | 2026-05-02 14:38:00 | purchase   | -

Путь до конверсии

Строим путь как строку через STRING_AGG:

WITH user_events AS (
    SELECT
        user_id,
        event_timestamp,
        event_name,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_timestamp) AS event_rank
    FROM events
    WHERE event_timestamp >= CURRENT_DATE - INTERVAL '30 days'
),
converted AS (
    SELECT DISTINCT user_id
    FROM user_events
    WHERE event_name = 'purchase'
),
journey AS (
    SELECT
        ue.user_id,
        STRING_AGG(ue.event_name, ' -> ' ORDER BY ue.event_rank) AS path,
        COUNT(*) AS path_length,
        MAX(ue.event_timestamp) - MIN(ue.event_timestamp) AS journey_duration
    FROM user_events ue
    WHERE ue.user_id IN (SELECT user_id FROM converted)
    GROUP BY ue.user_id
)
SELECT path, COUNT(*) AS users
FROM journey
GROUP BY path
ORDER BY users DESC
LIMIT 20;

Топ-путь обычно простой: landing → pricing → checkout → purchase. Длинные пути часто означают confusion.

Длина пути и время

WITH journey AS (
    SELECT
        user_id,
        COUNT(*) AS path_length,
        EXTRACT(EPOCH FROM (MAX(event_timestamp) - MIN(event_timestamp))) / 3600 AS hours_to_convert
    FROM events
    WHERE user_id IN (SELECT user_id FROM converted)
    GROUP BY user_id
)
SELECT
    path_length,
    COUNT(*) AS users,
    AVG(hours_to_convert) AS avg_hours,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY hours_to_convert) AS median_hours
FROM journey
GROUP BY path_length
ORDER BY path_length;

Аномалии: «путь из 30 шагов» = bot или баг трекера, выкидывайте.

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

Сегментация по типу пути

WITH journey AS (
    SELECT
        user_id,
        STRING_AGG(event_name, ' -> ' ORDER BY event_timestamp) AS path
    FROM events
    GROUP BY user_id
)
SELECT
    CASE
        WHEN path LIKE '%pricing%checkout%' THEN 'direct_intent'
        WHEN path LIKE '%blog%pricing%' THEN 'content_to_paid'
        WHEN path LIKE '%support%' THEN 'pre_sale_question'
        WHEN path LIKE '%trial%' THEN 'trial_user'
        ELSE 'other'
    END AS journey_type,
    COUNT(*) AS users
FROM journey
GROUP BY journey_type
ORDER BY users DESC;

Частые ошибки

Ошибка 1. Не отсортировать события. Без ORDER BY event_timestamp агрегация даёт случайный порядок — путь становится бессмыслицей.

Ошибка 2. Один юзер = много анонимных сессий. До логина у юзера может быть несколько device_id. Не идентифицировав — посчитаете каждую сессию отдельным путём.

Ошибка 3. Сжимать одинаковые последовательные шаги. landing → landing → landing обычно нужно свернуть в landing. LAG + фильтр.

Ошибка 4. STRING_AGG без ORDER BY внутри. В Postgres STRING_AGG(event_name, ' -> ') без ORDER BY даёт недетерминированный порядок.

Ошибка 5. Считать на всех событиях. Если в events 50 типов событий — путь становится мусором. Фильтруйте до значимых (page_view верхне-уровневых, не каждый клик).

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

FAQ

Сколько событий в пути — норма?

Для SaaS — 5–15. Для e-com — 3–8. > 20 — обычно bot или потерянный юзер.

Как обработать множественные конверсии?

Возьмите первую (first conversion) или все и анализируйте отдельно (retention loop).

Что включать в события?

Только значимые page views и actions: landing, blog, pricing, signup, trial_start, checkout, purchase. Не каждый клик.

Как обработать длинные пути?

Сжать одинаковые соседние, обрезать после конверсии, выкинуть юзеров с n > 50 событий.

Customer journey vs funnel?

Funnel — фиксированные шаги. Journey — реальный путь, любой порядок. Funnel — частный случай.