Как посчитать unit-экономику в SQL

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это нужно

Unit-экономика отвечает на главный вопрос бизнеса: «зарабатываем ли мы на каждом клиенте?». Если LTV/CAC = 3 — здоровый рост, можно инвестировать в маркетинг. Если 0.5 — сливаем деньги и банкротимся. Инвесторы первым делом смотрят на unit-экономику, а не на total revenue.

Аналитик в стартапе / SaaS / e-commerce должен уметь посчитать unit-экономику с нуля. Это значит — собрать несколько таблиц (users, orders, ad_spend, subscriptions), правильно атрибутировать CAC, учесть margin, посчитать LTV по cohort. Ни один BI-инструмент не даст это «из коробки» — только SQL.

На собесе для middle+ это одна из главных задач: «покажи, как бы ты построил дашборд unit-экономики». Без понимания компонентов и их связей — фейл.

В статье — готовые SQL-запросы для всех ключевых метрик:

  • CAC по каналам (blended, paid-only)
  • LTV по cohort (historical + predicted)
  • LTV/CAC ratio
  • CAC payback period (в месяцах)
  • Contribution margin
  • Все в одном финальном дашборде

Схемы: users, orders, ad_spend.

1. CAC (см. отдельный пост)

-- упрощённый blended CAC за квартал
SELECT
    SUM(spend)::FLOAT / COUNT(DISTINCT user_id) AS blended_cac
FROM (
    SELECT SUM(spend) AS spend, NULL::UUID AS user_id FROM ad_spend
    WHERE day >= '2026-01-01' AND day < '2026-04-01'
    UNION ALL
    SELECT 0, id FROM users WHERE signup_at >= '2026-01-01' AND signup_at < '2026-04-01'
) t;

Подробнее: как посчитать CAC.

2. LTV (см. отдельный пост)

-- historical LTV with margin
SELECT
    user_id,
    SUM(total * 0.25) AS ltv_profit  -- 25% margin
FROM orders
WHERE status = 'paid'
GROUP BY user_id;

Подробнее: как посчитать LTV.

3. LTV/CAC ratio по каналам

WITH cac_by_channel AS (
    SELECT
        u.attribution_channel AS channel,
        sp.total_spend::FLOAT / COUNT(u.id) AS cac
    FROM users u
    CROSS JOIN (
        SELECT SUM(spend) AS total_spend FROM ad_spend WHERE day >= '2026-01-01'
    ) sp
    WHERE u.signup_at >= '2026-01-01'
    GROUP BY u.attribution_channel, sp.total_spend
),
ltv_by_channel AS (
    SELECT
        u.attribution_channel AS channel,
        AVG(user_rev.revenue * 0.25) AS avg_ltv_profit
    FROM users u
    JOIN (
        SELECT user_id, SUM(total) AS revenue
        FROM orders WHERE status = 'paid'
        GROUP BY user_id
    ) user_rev ON user_rev.user_id = u.id
    GROUP BY u.attribution_channel
)
SELECT
    c.channel,
    c.cac,
    l.avg_ltv_profit AS ltv,
    l.avg_ltv_profit / NULLIF(c.cac, 0) AS ltv_cac_ratio,
    CASE
        WHEN l.avg_ltv_profit / NULLIF(c.cac, 0) >= 3 THEN 'healthy'
        WHEN l.avg_ltv_profit / NULLIF(c.cac, 0) >= 1 THEN 'acceptable'
        ELSE 'unprofitable'
    END AS health
FROM cac_by_channel c
JOIN ltv_by_channel l USING (channel)
ORDER BY ltv_cac_ratio DESC;

4. CAC payback period

За сколько месяцев окупается CAC:

