Как посчитать gross margin по продукту в SQL

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

Зачем margin по продукту

Average gross margin компании 45% скрывает то, что половина SKU имеет 60%, а другая — 20%. Маржу по продукту считают, чтобы:

  • найти убыточные позиции (cost outpaces price)
  • сегментировать портфолио
  • скорректировать pricing
  • prioritize marketing на high-margin

Формула

gross_margin = (revenue − COGS) / revenue

COGS = cost of goods sold — прямая стоимость производства / закупки.

Margin в SQL

SELECT
    p.product_id,
    p.product_name,
    SUM(oi.quantity * oi.unit_price) AS revenue,
    SUM(oi.quantity * p.unit_cost) AS cogs,
    SUM(oi.quantity * oi.unit_price) - SUM(oi.quantity * p.unit_cost) AS gross_profit,
    (SUM(oi.quantity * oi.unit_price) - SUM(oi.quantity * p.unit_cost))::NUMERIC * 100
    / NULLIF(SUM(oi.quantity * oi.unit_price), 0) AS gross_margin_pct
FROM order_items oi
JOIN products p USING (product_id)
WHERE oi.order_date >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY p.product_id, p.product_name
HAVING SUM(oi.quantity) >= 10
ORDER BY gross_margin_pct DESC;

HAVING SUM(quantity) >= 10 — отсекает рандомные SKU с шумными margins.

Убыточные продукты

SELECT
    product_id,
    product_name,
    revenue,
    cogs,
    gross_margin_pct,
    CASE
        WHEN gross_margin_pct < 0 THEN 'LOSING_MONEY'
        WHEN gross_margin_pct < 10 THEN 'LOW_MARGIN'
        WHEN gross_margin_pct < 30 THEN 'OK'
        ELSE 'HIGH_MARGIN'
    END AS category
FROM product_margin_summary
WHERE gross_margin_pct < 10
ORDER BY revenue DESC;

Negative margin = продукт убыточный. High revenue + negative margin — особенно болезненно (масштабируете убытки).

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

Mix shift

Как меняется средний margin с продуктовым составом:

WITH monthly_mix AS (
    SELECT
        DATE_TRUNC('month', order_date)::DATE AS month,
        SUM(oi.quantity * oi.unit_price) AS revenue,
        SUM(oi.quantity * p.unit_cost) AS cogs,
        SUM(oi.quantity * (oi.unit_price - p.unit_cost))::NUMERIC * 100
        / NULLIF(SUM(oi.quantity * oi.unit_price), 0) AS blended_margin_pct
    FROM order_items oi
    JOIN products p USING (product_id)
    WHERE order_date >= CURRENT_DATE - INTERVAL '12 months'
    GROUP BY DATE_TRUNC('month', order_date)
)
SELECT * FROM monthly_mix ORDER BY month;

Падение average margin без individual SKU margin changes = mix shifted в сторону low-margin.

Частые ошибки

Ошибка 1. COGS не fully loaded. Помимо raw materials, COGS должна включать direct labor + manufacturing overhead. «Material-only COGS» завышает margin.

Ошибка 2. Allocated overhead. Office rent — не COGS. Это OpEx. Не путать.

Ошибка 3. FX timing. Multi-currency: revenue в момент sale, cost в момент purchase. Если currency двигалась — distortion.

Ошибка 4. Returns reducing revenue, not cogs. Return снижает revenue, но cost (производство) уже понесён. Net margin меньше.

Ошибка 5. Bulk discount in COGS. Если supplier даёт volume discount — фактическая cost ниже invoice. Use net price.

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

FAQ

Какой gross margin хороший?

SaaS: 70-85%. E-com: 30-50%. Manufacturing: 25-40%. Restaurants: 60-70% food, 20-30% labor.

Negative margin когда выгодно?

Loss leader (как iPad с low margin + accessories). Customer acquisition tool.

Margin vs markup?

Margin = profit / revenue. Markup = profit / cost. На 50% margin = 100% markup.

Standard vs actual COGS?

Standard — planned. Actual — фактический. Variance показывает procurement issues.

По channel margin?

Channels часто имеют разный mix. Compute by channel и сравнивайте.