Как посчитать Gross Margin в SQL
events(created_at) нужно выбрать события за последние 7 дней с точностью до текущего момента (включая сегодняшние, ещё не завершённые сутки). Какое условие в WHERE корректнее?Содержание:
Зачем 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;Покажет категории с самой низкой маржой — кандидаты на пересмотр цен.
По продуктам топ-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 + переменные операционные расходы: доставка, обработка, комиссии) |
| 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% — старые проданные товары задним числом «теряют» маржу. Нужен снимок COGS на момент продажи. Зафиксируйте в orders.cogs_per_item при оформлении.
Ошибка 2. Включать скидки в COGS
Скидка — это уменьшение выручки, а не увеличение COGS. Правильно: revenue = quantity * (price - discount_per_item), cogs остаётся как есть.
Ошибка 3. Не учитывать возвраты
При возврате выручка сторнируется, но COGS уже понесён (товар испорчен или не может быть продан снова). Считайте чистую 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 категорий)? Декомпозируйте изменение на три вклада: от цены, от COGS и от сдвига в миксе.
Связанные темы
- Как посчитать contribution margin в SQL
- Что такое unit-экономика
- Как посчитать unit-economics в SQL
- Как посчитать payback period в SQL
FAQ
Какая Gross Margin считается хорошей?
Зависит от индустрии. Для софта и SaaS нормой считается 70–85%, для физических товаров в e-com — 25–45%, для FMCG — 15–30%. В ресторанах обычно говорят про себестоимость блюд (food cost) 60–70%, то есть gross margin выходит 30–40%. Ориентироваться стоит на конкурентов в своей категории, а не на абсолютные числа.
Gross Margin или Contribution Margin — что показывать?
Для финансовой отчётности показывают gross margin, а для операционных решений (стоит ли катить промо, какие SKU выводить из ассортимента) — contribution margin. В идеале держать перед глазами обе метрики.
Что входит в COGS для маркетплейса?
Это зависит от того, чью экономику вы считаете. Для самой платформы COGS — это эквайринг, инфраструктура обработки заказа и поддержка. Для продавца — закупка плюс логистика. Причём платформы обычно смотрят не на классическую gross margin, а на take rate относительно GMV.
GM упал на 5 п.п. — что делать?
Разложите падение на четыре возможные причины: подорожавшая закупка или курс валют (cost), промо, скидки и давление конкурентов (price), сдвиг продаж в более дешёвые категории (mix) и потери с возвратами (waste). Задача — найти конкретную причину, а не гадать по общей цифре.
Как считать GM для бандлов / комплектов?
Цену бандла распределите между компонентами пропорционально их отдельным (standalone) ценам, а COGS возьмите как сумму COGS всех компонентов. Считать так сложнее, зато честно.