Как посчитать CAC в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Что такое CAC
CAC (Customer Acquisition Cost) — стоимость привлечения одного клиента.
CAC = Marketing spend / New customers acquiredВарианты:
- Blended CAC — весь маркетинг / все новые клиенты
- Paid CAC — только платный трафик
- Fully-loaded CAC — включает зарплаты команды, tools, etc.
Схема данных
Предположим, есть таблицы:
ad_spend (day, channel, spend)
users (user_id, signup_at, attribution_channel)
first_orders (user_id, first_order_at) -- из orders1. Простой blended CAC
Весь маркетинговый spend за период / все новые клиенты за тот же период.
WITH spend_total AS (
SELECT SUM(spend) AS total_spend
FROM ad_spend
WHERE day BETWEEN '2026-01-01' AND '2026-03-31'
),
new_users AS (
SELECT COUNT(*) AS new_users_cnt
FROM users
WHERE signup_at BETWEEN '2026-01-01' AND '2026-03-31'
)
SELECT
st.total_spend,
nu.new_users_cnt,
st.total_spend / nu.new_users_cnt AS blended_cac
FROM spend_total st, new_users nu;2. CAC по каналам
WITH spend_by_channel AS (
SELECT channel, SUM(spend) AS total_spend
FROM ad_spend
WHERE day BETWEEN '2026-01-01' AND '2026-03-31'
GROUP BY channel
),
users_by_channel AS (
SELECT attribution_channel AS channel, COUNT(*) AS new_users
FROM users
WHERE signup_at BETWEEN '2026-01-01' AND '2026-03-31'
GROUP BY attribution_channel
)
SELECT
sbc.channel,
sbc.total_spend,
ubc.new_users,
sbc.total_spend / NULLIF(ubc.new_users, 0) AS cac
FROM spend_by_channel sbc
LEFT JOIN users_by_channel ubc USING (channel)
ORDER BY cac;3. CAC по когортам
Когортный CAC — точнее, чем общий.
WITH cohorts AS (
SELECT
DATE_TRUNC('month', signup_at) AS cohort_month,
attribution_channel,
COUNT(*) AS new_users
FROM users
GROUP BY 1, 2
),
spend AS (
SELECT
DATE_TRUNC('month', day) AS month,
channel,
SUM(spend) AS spend
FROM ad_spend
GROUP BY 1, 2
)
SELECT
c.cohort_month,
c.attribution_channel,
c.new_users,
s.spend,
s.spend / NULLIF(c.new_users, 0) AS cac
FROM cohorts c
LEFT JOIN spend s
ON s.month = c.cohort_month
AND s.channel = c.attribution_channel
ORDER BY c.cohort_month, c.attribution_channel;4. Paid-only CAC
Только paid-каналы, без organic:
WITH paid_spend AS (
SELECT SUM(spend) AS paid_spend
FROM ad_spend
WHERE channel IN ('google_ads', 'yandex_direct', 'vk_ads', 'facebook_ads')
AND day BETWEEN '2026-01-01' AND '2026-03-31'
),
paid_users AS (
SELECT COUNT(*) AS paid_users
FROM users
WHERE attribution_channel IN ('google_ads', 'yandex_direct', 'vk_ads', 'facebook_ads')
AND signup_at BETWEEN '2026-01-01' AND '2026-03-31'
)
SELECT
paid_spend.paid_spend / paid_users.paid_users AS paid_cac
FROM paid_spend, paid_users;5. CAC по факту первой покупки (не регистрации)
Более строгий CAC: не «зарегистрировался», а «купил».
WITH spend AS (
SELECT
channel,
DATE_TRUNC('month', day) AS month,
SUM(spend) AS spend
FROM ad_spend
GROUP BY 1, 2
),
first_buyers AS (
SELECT
u.attribution_channel AS channel,
DATE_TRUNC('month', fo.first_order_at) AS month,
COUNT(*) AS new_buyers
FROM first_orders fo
JOIN users u ON u.user_id = fo.user_id
GROUP BY 1, 2
)
SELECT
s.channel,
s.month,
s.spend,
fb.new_buyers,
s.spend / NULLIF(fb.new_buyers, 0) AS cac_per_buyer
FROM spend s
LEFT JOIN first_buyers fb
ON fb.channel = s.channel AND fb.month = s.month
ORDER BY s.month, cac_per_buyer;6. CAC vs LTV per channel
Главное сравнение для маркетинга:
WITH cac_channel AS (
SELECT
channel,
SUM(spend) / COUNT(DISTINCT user_id) AS cac
FROM ad_spend a
LEFT JOIN users u
ON u.attribution_channel = a.channel
AND DATE_TRUNC('month', u.signup_at) = DATE_TRUNC('month', a.day)
GROUP BY channel
),
ltv_channel AS (
SELECT
u.attribution_channel AS channel,
AVG(user_ltv.total_revenue) AS avg_ltv
FROM users u
JOIN (
SELECT user_id, SUM(total) AS total_revenue
FROM orders WHERE status = 'paid'
GROUP BY user_id
) user_ltv ON user_ltv.user_id = u.user_id
GROUP BY u.attribution_channel
)
SELECT
c.channel,
c.cac,
l.avg_ltv,
l.avg_ltv / NULLIF(c.cac, 0) AS ltv_cac_ratio
FROM cac_channel c
JOIN ltv_channel l USING (channel)
ORDER BY ltv_cac_ratio DESC;7. Fully-loaded CAC
Включает не только рекламу, но и зарплаты маркетинга:
WITH all_marketing_cost AS (
-- ad spend
SELECT SUM(spend) AS cost FROM ad_spend WHERE day BETWEEN ... AND ...
UNION ALL
-- зарплаты маркетинг-команды (из таблицы marketing_payroll)
SELECT SUM(monthly_salary) FROM marketing_payroll WHERE month BETWEEN ...
UNION ALL
-- SaaS-инструменты
SELECT SUM(cost) FROM marketing_tools WHERE month BETWEEN ...
)
SELECT SUM(cost) AS total_marketing_cost FROM all_marketing_cost;Дальше — как обычно.
8. CAC с учётом атрибуции (multi-touch)
Last-click — самый простой, но не лучший. Linear attribution в SQL:
WITH touches AS (
SELECT
user_id,
channel,
touched_at,
COUNT(*) OVER (PARTITION BY user_id) AS total_touches
FROM marketing_touches
WHERE touched_at <= signup_at
),
attributed AS (
SELECT
user_id,
channel,
1.0 / total_touches AS attribution_weight
FROM touches
)
SELECT
channel,
SUM(attribution_weight) AS attributed_users
FROM attributed
GROUP BY channel;Частые ошибки
Ошибка 1. Перемешивать blended и paid CAC
Blended ≠ paid. Если 50% пользователей organic, blended CAC = paid CAC / 2 — обманчиво хорошо.
Ошибка 2. CAC без margin
Сравнение CAC с LTV должно быть через margin. 1000₽ LTV при 30% маржа = 300₽ profit — его сравнивать с CAC.
Ошибка 3. NULLIF забыли
spend / new_users -- деление на 0 крашнет запрос
spend / NULLIF(new_users, 0) -- безопасно, вернёт NULLОшибка 4. Смешение регистраций и покупок
CAC per signup ≠ CAC per paying customer. Для SaaS обычно нужно именно «paying».
Ошибка 5. Игнорировать attribution window
Если пользователь кликнул рекламу в январе, а купил в марте — кому засчитать? 7 дней / 28 дней / forever — стандарты разные.
Связанные темы
FAQ
Blended или paid CAC — что важнее?
Paid — для принятия решений по рекламным бюджетам. Blended — для overall unit-экономики компании.
Нужен ли fully-loaded CAC?
Для малого бизнеса — обычно хватает paid + немного overhead. Для enterprise и инвесторов — обязательно.
Как учитывать long attribution window?
Ставить разумный default (28 дней) и держать несколько моделей атрибуции для сравнения.
CAC должен быть меньше LTV?
LTV / CAC ≥ 3:1 — здоровая экономика. < 3 — проблема или в CAC, или в LTV.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.