Как посчитать Upsell Rate в SQL

Проверь себя · 1/3разбор после ответа
Нужно пронумеровать заказы каждого пользователя по времени покупки, начиная с 1, чтобы найти его 1-й, 2-й, 3-й заказ. Какое выражение подходит?

Зачем Upsell Rate

Upsell Rate — это доля клиентов, которые перешли на более дорогой тариф и стали платить больше. Метрика важна, потому что расширить существующего клиента почти всегда дешевле, чем привести нового: он уже в продукте, уже платит, ему не нужно заново продавать ценность. Здоровый SaaS обычно апсейлит 10–20% базы в год, и именно апселл двигает вверх Net Revenue Retention (NRR) — метрику, на которую смотрит совет директоров.

Для аналитика подписочного продукта уметь посчитать upsell в SQL — базовый навык: от него зависят и прогноз выручки, и оценка эффективности продуктовых механик апгрейда.

Формула

Upsell Rate = клиенты_с_апгрейдом / всего_клиентов × 100%

Под «апгрейдом» понимают переход на тариф с более высоким MRR — например, Basic → Pro. Ключевое слово здесь «более высоким»: обычная пролонгация того же тарифа апселлом не считается.

Базовый расчёт

Чтобы поймать факт апгрейда, смотрим на историю подписок пользователя и через LEAD() сравниваем текущий тариф со следующим:

WITH customer_history AS (
    SELECT
        user_id,
        plan,
        plan_started_at,
        LEAD(plan) OVER (PARTITION BY user_id ORDER BY plan_started_at) AS next_plan,
        LEAD(plan_started_at) OVER (PARTITION BY user_id ORDER BY plan_started_at) AS next_plan_at
    FROM subscriptions
),
upgrades AS (
    SELECT DISTINCT user_id
    FROM customer_history
    WHERE plan = 'basic' AND next_plan = 'pro'
      OR plan = 'pro' AND next_plan = 'enterprise'
      OR plan = 'basic' AND next_plan = 'enterprise'
)
SELECT
    COUNT(DISTINCT s.user_id) AS total_customers,
    COUNT(DISTINCT u.user_id) AS upgraded,
    COUNT(DISTINCT u.user_id)::NUMERIC * 100 / COUNT(DISTINCT s.user_id) AS upsell_rate_pct
FROM subscriptions s
LEFT JOIN upgrades u USING (user_id)
WHERE s.plan_started_at >= CURRENT_DATE - INTERVAL '12 months';

Список пар «откуда → куда» в upgrades перечисляет только апгрейды (Basic→Pro, Pro→Enterprise, Basic→Enterprise). Обратные переходы сюда не попадают — их считают отдельно как contraction.

Expansion Revenue

Сам по себе процент апгрейдов не показывает, сколько денег они принесли. Для этого считают expansion revenue — прирост MRR от апгрейдов. Подтягиваем цены тарифов и через LAG() берём дельту к предыдущей цене:

WITH plan_prices AS (
    SELECT * FROM (VALUES
        ('basic', 29),
        ('pro', 79),
        ('enterprise', 299)
    ) AS p(plan, monthly_price)
),
customer_history AS (
    SELECT
        s.user_id,
        s.plan,
        s.plan_started_at,
        p.monthly_price,
        LAG(p.monthly_price) OVER (PARTITION BY s.user_id ORDER BY s.plan_started_at) AS prev_price
    FROM subscriptions s
    JOIN plan_prices p ON p.plan = s.plan
),
expansion AS (
    SELECT
        user_id,
        plan_started_at,
        monthly_price - prev_price AS mrr_added
    FROM customer_history
    WHERE prev_price IS NOT NULL
      AND monthly_price > prev_price
)
SELECT
    DATE_TRUNC('month', plan_started_at) AS month,
    COUNT(*) AS upsells,
    SUM(mrr_added) AS expansion_mrr
FROM expansion
WHERE plan_started_at >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY 1
ORDER BY 1;

Условие monthly_price > prev_price отсекает даунгрейды: в expansion попадает только рост цены. На выходе — помесячная динамика числа апселлов и добавленного MRR.

По сегментам

Средний upsell rate по всей базе скрывает, где апселл работает лучше. Разбивка по сегменту (например, по стране) показывает, куда стоит вкладывать усилия:

SELECT
    u.country,
    COUNT(DISTINCT s.user_id) AS customers,
    COUNT(DISTINCT CASE WHEN was_upgraded THEN s.user_id END) AS upgraded,
    COUNT(DISTINCT CASE WHEN was_upgraded THEN s.user_id END)::NUMERIC * 100
    / NULLIF(COUNT(DISTINCT s.user_id), 0) AS upsell_pct
