Как посчитать Gross Margin в SQL

Закрепи формулу gross margin в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать gross margin в Telegram

Зачем 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 - COGS

COGS (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 margin в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать gross margin в Telegram

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.

Связанные темы

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 компонентов. Сложнее, но честно.