Распределение по группам A/B-теста и метрики
Объединение распределения участников эксперимента с результатами и вычисление метрик для каждого варианта
«Распределение по группам A/B-теста и метрики» — бесплатный урок SQL Interview Prep на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Interview Prep содержит 4 уроков всего.
Что проверяет вопрос об A/B-тестировании
Вопросы об A/B-тестировании проверяют, умеете ли Вы корректно соединять распределение участников эксперимента с результатами и вычислять корректную метрику для каждого варианта.
Проблема почти всегда в соединении: можно посчитать результаты пользователей, которые никогда не участвовали в эксперименте, или дважды посчитать пользователей, которым вариант назначили дважды. Правильно выполните соединение с распределением — и вычисление метрик станет простой арифметикой.
Какие две таблицы Вы получаете
Ожидайте таблицу распределения и таблицу результатов:
assignments(user_id, variant, assigned_at), где вариант — «контрольный» или «тестовый».orders(user_id, order_id, amount, created_at)или обычную таблицу событий.
Распределение — источник истины для определения участников эксперимента. Результаты учитываются только в том случае, если пользователь есть в таблице распределения.
CREATE TABLE assignments (
user_id INT,
variant VARCHAR(20),
assigned_at TIMESTAMP
);
CREATE TABLE orders (
user_id INT,
order_id INT,
amount NUMERIC,
created_at TIMESTAMP
);Начинайте с распределения и выполняйте LEFT JOIN с результатами
Главное правило: начинайте с таблицы распределения и выполняйте LEFT JOIN с результатами. Это сохраняет пользователей, включённых в эксперимент, но не совершивших конверсию, — они нужны для честного знаменателя.
INNER JOIN незаметно исключит пользователей без конверсии и завысит коэффициент конверсии.
SELECT
a.user_id,
a.variant,
o.order_id
FROM assignments a
LEFT JOIN orders o
ON o.user_id = a.user_id;Подсчёт конверсии для каждого варианта
Коэффициент конверсии = пользователи с конверсией / назначенные пользователи для каждого варианта. В числителе считайте уникальных пользователей с конверсией, а в знаменателе — всех назначенных пользователей.
Используйте COUNT(DISTINCT ...) для пользователя из заказа, чтобы пользователь с тремя заказами всё равно считался одним пользователем с конверсией.
SELECT
a.variant,
COUNT(DISTINCT a.user_id) AS assigned,
COUNT(DISTINCT o.user_id) AS converters,
ROUND(100.0 * COUNT(DISTINCT o.user_id)
/ COUNT(DISTINCT a.user_id), 2) AS conv_rate_pct
FROM assignments a
LEFT JOIN orders o ON o.user_id = a.user_id
GROUP BY a.variant;Ловушка двойного распределения
Что произойдёт, если пользователь появится в таблице распределения дважды — по одному разу в каждом варианте? Теперь соединение посчитает его с обеих сторон, и эксперимент окажется искажён.
Интервьюеры специально добавляют такой случай. Защититесь от него: удалите дубликаты в распределении, оставив одному пользователю один вариант, обычно вариант из первого распределения, до выполнения соединения.
WITH dedup AS (
SELECT user_id, variant,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY assigned_at) AS rn
FROM assignments
)
SELECT user_id, variant
FROM dedup
WHERE rn = 1;Учитывайте результаты только после распределения
Заказ, оформленный до распределения пользователя, не может быть вызван экспериментом. Добавьте временное ограничение: результат должен произойти в момент assigned_at или позже.
Поместите это условие в предложение ON соединения LEFT JOIN, чтобы пользователи без конверсии по-прежнему сохранялись.
SELECT
a.variant,
COUNT(DISTINCT a.user_id) AS assigned,
COUNT(DISTINCT o.user_id) AS converters
FROM assignments a
LEFT JOIN orders o
ON o.user_id = a.user_id
AND o.created_at >= a.assigned_at
GROUP BY a.variant;ON и WHERE при соединении с результатами
Это гарантированный дополнительный вопрос. Если перенести o.created_at >= a.assigned_at в WHERE, Вы превратите LEFT JOIN во внутреннее соединение: строки, в которых пользователь ничего не заказал, имеют o.created_at = NULL, предикат получает значение UNKNOWN, и такие строки исчезают.
Оставляйте условия фильтрации результатов в ON, чтобы сохранять пользователей без конверсии в знаменателе.
Метрики выручки для каждого варианта
Помимо конверсии, интервьюеры спрашивают о выручке на пользователя (ARPU) и выручке на пользователя с конверсией. Сначала просуммируйте сумму, затем разделите её на правильный знаменатель.
ARPU рассчитывается по всем назначенным пользователям, а выручка на пользователя с конверсией — только по пользователям, разместившим заказ. Ясно указывайте, какой показатель нужен бизнесу.
SELECT
a.variant,
COUNT(DISTINCT a.user_id) AS assigned,
COALESCE(SUM(o.amount), 0) AS revenue,
ROUND(COALESCE(SUM(o.amount), 0)
/ COUNT(DISTINCT a.user_id), 2) AS arpu
FROM assignments a
LEFT JOIN orders o
ON o.user_id = a.user_id
AND o.created_at >= a.assigned_at
GROUP BY a.variant;Двухуровневый шаблон агрегации
Если метрика — это «среднее число заказов на пользователя», не вычисляйте её за один проход: так Вы смешаете уровни детализации пользователя и заказа. Сначала агрегируйте данные до уровня пользователя, затем усредняйте их по пользователям.
Этот шаблон «сначала по пользователю, затем по варианту» использует правильный уровень детализации и часто помогает отличить сильного кандидата на собеседовании.
WITH per_user AS (
SELECT a.variant, a.user_id,
COUNT(o.order_id) AS orders_cnt
FROM assignments a
LEFT JOIN orders o
ON o.user_id = a.user_id
AND o.created_at >= a.assigned_at
GROUP BY a.variant, a.user_id
)
SELECT variant, ROUND(AVG(orders_cnt), 3) AS avg_orders_per_user
FROM per_user
GROUP BY variant;Полный обоснованный запрос
Объедините всё: оставьте первое распределение, начинайте с таблицы распределения, отфильтруйте результаты по времени в ON и выведите конверсию и ARPU для каждого варианта. Объясняйте назначение каждого ограничения по мере написания запроса.
WITH enrolled AS (
SELECT user_id, variant, assigned_at
FROM (
SELECT user_id, variant, assigned_at,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY assigned_at) AS rn
FROM assignments
) x WHERE rn = 1
)
SELECT
e.variant,
COUNT(DISTINCT e.user_id) AS assigned,
COUNT(DISTINCT o.user_id) AS converters,
ROUND(100.0 * COUNT(DISTINCT o.user_id)
/ COUNT(DISTINCT e.user_id), 2) AS conv_pct,
ROUND(COALESCE(SUM(o.amount),0)
/ COUNT(DISTINCT e.user_id), 2) AS arpu
FROM enrolled e
LEFT JOIN orders o
ON o.user_id = e.user_id
AND o.created_at >= e.assigned_at
GROUP BY e.variant;Проверки, которых ожидают на собеседовании
Прежде чем приводить результаты, проверьте настройки эксперимента:
- Размеры групп вариантов примерно сбалансированы? Разделение 90/10 вместо запланированного 50/50 указывает на ошибку.
- Оказался ли какой-либо пользователь в обоих вариантах? Посчитайте пользователей, которым назначено более одного различного варианта.
- Есть ли распределения, для которых невозможно сформировать окно результатов, например выполненные после отсечения данных?
Если предложить эти проверки без подсказки, это покажет аналитическую зрелость.
SELECT user_id, COUNT(DISTINCT variant) AS variant_count
FROM assignments
GROUP BY user_id
HAVING COUNT(DISTINCT variant) > 1;Быстрая проверка
Вы вычисляете конверсию для каждого варианта, соединяя заказы с таблицей распределения с помощью LEFT JOIN, но помещаете o.created_at >= a.assigned_at в предложение WHERE. Что произойдёт?
Итоги: распределение участников A/B-тестирования и метрики
Теперь у Вас есть обоснованный алгоритм анализа экспериментов:
- Считайте распределение источником истины и выполняйте LEFT JOIN с результатами.
- Удаляйте дубликаты, оставляя одному пользователю один вариант (первое распределение).
- Ограничивайте результаты по времени в предложении ON, а не в WHERE, чтобы сохранять пользователей без конверсии.
- Выбирайте правильный знаменатель для конверсии, ARPU и выручки на пользователя с конверсией.
- Сначала агрегируйте данные на уровне пользователя, когда вычисляете средние показатели на пользователя.
- Выполняйте проверки корректности баланса групп и распределения пользователей между вариантами.
Далее: преобразование этих метрик для вариантов в прирост, статистическую значимость и защитные метрики.
Часто задаваемые вопросы
Урок «Распределение по группам A/B-теста и метрики» бесплатный?
Да — полный текст урока «Распределение по группам A/B-теста и метрики» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Interview Prep, подпишись на CoddyKit PRO. Курс SQL Interview Prep содержит 4 уроков всего.
Чему я научусь в уроке «Распределение по группам A/B-теста и метрики»?
Объединение распределения участников эксперимента с результатами и вычисление метрик для каждого варианта Ты практикуешь SQL Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать SQL Interview Prep?
Предыдущий опыт не требуется. SQL Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Распределение по группам A/B-теста и метрики»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке SQL Interview Prep?
Да. Каждый урок SQL Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Построение многоэтапной воронки
- Упорядоченные события и временные окна
- Распределение по группам A/B-теста и метрики
- Прирост, значимость и контрольные показатели в SQL