WITH monthly_arpu AS (
    SELECT
        user_id,
        AVG(total) * 0.25 AS monthly_profit  -- margin
    FROM orders
    WHERE status = 'paid'
      AND created_at >= NOW() - INTERVAL '6 months'
    GROUP BY user_id
),
avg_cac AS (
    SELECT 1500 AS cac  -- подставьте реальное значение
),
avg_arpu AS (
    SELECT AVG(monthly_profit) AS monthly_profit FROM monthly_arpu
)
SELECT
    avg_cac.cac,
    avg_arpu.monthly_profit,
    avg_cac.cac / NULLIF(avg_arpu.monthly_profit, 0) AS payback_months
FROM avg_cac, avg_arpu;

Целевые значения:

  • B2C SaaS: < 6 мес
  • B2B SaaS: < 12 мес
  • Enterprise: < 24 мес

5. Contribution margin

Прибыль, которая остаётся после variable costs (payment processing, shipping, CAC proportion):

SELECT
    user_id,
    SUM(total) AS revenue,
    SUM(total * 0.25) AS gross_profit,           -- margin 25%
    SUM(total * 0.25) - SUM(total * 0.03)        -- payment fees 3%
                     - SUM(shipping_cost)        -- shipping
                     AS contribution_margin
FROM orders
WHERE status = 'paid'
GROUP BY user_id;

6. Все метрики в одном запросе

Финальный дашборд unit-экономики:

WITH
cohorts AS (
    SELECT user_id, DATE_TRUNC('month', signup_at) AS cohort
    FROM users
    WHERE signup_at >= '2025-01-01'
),
cac AS (
    SELECT
        DATE_TRUNC('month', day) AS month,
        SUM(spend) AS spend
    FROM ad_spend
    GROUP BY 1
),
ltv AS (
    SELECT
        c.cohort AS month,
        COUNT(c.user_id) AS new_users,
        SUM(user_rev.revenue * 0.25) AS total_ltv_profit,
        AVG(user_rev.revenue * 0.25) AS avg_ltv_profit
    FROM cohorts c
    LEFT JOIN (
        SELECT user_id, SUM(total) AS revenue
        FROM orders WHERE status = 'paid'
        GROUP BY user_id
    ) user_rev USING (user_id)
    GROUP BY c.cohort
)
SELECT
    l.month AS cohort,
    l.new_users,
    cac.spend::FLOAT / NULLIF(l.new_users, 0) AS cac,
    l.avg_ltv_profit AS avg_ltv,
    l.avg_ltv_profit / NULLIF(cac.spend::FLOAT / NULLIF(l.new_users, 0), 0) AS ltv_cac_ratio,
    l.total_ltv_profit - cac.spend AS net_profit
FROM ltv l
LEFT JOIN cac ON cac.month = l.month
ORDER BY l.month;

7. Break-even analysis

Сколько нужно зарабатывать с клиента, чтобы выйти в ноль:

WITH fixed_costs AS (
    SELECT 5000000 AS monthly_fixed  -- зарплаты, офис, софт
),
variable_costs AS (
    SELECT 0.75 AS variable_pct  -- 75% от revenue
),
break_even AS (
    SELECT
        monthly_fixed / (1 - variable_pct) AS revenue_to_break_even
    FROM fixed_costs, variable_costs
)
SELECT * FROM break_even;

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

LTV revenue вместо profit

Сравнение с CAC через revenue → ложная картина. Нужен profit.

Ignore cohort

LTV «по всем клиентам» включает и старых (долго живут), и новых (ещё не начали). Overestimates.

CAC без fully-loaded

Только ad spend ≠ CAC. Плюс зарплаты, tools, content.

Не считать payback

LTV/CAC может быть отличным, но если payback 24 месяца — cash flow проблемы.

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

FAQ

LTV/CAC 3:1 всегда правильно?

Для SaaS — да. Для e-commerce может быть 1.5-2 ок (короткий payback).

Как считать для enterprise?

Отдельно. Один enterprise-клиент может покрыть десятки мелких.

Payback или LTV/CAC важнее?

Оба. Payback — для cash flow. LTV/CAC — для stratегии.

Contribution margin vs gross margin?

Gross — revenue минус COGS. Contribution — минус ВСЕ variable costs (включая CAC fraction).


Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.