FROM subscriptions s
JOIN users u USING (user_id)
LEFT JOIN (
    SELECT user_id, TRUE AS was_upgraded
    FROM plan_changes
    WHERE change_type = 'upgrade'
) up USING (user_id)
WHERE s.created_at >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY u.country
ORDER BY upsell_pct DESC;

NULLIF в знаменателе защищает от деления на ноль, если в сегменте вдруг не оказалось клиентов.

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

Время до апселла

Полезно знать не только сколько клиентов апгрейдится, но и как быстро. Метрика time-to-upsell помогает планировать, когда предлагать апгрейд:

WITH upgrades AS (
    SELECT
        user_id,
        MIN(initial_plan_started_at) AS signed_up_at,
        MIN(upgrade_at) AS first_upgrade_at
    FROM subscription_events
    GROUP BY user_id
)
SELECT
    AVG(EXTRACT(EPOCH FROM (first_upgrade_at - signed_up_at)) / 86400) AS avg_days_to_upgrade,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (first_upgrade_at - signed_up_at)) / 86400) AS median_days
FROM upgrades
WHERE first_upgrade_at IS NOT NULL;

Здесь считают и среднее, и медиану: распределение времени до апгрейда обычно скошено (кто-то апгрейдится сразу, кто-то через год), поэтому медиана честнее среднего.

Как это спрашивают на собесе

Тему любят на собесах в SaaS и подписочных продуктах — она проверяет и SQL с оконными функциями, и понимание unit-экономики.

«Чем upsell отличается от cross-sell?» Апселл — переход на более дорогой тариф того же продукта, cross-sell — покупка дополнительного продукта. Это разные механики продаж и считаются отдельно.

«Как отделить апселл от простого повышения цены тарифа?» Если клиент остался на том же плане, а цена плана выросла, это не апселл — ценность та же, изменилась только цена. Апселл — это именно переход на более высокий тариф.

«Считается ли добавление мест (seats) апселлом?» Обычно да — это тоже расширение (expansion), даже если тариф формально тот же. Поэтому seats важно трекать отдельно, иначе часть роста выручки потеряется. Потренировать такие вопросы по продуктовым метрикам можно в Карьернике.

«Как связаны upsell rate и NRR?» Апселл — один из драйверов expansion в формуле NRR. При сильном апселле NRR может быть выше 100% даже при ненулевом оттоке: база растёт без новых клиентов.

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

  • Путать upsell и cross-sell. Апселл — более дорогой тариф того же продукта, cross-sell — дополнительный продукт. Это разные механики, и смешивать их в одной метрике нельзя.
  • Некорректно учитывать даунгрейды. Клиент ушёл на тариф ниже, а через пару месяцев вернулся вверх — считать это апселлом или нет, надо зафиксировать в определении заранее.
  • Считать рост цены тарифа апселлом. Клиент остался на «Pro», но сам «Pro» подорожал — обычно это не апселл, ценность не изменилась. Иначе метрика раздувается на ровном месте.
  • Считать конверсию из триала апселлом. Переход из бесплатного триала в платный — это активация, а не апселл. Это отдельная метрика, их нельзя складывать.
  • Игнорировать рост по местам. Добавление seats на том же тарифе — это тоже expansion. Если не трекать seats, вы недосчитаете реальную величину расширения выручки.

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

FAQ

Какой Upsell Rate считается нормальным?

Для SaaS ориентир — 10–20% апгрейдов в год, это здоровый уровень. У быстрорастущих продуктов бывает и 25%+. Но цифра сильно зависит от модели: там, где тарифов мало и потолок дешёвый, апселлить физически некуда, и низкий rate не всегда проблема.

Чем upsell отличается от expansion?

Expansion — это зонтичный термин, куда входят апселл (переход на более дорогой тариф), cross-sell (доп. продукт) и добавление мест (seats). Upsell — частный случай expansion, именно повышение тарифа. Поэтому expansion revenue всегда шире, чем выручка от одного апселла.

Как триггерить апселл?

Рабочий подход — по приближению к лимиту тарифа: когда потребление подходит к потолку плана, показывают мягкое уведомление и предложение апгрейда. Это конвертит лучше, чем случайные предложения, потому что попадает в момент реальной потребности.

Можно ли делать авто-апгрейд без согласия клиента?

Клиенты такое ненавидят — тихое списание за более дорогой тариф бьёт по доверию и разгоняет отток и возвраты. Правильнее — явное предупреждение при подходе к лимиту и апгрейд по согласию (opt-in).

Что такое negative upsell?

Так иногда называют обратное движение — даунгрейд клиента на более дешёвый тариф. Его считают отдельно как contraction MRR и не смешивают с апселлом, иначе картина расширения выручки будет искажена.