Какие метрики важны для двустороннего маркетплейса? Как балансировать спрос и предложение?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ключевые метрики двустороннего маркетплейса
Двусторонний маркетплейс (например, Uber, Airbnb, Fiverr) — это экосистема с двумя типами пользователей: поставщики (suppliers) и потребители (consumers). Успех зависит от баланса между ними.
Иерархия метрик (AARRR для маркетплейса)
A — Acquisition (Акквизиция)
Отдельно для каждой стороны:
-- Поставщики (Suppliers)
SELECT
DATE(created_at) as day,
'suppliers' as side,
COUNT(*) as new_users,
SUM(CASE WHEN profile_complete THEN 1 ELSE 0 END) as completed_profile,
SUM(CASE WHEN profile_complete THEN 0 ELSE 1 END) as incomplete_profile,
ROUND(100.0 * SUM(CASE WHEN first_listing THEN 1 ELSE 0 END) / COUNT(*), 2) as pct_created_listing
FROM suppliers
WHERE created_at >= CURRENT_DATE - INTERVAL 90 DAY
GROUP BY DATE(created_at)
UNION ALL
-- Потребители (Consumers)
SELECT
DATE(created_at) as day,
'consumers' as side,
COUNT(*) as new_users,
SUM(CASE WHEN profile_complete THEN 1 ELSE 0 END) as completed_profile,
SUM(CASE WHEN profile_complete THEN 0 ELSE 1 END) as incomplete_profile,
ROUND(100.0 * SUM(CASE WHEN first_search THEN 1 ELSE 0 END) / COUNT(*), 2) as pct_first_search
FROM consumers
WHERE created_at >= CURRENT_DATE - INTERVAL 90 DAY
GROUP BY DATE(created_at)
Вопросы:
- Кого привлекаем больше — поставщиков или потребителей?
- Есть ли холодный стартовый перекос (chicken and egg проблема)?
A2 — Activation
Первое ценное действие для каждой стороны:
-- Поставщики: первый листинг
SELECT
DATE(created_at) as cohort_date,
COUNT(*) as new_suppliers,
SUM(CASE WHEN days_to_first_listing <= 1 THEN 1 ELSE 0 END) as activated_d1,
SUM(CASE WHEN days_to_first_listing <= 7 THEN 1 ELSE 0 END) as activated_d7,
SUM(CASE WHEN days_to_first_listing <= 30 THEN 1 ELSE 0 END) as activated_d30,
ROUND(100.0 * SUM(CASE WHEN days_to_first_listing <= 7 THEN 1 ELSE 0 END) / COUNT(*), 2) as activation_rate_d7
FROM suppliers
WHERE created_at >= CURRENT_DATE - INTERVAL 90 DAY
GROUP BY DATE(created_at)
ORDER BY cohort_date DESC
UNION ALL
-- Потребители: первый заказ
SELECT
DATE(created_at) as cohort_date,
COUNT(*) as new_consumers,
SUM(CASE WHEN days_to_first_order <= 1 THEN 1 ELSE 0 END) as activated_d1,
SUM(CASE WHEN days_to_first_order <= 7 THEN 1 ELSE 0 END) as activated_d7,
SUM(CASE WHEN days_to_first_order <= 30 THEN 1 ELSE 0 END) as activated_d30,
ROUND(100.0 * SUM(CASE WHEN days_to_first_order <= 7 THEN 1 ELSE 0 END) / COUNT(*), 2) as activation_rate_d7
FROM consumers
WHERE created_at >= CURRENT_DATE - INTERVAL 90 DAY
GROUP BY DATE(created_at)
ORDER BY cohort_date DESC
Вопросы:
- Кто быстрее активируется? (обычно потребители быстрее)
- Есть ли bottleneck в одной из сторон?
R — Retention (Удержание)
Критичный метрик для двустороннего маркетплейса:
-- Retention поставщиков (активные листинги в месяц)
WITH cohort AS (
SELECT
DATE(created_at) as cohort_date,
id as supplier_id
FROM suppliers
WHERE created_at >= CURRENT_DATE - INTERVAL 180 DAY
)
SELECT
c.cohort_date,
DATEDIFF(month, c.cohort_date, CURRENT_DATE) as months_since_signup,
COUNT(*) as suppliers_in_cohort,
SUM(CASE WHEN DATEDIFF(month, c.cohort_date, l.updated_at) <= months_since_signup
AND l.updated_at >= CURRENT_DATE - INTERVAL 30 DAY THEN 1 ELSE 0 END) as active_suppliers,
ROUND(100.0 * SUM(CASE WHEN l.updated_at >= CURRENT_DATE - INTERVAL 30 DAY THEN 1 ELSE 0 END) / COUNT(*), 2) as retention_pct
FROM cohort c
LEFT JOIN listings l ON c.supplier_id = l.supplier_id
GROUP BY c.cohort_date, months_since_signup
ORDER BY cohort_date DESC, months_since_signup
UNION ALL
-- Retention потребителей (повторные заказы в месяц)
WITH cohort AS (
SELECT
DATE(created_at) as cohort_date,
id as consumer_id
FROM consumers
WHERE created_at >= CURRENT_DATE - INTERVAL 180 DAY
)
SELECT
c.cohort_date,
DATEDIFF(month, c.cohort_date, CURRENT_DATE) as months_since_signup,
COUNT(*) as consumers_in_cohort,
SUM(CASE WHEN o.created_at >= CURRENT_DATE - INTERVAL 30 DAY THEN 1 ELSE 0 END) as repeat_consumers,
ROUND(100.0 * SUM(CASE WHEN o.created_at >= CURRENT_DATE - INTERVAL 30 DAY THEN 1 ELSE 0 END) / COUNT(*), 2) as retention_pct
FROM cohort c
LEFT JOIN orders o ON c.consumer_id = o.consumer_id
GROUP BY c.cohort_date, months_since_signup
Вопросы:
- Какая сторона быстрее уходит?
- Есть ли тренд улучшения/ухудшения?
R2 — Revenue (Монетизация)
SELECT
DATE(created_at) as day,
COUNT(DISTINCT consumer_id) as buyers,
COUNT(DISTINCT supplier_id) as sellers_with_sales,
COUNT(*) as orders,
SUM(total_amount) as gmv, -- Gross Merchandise Value
SUM(platform_fee) as platform_revenue,
SUM(supplier_payout) as supplier_payouts,
ROUND(100.0 * SUM(platform_fee) / SUM(total_amount), 2) as take_rate_pct,
ROUND(AVG(total_amount), 2) as avg_order_value
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
GROUP BY DATE(created_at)
ORDER BY day DESC
O — Opening (Optional, но важно для маркетплейса)
Мощность предложения и спроса:
-- Баланс спроса и предложения
SELECT
DATE(current_date) as day,
-- ПРЕДЛОЖЕНИЕ (Supply Side)
COUNT(DISTINCT ls.supplier_id) as active_suppliers,
COUNT(DISTINCT ls.id) as total_listings,
SUM(CASE WHEN ls.status = 'active' THEN 1 ELSE 0 END) as active_listings,
ROUND(100.0 * SUM(CASE WHEN ls.status = 'active' THEN 1 ELSE 0 END) / COUNT(DISTINCT ls.id), 2) as activation_rate,
-- СПРОС (Demand Side)
COUNT(DISTINCT co.id) as dau_consumers,
SUM(CASE WHEN co.searched_today THEN 1 ELSE 0 END) as searchers,
SUM(CASE WHEN co.viewed_listings THEN 1 ELSE 0 END) as viewers,
-- БАЛАНС
ROUND(COUNT(DISTINCT ls.supplier_id) * 1.0 / NULLIF(COUNT(DISTINCT co.id), 0), 2) as suppliers_per_consumer,
ROUND(COUNT(DISTINCT ls.id) * 1.0 / NULLIF(COUNT(DISTINCT co.id), 0), 2) as listings_per_consumer
FROM listings ls
CROSS JOIN consumers co
WHERE ls.created_at >= CURRENT_DATE - INTERVAL 1 DAY
AND co.last_active >= CURRENT_DATE - INTERVAL 1 DAY
GROUP BY DATE(current_date)
Ключевые метрики баланса
1. Ratio поставщиков к потребителям (S/C Ratio)
SELECT
ROUND(COUNT(DISTINCT supplier_id) * 1.0 / NULLIF(COUNT(DISTINCT consumer_id), 0), 2) as sc_ratio
FROM (
SELECT DISTINCT supplier_id, NULL as consumer_id FROM listings WHERE created_at >= CURRENT_DATE - 7
UNION ALL
SELECT NULL, consumer_id FROM orders WHERE created_at >= CURRENT_DATE - 7
)
Оптимальный диапазон: 1:10 до 1:50 (зависит от вертикали)
- Низкое (1:100+) — дефицит предложения, потребители не находят
- Высокое (1:5) — переизбыток предложения, сложно продать поставщикам
2. Listing coverage — сколько активных листингов на потребителя
SELECT
DATE(current_date) as day,
COUNT(DISTINCT consumer_id) as consumers,
COUNT(DISTINCT id) as active_listings,
ROUND(COUNT(DISTINCT id) * 1.0 / COUNT(DISTINCT consumer_id), 2) as listings_per_consumer
FROM listings
WHERE status = 'active'
AND created_at >= CURRENT_DATE - INTERVAL 30 DAY
GROUP BY DATE(current_date)
Оптимальный диапазон: 10-50 листингов на активного потребителя
3. Поиск по предложению (Search-to-booking ratio)
SELECT
DATE(created_at) as day,
COUNT(DISTINCT CASE WHEN event_type = 'search' THEN user_id END) as searchers,
COUNT(DISTINCT CASE WHEN event_type = 'view_listing' THEN user_id END) as viewers,
COUNT(DISTINCT CASE WHEN event_type = 'booking' THEN user_id END) as bookers,
ROUND(100.0 * COUNT(DISTINCT CASE WHEN event_type = 'view_listing' THEN user_id END) / NULLIF(COUNT(DISTINCT CASE WHEN event_type = 'search' THEN user_id END), 0), 2) as search_to_view_pct,
ROUND(100.0 * COUNT(DISTINCT CASE WHEN event_type = 'booking' THEN user_id END) / NULLIF(COUNT(DISTINCT CASE WHEN event_type = 'search' THEN user_id END), 0), 2) as search_to_booking_pct
FROM events
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
GROUP BY DATE(created_at)
ORDER BY day DESC
Как балансировать спрос и предложение
Сценарий 1: Переизбыток предложения (больше поставщиков, меньше потребителей)
Признаки:
- Listings per consumer > 100
- S/C ratio > 1:5
- Search to booking rate < 3%
- Suppliers с нулевыми продажами
Действия:
1. Минимизировать поставщиков низкого качества (нет продаж за 90 дней)
2. Повысить требования к входу для новых поставщиков
3. Удвоить spend на акквизицию потребителей
4. Поощрять поставщиков с высокими ratings
SQL для выявления мертвых листингов:
SELECT
supplier_id,
COUNT(*) as total_listings,
SUM(CASE WHEN days_since_order > 90 THEN 1 ELSE 0 END) as inactive_90d,
MIN(last_order_date) as last_sale
FROM listings
WHERE status = 'active'
GROUP BY supplier_id
HAVING COUNT(*) > 0 AND MIN(last_order_date) < CURRENT_DATE - INTERVAL 90 DAY
ORDER BY last_sale
Сценарий 2: Дефицит предложения (больше потребителей, меньше поставщиков)
Признаки:
- Listings per consumer < 5
- S/C ratio < 1:100
- High search churn (потребители не находят)
- Search to booking rate > 15%
Действия:
1. Агрессивная акквизиция поставщиков (бонусы, маркетинг)
2. Упростить процесс создания листинга
3. Посеять контент (вы сами создаёте листинги)
4. Снизить требования к качеству на время
Мониторинг отсутствия предложения:
SELECT
search_query,
COUNT(*) as searches,
SUM(CASE WHEN results = 0 THEN 1 ELSE 0 END) as no_results,
ROUND(100.0 * SUM(CASE WHEN results = 0 THEN 1 ELSE 0 END) / COUNT(*), 2) as no_results_pct
FROM search_events
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
GROUP BY search_query
HAVING COUNT(*) > 100
ORDER BY no_results_pct DESC
LIMIT 20
Дашборд баланса спроса-предложения
SELECT
DATE(current_date) as day,
-- Supply metrics
COUNT(DISTINCT CASE WHEN type = 'supplier' THEN id END) as total_suppliers,
SUM(CASE WHEN type = 'supplier' AND listings_count > 0 THEN 1 ELSE 0 END) as active_suppliers,
SUM(CASE WHEN type = 'supplier' THEN listings_count ELSE 0 END) as total_listings,
ROUND(100.0 * SUM(CASE WHEN type = 'supplier' AND orders_last_30d > 0 THEN 1 ELSE 0 END) / NULLIF(COUNT(DISTINCT CASE WHEN type = 'supplier' THEN id END), 0), 2) as supplier_sales_rate,
-- Demand metrics
COUNT(DISTINCT CASE WHEN type = 'consumer' THEN id END) as total_consumers,
SUM(CASE WHEN type = 'consumer' AND orders_last_30d > 0 THEN 1 ELSE 0 END) as repeat_consumers,
-- Balance metrics
ROUND(SUM(CASE WHEN type = 'supplier' THEN listings_count ELSE 0 END) * 1.0 / NULLIF(COUNT(DISTINCT CASE WHEN type = 'consumer' THEN id END), 0), 2) as listings_per_consumer,
ROUND(COUNT(DISTINCT CASE WHEN type = 'supplier' AND listings_count > 0 THEN id END) * 1.0 / NULLIF(COUNT(DISTINCT CASE WHEN type = 'consumer' THEN id END), 0), 2) as suppliers_per_consumer,
-- Health
ROUND(100.0 * SUM(CASE WHEN type = 'supplier' AND listings_count > 0 THEN 1 ELSE 0 END) / NULLIF(COUNT(DISTINCT CASE WHEN type = 'supplier' THEN id END), 0), 2) as supplier_activation_rate,
ROUND(100.0 * SUM(CASE WHEN type = 'consumer' AND orders_last_30d > 0 THEN 1 ELSE 0 END) / NULLIF(COUNT(DISTINCT CASE WHEN type = 'consumer' THEN id END), 0), 2) as consumer_activity_rate
FROM users
WHERE created_at >= CURRENT_DATE - INTERVAL 1 DAY
GROUP BY DATE(current_date)
Главный принцип
Успех маркетплейса = холодный запуск (chicken-and-egg) + баланс + качество исполнения.
Всегда отслеживай обе стороны одновременно. Когда одна сторона падает, весь маркетплейс идёт вниз.