Как посчитать Gross Margin в SQL
Содержание:
Зачем Gross Margin
Команда катит промо: «-30% на электронику, выручка +50%!». Финдиректор молча. На электронике маржа 12%, скидка 30% — продали в убыток. Gross margin резко упала: было 12%, стала -18%.
Gross margin — первая фильтрация: продукт хотя бы окупает свою себестоимость? Без неё нельзя обсуждать operating margin, EBITDA, unit-экономику. В статье — SQL для расчёта по разрезам и нюансы (что включать в COGS).
Что такое Gross Margin
Gross Margin — доля выручки, оставшаяся после вычета себестоимости проданных товаров (COGS).
Gross Margin (%) = (Revenue - COGS) / Revenue × 100%
Gross Profit ($) = Revenue - COGSCOGS (Cost of Goods Sold) — прямые затраты на товар: закупка, упаковка, доставка до клиента, иногда комиссии платежных систем.
Базовый расчёт
Данные: orders(order_id, item_id, quantity, price, cogs_per_item, created_at).
SELECT
SUM(quantity * price) AS revenue,
SUM(quantity * cogs_per_item) AS cogs,
SUM(quantity * price) - SUM(quantity * cogs_per_item) AS gross_profit,
(SUM(quantity * price) - SUM(quantity * cogs_per_item))::NUMERIC
* 100.0 / NULLIF(SUM(quantity * price), 0) AS gross_margin_pct
FROM orders
WHERE status = 'paid'
AND created_at >= '2026-04-01'
AND created_at < '2026-05-01';Важно: cogs_per_item должен быть нормализован — на момент продажи, не текущая закупочная цена. Иначе исторические маржи плывут.
Gross Margin по разрезам
По категориям
SELECT
p.category,
SUM(o.quantity * o.price) AS revenue,
SUM(o.quantity * o.cogs_per_item) AS cogs,
(SUM(o.quantity * o.price) - SUM(o.quantity * o.cogs_per_item))::NUMERIC
* 100.0 / NULLIF(SUM(o.quantity * o.price), 0) AS gross_margin_pct
FROM orders o
JOIN products p ON p.item_id = o.item_id
WHERE o.status = 'paid'
AND o.created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY p.category
ORDER BY gross_margin_pct ASC;Покажет категории с самой низкой маржой — кандидаты на pricing review.
По продуктам топ-100
SELECT
p.item_name,
SUM(o.quantity) AS units_sold,
SUM(o.quantity * o.price) AS revenue,
(SUM(o.quantity * o.price) - SUM(o.quantity * o.cogs_per_item))::NUMERIC
* 100.0 / NULLIF(SUM(o.quantity * o.price), 0) AS gross_margin_pct
FROM orders o
JOIN products p ON p.item_id = o.item_id
WHERE o.status = 'paid'
AND o.created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY p.item_id, p.item_name
HAVING SUM(o.quantity) >= 50
ORDER BY revenue DESC
LIMIT 100;HAVING SUM(quantity) >= 50 отсекает товары с микропродажами (1 продажа может дать 80% маржу из-за округлений).
Gross vs Contribution Margin
| Метрика | Что включает |
|---|---|
| Gross Margin | Revenue − Direct COGS (закупка, упаковка) |
| Contribution Margin | Revenue − Variable costs (COGS + переменные операционные расходы: доставка, обработка, commission) |
| Operating Margin | Revenue − Variable costs − Fixed overhead |
Contribution margin показывает, окупает ли продукт переменные затраты после прямой себестоимости.
SELECT
SUM(quantity * price) AS revenue,
SUM(quantity * cogs_per_item) AS cogs,
SUM(quantity * variable_cost_per_item) AS variable_costs,
-- Gross margin
(SUM(quantity * price) - SUM(quantity * cogs_per_item))::NUMERIC
* 100.0 / NULLIF(SUM(quantity * price), 0) AS gross_margin_pct,
-- Contribution margin
(SUM(quantity * price)
- SUM(quantity * cogs_per_item)
- SUM(quantity * variable_cost_per_item))::NUMERIC
* 100.0 / NULLIF(SUM(quantity * price), 0) AS contribution_margin_pct
FROM orders
WHERE status = 'paid';В e-com gross margin 40% при contribution margin 8% — значит, логистика и обработка съедают почти всё. Уменьшать вариативные затраты — приоритет.
Динамика Gross Margin
WITH monthly AS (
SELECT
DATE_TRUNC('month', created_at) AS month,
SUM(quantity * price) AS revenue,
SUM(quantity * cogs_per_item) AS cogs
FROM orders
WHERE status = 'paid'
AND created_at >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY 1
)
SELECT
month,
revenue,
cogs,
(revenue - cogs)::NUMERIC * 100.0 / NULLIF(revenue, 0) AS gm_pct,
(revenue - cogs)::NUMERIC * 100.0 / NULLIF(revenue, 0)
- LAG((revenue - cogs)::NUMERIC * 100.0 / NULLIF(revenue, 0))
OVER (ORDER BY month) AS mom_change_pp
FROM monthly
ORDER BY month;mom_change_pp — изменение маржи в процентных пунктах между месяцами. -2 п.п. — серьёзный сигнал.
Частые ошибки
Ошибка 1. Использовать актуальный COGS вместо исторического
Закупочная цена выросла на 15% — старые проданные товары задним числом «теряют» маржу. Нужен snapshot COGS на момент продажи. Зафиксируйте в orders.cogs_per_item при оформлении.
Ошибка 2. Включать скидки в COGS
Скидка — это уменьшение revenue, не увеличение COGS. Правильно: revenue = quantity * (price - discount_per_item), cogs остаётся как есть.
Ошибка 3. Не учитывать возвраты
Refund возвращает revenue, но COGS уже понесён (товар испорчен / не может быть продан). Считайте net gross margin = (revenue - refunds - cogs_of_refunded - cogs).
Ошибка 4. Смешивать GM услуг и физических товаров
Софт-подписка: GM 80%. Физический ритейл: GM 25%. В одной таблице с одинаковой формулой — несравнимая каша.
Ошибка 5. Игнорировать FX
Закупка в долларах, продажа в рублях, курс плавает. Зафиксируйте курс на момент сделки: cogs_per_item_rub = cogs_per_item_usd * fx_rate_at_purchase.
Ошибка 6. Игнорировать unit-эффект
«GM упал на 3 п.п.» — это от изменения цены / COGS, или от изменения mix категорий? Декомпозируйте: weighted shift due to price + due to COGS + due to mix.
Связанные темы
- Как посчитать contribution margin в SQL
- Что такое unit-экономика
- Как посчитать unit-economics в SQL
- Как посчитать payback period в SQL
FAQ
Какая Gross Margin считается хорошей?
Зависит от индустрии. Софт / SaaS — 70-85%. E-com физ. товары — 25-45%. FMCG — 15-30%. Рестораны — 60-70% food cost (т.е. GM 30-40%). Сравнивайте с конкурентами в категории.
Gross Margin или Contribution Margin — что показывать?
Финансовая отчётность — Gross. Операционные решения (стоит ли катить промо, какие SKU выводить) — Contribution. Идеально показывать обе.
Что входит в COGS для маркетплейса?
Сложный вопрос. Для платформы COGS — это payment processing, инфраструктура заказа, support. Для селлера — закупка + логистика. На платформе обычно используют take rate / GMV вместо классической GM.
GM упал на 5 п.п. — что делать?
Декомпозиция: 1) cost (закупка подорожала / валюта). 2) price (промо, скидки, давление конкурентов). 3) mix (сдвиг в более дешёвые категории). 4) waste (потери / возвраты). Найдите конкретный driver.
Как считать GM для бандлов / комплектов?
Распределите бандл price между компонентами proportionally к их standalone price. COGS — сумма COGS компонентов. Сложнее, но честно.