SQL для RFM-анализа: построение и сегментация
Что такое RFM
RFM — одна из старейших и простейших моделей сегментации клиентов. Работает в ecommerce, retail, подписочных сервисах. Три компонента:
Recency — как давно клиент совершал действие (покупку, вход). Чем свежее — тем лучше.
Frequency — как часто клиент совершает действие. Чем чаще — тем ценнее.
Monetary — сколько денег приносит клиент. Чем больше — тем важнее.
Каждого клиента оценивают по трём осям, присваивают score, на основе которого делят на сегменты: «чемпионы», «лояльные», «под угрозой оттока» и так далее. Дальше маркетинг работает с каждым сегментом по-разному.
Базовый SQL для RFM
Пошагово посчитаем три метрики для каждого клиента:
WITH rfm_base AS (
SELECT
user_id,
EXTRACT(DAY FROM (CURRENT_DATE - MAX(created_at)::DATE)) AS recency_days,
COUNT(*) AS frequency,
SUM(amount) AS monetary
FROM orders
WHERE status = 'paid'
GROUP BY user_id
)
SELECT * FROM rfm_base
LIMIT 10;Получаем табличку:
user_id | recency_days | frequency | monetary
1 | 5 | 15 | 45000
2 | 120 | 3 | 8000
3 | 2 | 30 | 120000Это сырые данные. Дальше их нужно привести к scores.
RFM scores через NTILE
Классический подход — разбить каждую метрику на 5 групп (квантилей) и присвоить score 1-5:
WITH rfm_base AS (
SELECT
user_id,
EXTRACT(DAY FROM (CURRENT_DATE - MAX(created_at)::DATE)) AS recency_days,
COUNT(*) AS frequency,
SUM(amount) AS monetary
FROM orders
WHERE status = 'paid'
GROUP BY user_id
)
SELECT
user_id,
recency_days,
frequency,
monetary,
-- Recency: меньше = лучше (score 5)
NTILE(5) OVER (ORDER BY recency_days DESC) AS r_score,
-- Frequency: больше = лучше
NTILE(5) OVER (ORDER BY frequency) AS f_score,
-- Monetary: больше = лучше
NTILE(5) OVER (ORDER BY monetary) AS m_score
FROM rfm_base;Обратите внимание на ORDER BY recency_days DESC для recency — чем меньше дней с последней покупки, тем выше должен быть score.
После этого у каждого клиента тройка вроде (5, 4, 5) — отличный recency, хорошая frequency, отличный monetary.
Классические сегменты
Стандартная классификация на основе RFM score:
WITH rfm_scored AS (
-- предыдущий запрос
),
segments AS (
SELECT
user_id,
r_score,
f_score,
m_score,
CASE
WHEN r_score >= 4 AND f_score >= 4 AND m_score >= 4 THEN 'Чемпионы'
WHEN r_score >= 3 AND f_score >= 3 AND m_score >= 3 THEN 'Лояльные'
WHEN r_score >= 4 AND f_score <= 2 THEN 'Новые'
WHEN r_score <= 2 AND f_score >= 3 AND m_score >= 3 THEN 'Под риском оттока'
WHEN r_score <= 2 AND f_score <= 2 AND m_score >= 4 THEN 'Потерянные VIP'
WHEN r_score <= 2 AND f_score <= 2 AND m_score <= 2 THEN 'Потерянные'
ELSE 'Обычные'
END AS segment
FROM rfm_scored
)
SELECT segment, COUNT(*) AS customers
FROM segments
GROUP BY segment
ORDER BY customers DESC;Теперь видно распределение: сколько чемпионов, сколько под риском оттока, и так далее.
Каждый сегмент требует своей стратегии:
Чемпионы — поощрять за loyalty, просить рекомендации, тестировать новые продукты.
Лояльные — поддерживать engagement, предлагать upsell.
Под риском оттока — активная retention-кампания, персональные предложения.
Потерянные VIP — winback-кампания с сильным оффером (они приносили много, стоит постараться).
Новые — онбординг, первая покупка → вторая покупка.
Концентрированный single score
Иногда хочется одно число вместо трёх:
SELECT
user_id,
r_score * 100 + f_score * 10 + m_score AS rfm_code,
(r_score + f_score + m_score) AS rfm_sum
FROM rfm_scored;rfm_code — уникальный код (например, 545 — R=5, F=4, M=5). Всего 125 комбинаций.
rfm_sum — простое среднее, от 3 до 15. Меньше уровней, удобнее для дашбордов.
Для конкретных решений первый вариант гибче, второй — для upper-level просмотра.
Глубокое погружение в продуктовую сегментацию — важная часть senior-роли в ecommerce. В тренажёре Карьерник есть задачи на построение сегментов и метрик для retention-команд.
RFM для SaaS
Для подписочной модели немного другой RFM.
Recency — как давно логинился / использовал продукт.
Frequency — число active sessions за период.
Monetary — MRR (monthly recurring revenue) клиента.
WITH saas_rfm AS (
SELECT
u.user_id,
EXTRACT(DAY FROM CURRENT_DATE - MAX(e.event_time)::DATE) AS recency_days,
COUNT(DISTINCT e.event_time::DATE) AS active_days, -- за последние 90 дней
MAX(s.monthly_amount) AS current_mrr
FROM users u
LEFT JOIN events e ON u.user_id = e.user_id
AND e.event_time >= CURRENT_DATE - INTERVAL '90 day'
LEFT JOIN subscriptions s ON u.user_id = s.user_id
AND s.status = 'active'
GROUP BY u.user_id
)
SELECT ... -- применяем NTILE как раньшеДля B2B — agregate на уровне аккаунта, не пользователя.
Dynamic RFM
RFM — это снимок. Для динамики полезно смотреть, как клиенты перемещаются между сегментами.
WITH rfm_current AS (
-- RFM сегменты на сегодня
),
rfm_month_ago AS (
-- RFM на месяц назад (та же логика, но с фильтром created_at <= CURRENT_DATE - 30)
),
transitions AS (
SELECT
COALESCE(c.user_id, m.user_id) AS user_id,
c.segment AS current_segment,
m.segment AS previous_segment
FROM rfm_current c
FULL OUTER JOIN rfm_month_ago m USING (user_id)
)
SELECT
previous_segment,
current_segment,
COUNT(*) AS users
FROM transitions
GROUP BY previous_segment, current_segment
ORDER BY previous_segment, current_segment;Матрица переходов. Видно, сколько клиентов «чемпионов» стали «под риском», сколько «новых» стали «лояльными» — что-то работает.
Продвинутая сегментация
Классический RFM — это 3 измерения. Но сегментация может быть богаче:
- Product category affinity — что человек покупает (электроника, одежда, еда).
- Device — mobile vs desktop vs both.
- Geography — столица vs регионы.
- Source — как пришёл (organic, paid, referral).
Полная сегментация — это RFM × product × device × source. В результате получаются сотни микросегментов. Для production обычно делают RFM + 1-2 важных разреза.
Когда RFM не работает
RFM плох для:
Подписочных моделей с одинаковой ценой. Все платят одинаково — Monetary становится proxy длительности подписки.
Продуктов с редкими покупками. Авто, недвижимость. Там каждая покупка — событие, frequency=1 для всех.
Сильно сезонных бизнесов. Летом все «под риском», зимой все «лояльные». Нужна normalization.
В этих случаях используют другие модели сегментации — behavioral, cohort-based, cluster analysis.
Применение в маркетинге
Практический workflow в ecommerce:
Раз в неделю SQL-скрипт пересчитывает RFM-сегменты.
Экспорт в CRM (Mindbox, CleverTap, собственная система).
Автоматические триггеры: для сегмента «Под риском» — SMS с персональным промокодом. Для «Чемпионов» — access к новой коллекции первыми. Для «Потерянных VIP» — звонок менеджера.
Retention-команда отслеживает переходы между сегментами. Если «Под риском» → «Чемпионы» растёт, кампании работают.
Читайте также
FAQ
5 или 10 квантилей?
5 — стандарт, понятно маркетингу. 10 — больше градаций, но усложняет. Для большинства задач 5 достаточно.
Как часто пересчитывать?
Еженедельно обычно. Для бизнесов с быстрым циклом (fastfood) — ежедневно. Для SaaS с годовой подпиской — ежемесячно.
RFM-сегменты меняются сами?
Да. Клиент ничего не делает — через месяц он «под риском». Покупает — становится «чемпионом». Динамика — часть логики.
Можно ли без M (monetary)?
RF-сегментация возможна. Используется для продуктов без прямой монетизации (контент-платформы). Но M добавляет business context.