Как посчитать конверсию в SQL
Формула конверсии
Базовая формула:
Конверсия = (Пользователи, выполнившие действие) / (Все пользователи) × 100%Или в SQL:
SELECT
COUNT(DISTINCT user_id) FILTER (WHERE action = 'purchase') * 100.0 /
COUNT(DISTINCT user_id) AS conversion_pct
FROM events;Вариант 1: через FILTER (PostgreSQL)
SELECT
COUNT(*) FILTER (WHERE event = 'purchase') * 100.0 /
COUNT(*) FILTER (WHERE event = 'landing') AS conversion
FROM events;Чище читается.
Вариант 2: через CASE WHEN
Универсально для всех СУБД:
SELECT
SUM(CASE WHEN event = 'purchase' THEN 1 ELSE 0 END) * 100.0 /
SUM(CASE WHEN event = 'landing' THEN 1 ELSE 0 END) AS conversion
FROM events;Воронка: конверсия по шагам
Для воронки view → cart → purchase:
WITH user_events AS (
SELECT user_id,
MAX(CASE WHEN event = 'view' THEN 1 ELSE 0 END) AS viewed,
MAX(CASE WHEN event = 'cart' THEN 1 ELSE 0 END) AS carted,
MAX(CASE WHEN event = 'purchase' THEN 1 ELSE 0 END) AS bought
FROM events
GROUP BY user_id
)
SELECT
SUM(viewed) AS step1,
SUM(carted) AS step2,
SUM(bought) AS step3,
SUM(carted) * 100.0 / NULLIF(SUM(viewed), 0) AS v_to_c,
SUM(bought) * 100.0 / NULLIF(SUM(carted), 0) AS c_to_p,
SUM(bought) * 100.0 / NULLIF(SUM(viewed), 0) AS overall
FROM user_events;Важно: NULLIF защищает от деления на 0.
Если хочется сразу закрепить тему на практике — открой тренажёр в Telegram. 10 минут в день — и синтаксис в пальцах.
Конверсия по дням
SELECT
event_time::DATE AS day,
COUNT(DISTINCT user_id) FILTER (WHERE event = 'purchase') * 100.0 /
COUNT(DISTINCT user_id) FILTER (WHERE event = 'landing') AS conv_pct
FROM events
GROUP BY event_time::DATE
ORDER BY day;Конверсия по каналам
SELECT
u.channel,
COUNT(DISTINCT e.user_id) FILTER (WHERE e.event = 'purchase') AS buyers,
COUNT(DISTINCT e.user_id) AS visitors,
COUNT(DISTINCT e.user_id) FILTER (WHERE e.event = 'purchase') * 100.0 /
COUNT(DISTINCT e.user_id) AS conversion
FROM users u
JOIN events e USING (user_id)
GROUP BY u.channel
ORDER BY conversion DESC;Важные нюансы
1. По пользователям или по событиям
-- По пользователям (правильно)
COUNT(DISTINCT user_id) FILTER (WHERE event = 'purchase')
-- По событиям (часто завышает)
COUNT(*) FILTER (WHERE event = 'purchase')Один пользователь может купить несколько раз — если считать по событиям, конверсия может превышать 100%.
2. Временное окно
Без окна — конверсия включает покупки через годы после первого визита. Часто нужно:
-- За сессию
WHERE purchase_time - first_visit_time < INTERVAL '1 hour'
-- За сутки
WHERE purchase_time::DATE = visit_time::DATE3. Последовательность событий
«Купил после view». Используем LAG или JOIN по времени.
WITH seq AS (
SELECT user_id, event,
LAG(event) OVER (PARTITION BY user_id ORDER BY event_time) AS prev
FROM events
)
SELECT COUNT(*) FILTER (WHERE event = 'purchase' AND prev = 'view') AS sequential
FROM seq;Защита от деления на 0
Всегда:
numerator * 100.0 / NULLIF(denominator, 0)NULLIF превращает 0 в NULL, деление на NULL даёт NULL — без ошибки.
Чтобы не только читать теорию, но и решать реальные задачи — загляните в бот Карьерника. Там по каждой теме подборка вопросов с разборами.
Типичные ошибки
1. * 100 / COUNT(*) — integer division
-- ❌ Возвращает 0
COUNT(*) * 100 / COUNT(*)
-- ✅
COUNT(*) * 100.0 / COUNT(*)2. Считать события вместо пользователей
Без DISTINCT пользователи с 10 заказами считаются 10 раз.
3. Забывать временное окно
«Конверсия за весь год» бессмысленна, если средний цикл покупки — день.
4. Не учитывать сегменты
Средняя конверсия 3% может быть: iOS 5%, Android 1%. Сегментация — всегда.
Примеры задач
Задача 1. Конверсия регистрации → первой покупки
SELECT
COUNT(DISTINCT o.user_id) * 100.0 / COUNT(DISTINCT u.user_id) AS conv
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
WHERE u.registered_at >= '2026-04-01';Задача 2. Конверсия в сегментах
SELECT segment,
COUNT(DISTINCT user_id) FILTER (WHERE purchased) * 100.0 /
COUNT(DISTINCT user_id) AS conv
FROM user_stats
GROUP BY segment;Задача 3. Конверсия A/B-групп
SELECT ab_group,
COUNT(DISTINCT user_id) FILTER (WHERE purchased) * 100.0 /
COUNT(DISTINCT user_id) AS conv
FROM ab_test_users
GROUP BY ab_group;Задача 4. Конверсия по месяцам
SELECT DATE_TRUNC('month', visit_time) AS month,
COUNT(DISTINCT user_id) FILTER (WHERE purchased) * 100.0 /
COUNT(DISTINCT user_id) AS conv
FROM sessions
GROUP BY 1 ORDER BY 1;Читайте также
FAQ
Считать CTR или конверсию?
CTR = клики / показы. Конверсия = покупки / посещения. Разные метрики, разные воронки.
Макро или микро конверсия?
Макро — конечное действие (покупка). Микро — промежуточные (регистрация, клик). Показывайте обе.
Как обрабатывать повторные покупки?
Зависит от определения: «конверсия в первую покупку» или «совершил покупку хоть раз». Уточняйте с PM.
Когда конверсия не информативна?
Когда нет сравнения: «конверсия 3%» — это хорошо или плохо? Нужен бенчмарк или тренд.