Как посчитать CAC по каналам в SQL
Содержание:
Зачем CAC по каналам
Blended CAC скрывает, какие каналы дешёвые, а какие дорогие. Если общий CAC = $100, это среднее: organic — $20, Google Ads — $250, Facebook — $80. Чтобы решать, куда направить бюджет, нужен разрез.
Базовая формула
CAC_channel = (расходы на канал за период) / (привлечённых юзеров через канал)«Привлечённый» обычно = first paid customer. Можно signups или активаций.
По utm_source
WITH new_users AS (
SELECT
utm_source AS channel,
user_id
FROM users
WHERE created_at >= '2026-04-01' AND created_at < '2026-05-01'
),
spend AS (
SELECT channel, SUM(amount) AS total_spend
FROM marketing_spend
WHERE spend_date >= '2026-04-01' AND spend_date < '2026-05-01'
GROUP BY channel
)
SELECT
s.channel,
s.total_spend,
COUNT(nu.user_id) AS new_users,
s.total_spend / NULLIF(COUNT(nu.user_id), 0) AS cac
FROM spend s
LEFT JOIN new_users nu USING (channel)
GROUP BY s.channel, s.total_spend
ORDER BY cac DESC;LEFT JOIN чтобы каналы без users в этом месяце тоже показались (с infinite CAC — флаг).
По region/platform
Иногда канал работает разно в RU и US. Добавляем сегмент:
WITH cohort AS (
SELECT u.user_id, u.utm_source AS channel, u.country
FROM users u
WHERE u.created_at >= '2026-04-01' AND u.created_at < '2026-05-01'
),
spend AS (
SELECT channel, country, SUM(amount) AS spend
FROM marketing_spend
WHERE spend_date >= '2026-04-01' AND spend_date < '2026-05-01'
GROUP BY channel, country
)
SELECT
s.channel,
s.country,
s.spend,
COUNT(c.user_id) AS new_users,
s.spend / NULLIF(COUNT(c.user_id), 0) AS cac
FROM spend s
LEFT JOIN cohort c USING (channel, country)
GROUP BY s.channel, s.country, s.spend
ORDER BY s.country, cac DESC;Google Ads / US может быть $300, Google Ads / Russia — $50. Разная конкуренция, разное предложение.
Динамика во времени
Помесячный CAC по каналам — увидеть, растёт ли цена привлечения:
WITH monthly AS (
SELECT
DATE_TRUNC('month', spend_date)::DATE AS month,
channel,
SUM(amount) AS spend
FROM marketing_spend
WHERE spend_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY DATE_TRUNC('month', spend_date), channel
),
users_per_month AS (
SELECT
DATE_TRUNC('month', created_at)::DATE AS month,
utm_source AS channel,
COUNT(*) AS new_users
FROM users
WHERE created_at >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY DATE_TRUNC('month', created_at), utm_source
)
SELECT
m.month,
m.channel,
m.spend,
u.new_users,
m.spend / NULLIF(u.new_users, 0) AS monthly_cac
FROM monthly m
LEFT JOIN users_per_month u USING (month, channel)
ORDER BY m.channel, m.month;Растущий CAC в канале — flag: бьют ceiling масштабирования.
Частые ошибки
Ошибка 1. Attribution model. First-touch, last-touch, multi-touch дают разные CAC. Зафиксируйте модель.
Ошибка 2. Считать только paid customers. Если включаете free signups в knowledge base, CAC ниже, но обманчиво — free не отбивают расходы.
Ошибка 3. Игнорировать organic. Organic CAC = (контент-команда + SEO tools) / organic signups. Не «бесплатно».
Ошибка 4. Не учитывать lag. Расходы в апреле могут давать signups в мае. Особенно для long sales cycle.
Ошибка 5. CAC без LTV. $50 CAC выглядит дёшево, но если LTV = $30 — убыточно. LTV/CAC ratio > 3 — стандарт.
Связанные темы
- Как посчитать CAC в SQL
- Как посчитать blended CAC в SQL
- Как посчитать LTV в SQL
- Как посчитать CAC payback в SQL
FAQ
Что считать «привлечённым»?
Зависит от продукта. SaaS — first paid. B2C apps — first install. Solid: тот, на кого ROI считается.
Lag — сколько?
B2C 7-30 дней. B2B 90+. Calibrate by data.
Branded paid и organic — разные?
Yes. Branded paid (когда кто-то ищет «kariernik») зачастую перехват organic — фактический CAC выше.
Какой CAC «нормальный»?
Зависит от LTV. LTV/CAC > 3 — healthy.
CAC по сегменту customers?
Сложнее: нужно matching paid → segment. Через UTM + sign-up survey.