Как посчитать time-to-convert в SQL
Содержание:
Зачем time-to-convert
Time-to-convert — время от первого касания до конверсии. У SaaS обычно 7–14 дней между signup и первой оплатой. Если медиана растёт — sales-цикл удлиняется, что-то ломается. Если ниже среднего по каналу — канал «горячий».
База в SQL
Простейший расчёт: signup → first purchase.
WITH first_purchase AS (
SELECT
user_id,
MIN(created_at) AS first_purchase_at
FROM orders
GROUP BY user_id
)
SELECT
u.user_id,
u.created_at AS signup_at,
fp.first_purchase_at,
EXTRACT(EPOCH FROM (fp.first_purchase_at - u.created_at)) / 86400 AS days_to_convert
FROM users u
JOIN first_purchase fp USING (user_id)
WHERE u.created_at >= CURRENT_DATE - INTERVAL '90 days';Медиана и перцентили
Среднее можно искажают whales и outliers (юзер купил через год). Считают перцентили:
SELECT
COUNT(*) AS converted_users,
AVG(days_to_convert) AS avg_days,
PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY days_to_convert) AS p25,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY days_to_convert) AS median,
PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY days_to_convert) AS p75,
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY days_to_convert) AS p95
FROM time_to_convert_per_user;Медиана 3 дня, p95 = 30 дней — большинство быстро, хвост ходит долго.
По когортам и каналам
WITH base AS (
SELECT
u.user_id,
u.utm_source AS channel,
DATE_TRUNC('month', u.created_at)::DATE AS signup_month,
EXTRACT(EPOCH FROM (fp.first_purchase_at - u.created_at)) / 86400 AS days
FROM users u
JOIN first_purchase fp USING (user_id)
WHERE u.created_at >= CURRENT_DATE - INTERVAL '180 days'
)
SELECT
signup_month,
channel,
COUNT(*) AS converted,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY days) AS median_days
FROM base
GROUP BY signup_month, channel
HAVING COUNT(*) >= 30
ORDER BY signup_month, median_days;Сравните organic vs paid: если paid даёт быстрее conversion — стоит увеличивать бюджет.
Censored data
Юзер, который ещё не купил, не должен попасть в среднее (иначе подмёт ниже). Учитывайте «right-censored»:
WITH cohort AS (
SELECT
user_id,
created_at,
(SELECT MIN(created_at) FROM orders WHERE user_id = u.user_id) AS first_purchase_at
FROM users u
WHERE created_at >= '2026-01-01'
),
analysis AS (
SELECT
user_id,
first_purchase_at IS NOT NULL AS converted,
COALESCE(
EXTRACT(EPOCH FROM (first_purchase_at - created_at)) / 86400,
EXTRACT(EPOCH FROM (CURRENT_DATE - created_at)) / 86400
) AS time_observed
FROM cohort
)
SELECT
COUNT(*) FILTER (WHERE converted) AS converted_n,
COUNT(*) FILTER (WHERE NOT converted) AS still_observing_n,
AVG(time_observed) FILTER (WHERE converted) AS avg_among_converted_only
FROM analysis;Для строгого анализа — Kaplan-Meier (survival analysis), но это уже сложнее.
Частые ошибки
Ошибка 1. Average на skewed данных. Распределение time-to-convert log-normal или Weibull. Медиана честнее среднего.
Ошибка 2. Игнорировать censoring. Юзер signup 2 дня назад, не купил — не значит, что time-to-convert = ∞. Это «ещё не наблюдалось».
Ошибка 3. Считать на чёткой INNER JOIN.
users INNER JOIN orders отрезает не-converted — занижает данные. LEFT JOIN + дальнейшая фильтрация.
Ошибка 4. Не нормировать на cohort age. Юзеры из January 2026 имели 4 месяца для конверсии, May 2026 — 13 дней. Сравнение without truncation некорректно.
Ошибка 5. Округлять до дней слишком грубо. Для SaaS с trial и переходом в день 14 — округление до недель утратит сигнал.
Связанные темы
- Как посчитать customer journey в SQL
- Как посчитать time-to-first-purchase в SQL
- Как посчитать time-to-value в SQL
- Как посчитать conversion-window в SQL
FAQ
Среднее или медиана?
Медиана. Среднее искажают outliers.
Что делать с не-конвертнувшими?
Survival analysis (Kaplan-Meier). Или анализ только на закрытых cohort'ах.
Time-to-convert vs sales cycle?
Sales cycle — обычно sales-team метрика (lead → deal). Time-to-convert — продуктовая (signup → first purchase).
Какая медиана нормальная?
SaaS B2C 1–7 дней, B2B 30–90 дней, e-com 0–3 дня.
По каналу разные медианы — что делать?
Канал с быстрой конверсией — масштабируем. Канал с долгой — переоцениваем CAC.