Как посчитать Free-to-Paid в SQL
payments(amount), в части строк amount равно NULL. Какой запрос посчитает количество всех строк в таблице, включая строки с NULL в столбце amount?Содержание:
Зачем Free-to-Paid
Основа любой freemium-воронки: регистрация → бесплатное использование → конверсия в платящего. Без здоровой Free-to-Paid масштабироваться невозможно. Slack, Dropbox, Notion — все построены на этом.
Формула
Free-to-Paid = users_paid_within_X / total_signups × 100%Окно обычно 30/60/90 дней после регистрации.
Базовый расчёт
WITH signups AS (
SELECT user_id, signed_up_at
FROM users
WHERE signed_up_at BETWEEN '2026-01-01' AND '2026-02-01'
),
first_paid AS (
SELECT
user_id,
MIN(created_at) AS first_payment_at
FROM transactions
WHERE status = 'paid'
AND amount > 0
GROUP BY user_id
)
SELECT
COUNT(s.user_id) AS signups,
COUNT(p.user_id) AS converted_to_paid,
COUNT(p.user_id) FILTER (WHERE p.first_payment_at <= s.signed_up_at + INTERVAL '30 days') AS converted_30d,
COUNT(p.user_id) FILTER (WHERE p.first_payment_at <= s.signed_up_at + INTERVAL '90 days') AS converted_90d,
COUNT(p.user_id) FILTER (WHERE p.first_payment_at <= s.signed_up_at + INTERVAL '30 days')::NUMERIC * 100 / COUNT(s.user_id) AS free_to_paid_30d_pct
FROM signups s
LEFT JOIN first_paid p ON p.user_id = s.user_id;По когортам
WITH cohort AS (
SELECT
DATE_TRUNC('week', signed_up_at) AS cohort_week,
user_id
FROM users
WHERE signed_up_at >= CURRENT_DATE - INTERVAL '12 weeks'
),
paid AS (
SELECT user_id, MIN(created_at) AS paid_at
FROM transactions
WHERE status = 'paid'
GROUP BY user_id
)
SELECT
c.cohort_week,
COUNT(c.user_id) AS signups,
COUNT(p.user_id) AS paid,
COUNT(p.user_id)::NUMERIC * 100 / NULLIF(COUNT(c.user_id), 0) AS conversion_pct
FROM cohort c
LEFT JOIN paid p ON p.user_id = c.user_id
AND p.paid_at - (
SELECT signed_up_at FROM users WHERE user_id = c.user_id
) <= INTERVAL '30 days'
GROUP BY c.cohort_week
ORDER BY c.cohort_week;Время до конверсии
WITH conversions AS (
SELECT
u.user_id,
u.signed_up_at,
MIN(t.created_at) AS first_paid_at,
EXTRACT(EPOCH FROM (MIN(t.created_at) - u.signed_up_at)) / 86400 AS days_to_pay
FROM users u
JOIN transactions t ON t.user_id = u.user_id
WHERE t.status = 'paid'
AND u.signed_up_at >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY u.user_id, u.signed_up_at
)
SELECT
PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY days_to_pay) AS p25_days,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY days_to_pay) AS median_days,
PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY days_to_pay) AS p75_days,
AVG(days_to_pay) AS avg_days
FROM conversions;Частые ошибки
Ошибка 1. Снимок вместо когорты. «Free-to-Paid 10%» сегодня — включая тех, кто зарегистрировался 5 лет назад? Считайте по когортам.
Ошибка 2. Выбор окна. 30 и 90 дней дают очень разные цифры. Длинный хвост конверсий (оплата спустя полгода) — обычное дело.
Ошибка 3. Триал против бесплатного тарифа. Trial-to-Paid и Free-to-Paid — это разные воронки. Не путайте их.
Ошибка 4. Несколько ценовых точек. Оплатил пробную подписку за $1 — считать ли его платящим? Заранее договоритесь, что такое «платящий».
Ошибка 5. Возвраты. Оплатил, а на второй неделе вернул деньги. Отфильтруйте такие случаи.
Связанные темы
- Как посчитать trial conversion в SQL
- Как посчитать paying users в SQL
- Как посчитать activation rate в SQL
- Кейс: free-to-paid упал — как диагностировать
FAQ
Какой Free-to-Paid считается нормальным?
В B2C-SaaS 2-5% считается средним показателем, 5-10% — хорошим. В B2B-SaaS диапазон выше — 5-15%. У Slack конверсия доходит до ~30%, но это на уровне рабочих пространств, а не отдельных пользователей.
Окно 30 или 90 дней?
30 дней дают ранний сигнал — быстро видно, работает ли активация. 90 дней ближе к реальности для продуктов с длинным циклом принятия решения. Для оперативного мониторинга берут 30, для честной итоговой цифры — 90.
Free-to-Paid падает — что делать?
Пройдитесь по воронке по шагам: качество регистраций, активация, время до первой ценности, трение на пейволе. Обычно проседает один конкретный этап — его и нужно искать, а не крутить всё сразу.
Считать конверсию от активированных пользователей?
Конверсия «активированный → платящий» информативнее, чем «регистрация → платящий»: она убирает из знаменателя тех, кто вообще не начал пользоваться продуктом. Отфильтруйте выборку до активированных пользователей, и цифра станет честнее.
Что делать с длинным хвостом конверсий?
В SaaS часть пользователей конвертируется спустя год и больше после регистрации. Чтобы не терять их из виду, отслеживайте полную кривую конверсии за всё время жизни когорты, а не только за первое окно.