Как посчитать Frequency в SQL
Содержание:
Зачем Frequency
Продакт смотрит retention 35% — кажется хорошо. Аналитик добавляет покупательскую frequency: повторные покупатели делают в среднем 1,8 покупок за квартал. Конкурент в той же категории — 4,2. Значит, юзер возвращается, но мало покупает. CAC окупается медленно.
Frequency — компаньон retention: первая показывает «вернулся», вторая — «насколько активно». В RFM-анализе frequency определяет ценность сегмента. В статье — SQL для расчёта и подводные камни.
Что такое Frequency
Purchase Frequency — среднее число покупок одного пользователя за период.
Frequency = Total orders / Unique buyers (за период)В RFM F — это позиция пользователя в распределении frequency, обычно через NTILE(5).
Базовый расчёт
Данные: orders(user_id, order_id, total, status, created_at).
SELECT
COUNT(*) AS total_orders,
COUNT(DISTINCT user_id) AS unique_buyers,
COUNT(*)::NUMERIC / NULLIF(COUNT(DISTINCT user_id), 0) AS avg_frequency
FROM orders
WHERE status = 'paid'
AND created_at >= '2026-01-01'
AND created_at < '2026-04-01';Важно: делите orders на unique buyers (тех, кто хоть раз купил), а не на all users (которые могли не покупать). Иначе frequency занижено.
Frequency в RFM
WITH user_frequency AS (
SELECT
user_id,
COUNT(*) AS purchases,
MAX(created_at) AS last_purchase,
SUM(total) AS monetary
FROM orders
WHERE status = 'paid'
AND created_at >= CURRENT_DATE - INTERVAL '365 days'
GROUP BY user_id
)
SELECT
user_id,
purchases AS frequency,
EXTRACT(DAY FROM CURRENT_DATE - last_purchase) AS recency_days,
monetary,
NTILE(5) OVER (ORDER BY purchases DESC) AS f_score,
NTILE(5) OVER (ORDER BY last_purchase DESC) AS r_score,
NTILE(5) OVER (ORDER BY monetary DESC) AS m_score
FROM user_frequency;NTILE(5) делит пользователей на 5 квинтилей. F=1 — топ 20% по частоте.
Frequency по сегментам
По acquisition channel
WITH user_freq AS (
SELECT
o.user_id,
u.acquisition_channel,
COUNT(*) AS purchases
FROM orders o
JOIN users u ON u.user_id = o.user_id
WHERE o.status = 'paid'
AND o.created_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY o.user_id, u.acquisition_channel
)
SELECT
acquisition_channel,
COUNT(*) AS buyers,
AVG(purchases) AS avg_frequency,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY purchases) AS median_frequency
FROM user_freq
GROUP BY acquisition_channel
ORDER BY avg_frequency DESC;Медиана важна: канал с одним «китом» (10 покупок) и 9 случайными (1 покупка) даст avg = 1,9, но median = 1.
Распределение frequency
WITH user_freq AS (
SELECT user_id, COUNT(*) AS purchases
FROM orders
WHERE status = 'paid' AND created_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY user_id
)
SELECT
purchases,
COUNT(*) AS users,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () AS pct
FROM user_freq
GROUP BY purchases
ORDER BY purchases;В e-com обычно 60-70% — 1 покупка, 15-20% — 2-3, остальные — 4+. «Хвост» из активных — самые ценные.
Visit Frequency vs Purchase Frequency
| Метрика | Что считаем |
|---|---|
| Visit Frequency | Кол-во сессий на пользователя |
| Purchase Frequency | Кол-во покупок на покупателя |
Visit frequency обычно в 5-15 раз выше purchase frequency. Conversion rate ≈ purchase / visit frequency × number of visitors.
WITH sessions_per_user AS (
SELECT user_id, COUNT(DISTINCT session_id) AS visits
FROM events
WHERE event_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
),
purchases_per_user AS (
SELECT user_id, COUNT(*) AS purchases
FROM orders
WHERE status = 'paid' AND created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
)
SELECT
AVG(s.visits) AS avg_visit_freq,
AVG(p.purchases) AS avg_purchase_freq,
AVG(p.purchases)::NUMERIC / NULLIF(AVG(s.visits), 0) AS visit_to_purchase_ratio
FROM sessions_per_user s
LEFT JOIN purchases_per_user p ON p.user_id = s.user_id;Частые ошибки
Ошибка 1. Делить orders на all users
COUNT(orders) / COUNT(DISTINCT users) где users — все зарегистрированные. Получите frequency 0,2 — но это не frequency, а conversion × frequency. Делите только на тех, кто купил.
Ошибка 2. Не отсекать pending / cancelled
Корзины и отменённые заказы не должны идти в frequency. Только status = 'paid' или 'delivered'.
Ошибка 3. Frequency на коротком периоде
7 дней → почти все юзеры с frequency = 1. Нужно ≥ 30-90 дней для содержательных значений.
Ошибка 4. Avg вместо median
Один «кит» с 50 покупками сдвигает avg на 30%. Показывайте median рядом или используйте NTILE / percentile.
Ошибка 5. Frequency без сегментации
Total frequency 1,9 говорит мало. Frequency по сегментам (B2B vs B2C, repeat-cohort, регион) показывает реальную динамику.
Ошибка 6. Путать с frequency капы в маркетинге
В рекламе «frequency cap» — это другое понятие (макс. показов на пользователя). В аналитике frequency — про действия пользователя.
Связанные темы
FAQ
Какая frequency считается хорошей?
Зависит от категории. Продукты (FMCG) — 4-8/мес. Одежда — 1-3/квартал. Электроника — 0,3-0,8/год. Доставка еды — 5-15/мес. Сравнивайте с конкурентами в категории.
Frequency или Retention — что показывать?
Retention — про возврат. Frequency — про активность вернувшихся. Идеально вместе: retention 35% при frequency 2 = лоялисты. Retention 35% при frequency 1,1 = «зомби-аккаунты», заходят редко.
Frequency считать по календарю или по cohort age?
Зависит от вопроса. Календарный (последние 90 дней) — для оперативного отчёта. Cohort age (первые 90 дней после регистрации) — для onboarding-анализа.
Что делать, если frequency низкая?
Декомпозиция: барьеры (товара нет / должно быть лучше), pricing (дороже конкурентов), retention (юзеры не возвращаются), product-market fit. Часто работает: cross-sell, лояльность, push на возврат.
Как считать frequency для подписочного бизнеса?
В подписке frequency обычно не используется — там есть active months или churn. Frequency релевантен для transactional бизнеса с дискретными покупками.