Как посчитать 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+ вопросами для собесов.