Как посчитать GMV в SQL
Содержание:
Зачем GMV
Маркетплейс отчитывается: «GMV вырос на 30% за квартал». Аудитория хлопает. Аналитик копает: revenue компании выросла на 5%, take rate упал с 10% до 8%. Реальная картина — рост за счёт скидок и промо, не органический.
GMV — главная top-line метрика маркетплейсов и e-com платформ. Аналитики постоянно строят: GMV по категориям, по селлерам, динамика по неделям, GMV новых vs повторных. В статье — SQL-запросы и разница между GMV / Revenue / Net GMV.
Что такое GMV
GMV (Gross Merchandise Value) — суммарная стоимость товаров и услуг, проданных через платформу за период.
GMV = Σ (item_price × quantity) для всех заказов в статусе оплачен/доставленGMV ≠ Revenue. На маркетплейсе GMV — это деньги, которые прошли через платформу, а Revenue — комиссия (take rate × GMV) + дополнительные сервисы.
Базовый расчёт
Данные: orders(order_id, user_id, total, status, created_at).
SELECT
SUM(total) AS gmv
FROM orders
WHERE status IN ('paid', 'delivered')
AND created_at >= '2026-04-01'
AND created_at < '2026-05-01';Важно: фильтр по status обязателен. Корзины (pending) и отменённые (cancelled) не считаются в GMV.
GMV по разрезам
По категориям
SELECT
category,
SUM(total) AS gmv,
COUNT(*) AS orders,
SUM(total)::NUMERIC / NULLIF(COUNT(*), 0) AS aov
FROM orders
WHERE status IN ('paid', 'delivered')
AND created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY category
ORDER BY gmv DESC;По селлерам / мерчантам
SELECT
seller_id,
SUM(total) AS gmv,
COUNT(DISTINCT user_id) AS buyers,
COUNT(*) AS orders
FROM orders
WHERE status IN ('paid', 'delivered')
AND created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY seller_id
ORDER BY gmv DESC
LIMIT 100;Новые vs повторные покупатели
WITH user_orders AS (
SELECT
user_id,
total,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) AS order_num
FROM orders
WHERE status IN ('paid', 'delivered')
)
SELECT
CASE WHEN order_num = 1 THEN 'new' ELSE 'repeat' END AS buyer_type,
SUM(total) AS gmv,
COUNT(*) AS orders,
AVG(total) AS aov
FROM user_orders
GROUP BY 1;Здоровое соотношение: 60-80% GMV из повторных в зрелом маркетплейсе.
GMV vs Revenue vs Net GMV
| Метрика | Что считаем | Формула |
|---|---|---|
| GMV | Полная стоимость заказов | SUM(total) для всех оплаченных |
| Net GMV | GMV минус возвраты, отмены, скидки от платформы | SUM(total) - SUM(refunds) - SUM(platform_discounts) |
| Revenue | Заработок платформы | SUM(commission) + SUM(ads_revenue) + ... |
WITH gross AS (
SELECT SUM(total) AS gmv
FROM orders
WHERE status IN ('paid', 'delivered')
AND created_at >= '2026-04-01' AND created_at < '2026-05-01'
),
refunds AS (
SELECT SUM(refund_amount) AS refunded
FROM order_refunds
WHERE refund_date >= '2026-04-01' AND refund_date < '2026-05-01'
),
platform_discounts AS (
SELECT SUM(discount_amount) AS discount
FROM order_discounts
WHERE source = 'platform'
AND created_at >= '2026-04-01' AND created_at < '2026-05-01'
),
revenue AS (
SELECT SUM(commission_amount) AS commission
FROM order_commissions
WHERE commission_date >= '2026-04-01' AND commission_date < '2026-05-01'
)
SELECT
g.gmv,
g.gmv - COALESCE(r.refunded, 0) - COALESCE(pd.discount, 0) AS net_gmv,
rev.commission AS revenue,
rev.commission::NUMERIC / NULLIF(g.gmv, 0) * 100 AS take_rate_pct
FROM gross g
CROSS JOIN refunds r
CROSS JOIN platform_discounts pd
CROSS JOIN revenue rev;GMV динамика и сезонность
WITH daily AS (
SELECT
DATE_TRUNC('day', created_at) AS day,
SUM(total) AS gmv
FROM orders
WHERE status IN ('paid', 'delivered')
AND created_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY 1
)
SELECT
day,
gmv,
AVG(gmv) OVER (
ORDER BY day
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS gmv_7d_avg,
gmv * 100.0 / NULLIF(LAG(gmv, 7) OVER (ORDER BY day), 0) - 100 AS wow_change_pct
FROM daily
ORDER BY day;7-day rolling average сглаживает дневной шум. WoW change — это GMV сегодня vs неделю назад, в процентах.
Частые ошибки
Ошибка 1. Включать pending / cancelled
SUM(total) FROM orders без фильтра status завышает GMV на 20-30%. Только paid / delivered / completed (зависит от воркфлоу).
Ошибка 2. Игнорировать возвраты
В первый месяц GMV кажется высоким, через 30 дней приходит волна refund — реальный GMV ниже. Считайте Net GMV для управленческих решений.
Ошибка 3. GMV в смешанной валюте
Заказы в рублях, тенге, лирах — сложить нельзя без приведения. SUM(amount * fx_rate) с курсом на дату транзакции.
Ошибка 4. Подмена GMV на Revenue в отчёте
«GMV вырос на 30%» — но это revenue (комиссия). Маркетинг радуется, финдиректор спрашивает: «а где GMV?». Договоритесь о терминологии в команде.
Ошибка 5. Дабл-каунт refund
Если refund попадает и в order_refunds, и снижает status заказа на refunded — можно вычесть дважды. Проверьте, что данные не пересекаются.
Ошибка 6. GMV без учёта tax
Иногда total — это GMV с НДС, иногда без. На маркетплейсах часто GMV считают без tax (Net of VAT). Уточните у финансистов формулу.
Связанные темы
- Что такое GMV простыми словами
- Как посчитать AOV в SQL
- Кейс: GMV упал
- Как посчитать unit-экономику в SQL
FAQ
GMV — это revenue?
Нет. GMV — стоимость заказов через платформу. Revenue — заработок платформы (комиссия + сервисы). На WB take rate ~12-15% → revenue ≈ 12-15% от GMV.
Какая take rate считается хорошей?
Маркетплейсы: 5-15%. Доставка еды (UberEats, Delivery): 20-35%. Билеты на концерты: 5-10%. Travel: 15-25%. Сравнивайте с конкурентами в своей категории.
GMV или Net GMV — что показывать?
Внутренне — обе. PR-релизы обычно про GMV (выглядит больше). Управленческие отчёты — Net GMV (отражает реальный экономический эффект).
GMV считать по дате заказа или дате доставки?
Чаще по дате заказа (когда деньги прошли). Но для логистических метрик — по дате доставки. Зафиксируйте определение в команде.
Как сравнить GMV между маркетплейсами?
Сложно — разные курсы валют, методология (с tax / без), включение цифровых товаров. Используйте опубликованные цифры компаний, не стройте сравнение «на коленке».