SQL для e-commerce аналитика

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это знать

E-commerce — массивный sector analytics jobs. Wildberries, Ozon, Lamoda, Avito, Amazon — все нанимают. Специфические метрики: GMV, AOV, cart abandonment, repeat purchase.

На собесах в e-commerce SQL вопросы domain-oriented.

Ключевые таблицы

-- orders
(id, user_id, created_at, status, shipping_cost, discount)

-- order_items
(id, order_id, product_id, quantity, price)

-- products
(id, name, category, brand, cost)

-- users
(id, signup_at, email)

-- sessions
(id, user_id, started_at, device, utm_source)

GMV (Gross Merchandise Value)

Total value sold:

SELECT SUM(quantity * price) AS gmv
FROM order_items oi
JOIN orders o ON oi.order_id = o.id
WHERE o.status = 'completed'
    AND o.created_at BETWEEN '2026-04-01' AND '2026-04-30';

Per day:

SELECT
    DATE(o.created_at) AS day,
    SUM(oi.quantity * oi.price) AS gmv
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
WHERE o.status = 'completed'
GROUP BY 1
ORDER BY 1;

AOV (Average Order Value)

SELECT
    AVG(order_total) AS aov
FROM (
    SELECT order_id, SUM(quantity * price) AS order_total
    FROM order_items
    GROUP BY order_id
) sub;

По сегментам:

SELECT
    device,
    AVG(order_total) AS aov,
    COUNT(DISTINCT order_id) AS orders
FROM (
    SELECT o.id AS order_id, o.device,
           SUM(oi.quantity * oi.price) AS order_total
    FROM orders o
    JOIN order_items oi ON oi.order_id = o.id
    GROUP BY o.id, o.device
) s
GROUP BY device;

Cart abandonment

WITH cart_events AS (
    SELECT user_id, session_id,
           MAX(CASE WHEN event = 'cart_add' THEN 1 ELSE 0 END) AS added_to_cart,
           MAX(CASE WHEN event = 'checkout_start' THEN 1 ELSE 0 END) AS started_checkout,
           MAX(CASE WHEN event = 'purchase' THEN 1 ELSE 0 END) AS purchased
    FROM events
    WHERE created_at BETWEEN '2026-04-01' AND '2026-04-30'
    GROUP BY 1, 2
)
SELECT
    SUM(added_to_cart) AS carts_created,
    SUM(CASE WHEN added_to_cart AND NOT purchased THEN 1 ELSE 0 END) AS abandoned,
    SUM(CASE WHEN added_to_cart AND NOT purchased THEN 1 ELSE 0 END) * 100.0 /
        SUM(added_to_cart) AS abandonment_rate
FROM cart_events;

Repeat purchase rate

WITH user_orders AS (
    SELECT user_id, COUNT(*) AS order_count
    FROM orders
    WHERE status = 'completed'
    GROUP BY user_id
)
SELECT
    COUNT(*) AS total_buyers,
    COUNT(CASE WHEN order_count >= 2 THEN 1 END) AS repeat_buyers,
    COUNT(CASE WHEN order_count >= 2 THEN 1 END) * 100.0 / COUNT(*) AS repeat_rate
FROM user_orders;

Days between purchases

WITH ordered AS (
    SELECT
        user_id,
        created_at,
        LAG(created_at) OVER (PARTITION BY user_id ORDER BY created_at) AS prev_order
    FROM orders
    WHERE status = 'completed'
)
SELECT
    AVG(EXTRACT(DAY FROM (created_at - prev_order))) AS avg_days_between
FROM ordered
WHERE prev_order IS NOT NULL;

Conversion rate

WITH session_outcomes AS (
    SELECT
        s.id AS session_id,
        s.user_id,
        MAX(CASE WHEN o.id IS NOT NULL THEN 1 ELSE 0 END) AS converted
    FROM sessions s
    LEFT JOIN orders o ON o.user_id = s.user_id
        AND o.created_at BETWEEN s.started_at AND s.started_at + INTERVAL '1 hour'
    WHERE s.started_at >= CURRENT_DATE - 30
    GROUP BY 1, 2
)
SELECT
    AVG(converted) * 100 AS session_cr
FROM session_outcomes;

Product metrics

Best-selling

SELECT
    p.name,
    p.category,
    SUM(oi.quantity) AS units_sold,
    SUM(oi.quantity * oi.price) AS revenue
FROM products p
JOIN order_items oi ON oi.product_id = p.id
JOIN orders o ON o.id = oi.order_id
WHERE o.status = 'completed'
    AND o.created_at BETWEEN '2026-04-01' AND '2026-04-30'
GROUP BY p.id, p.name, p.category
ORDER BY revenue DESC
LIMIT 20;

Return rate per product

SELECT
    p.id, p.name,
    COUNT(DISTINCT CASE WHEN o.status = 'completed' THEN o.id END) AS orders,
    COUNT(DISTINCT CASE WHEN o.status = 'returned' THEN o.id END) AS returns,
    COUNT(DISTINCT CASE WHEN o.status = 'returned' THEN o.id END) * 100.0 /
        NULLIF(COUNT(DISTINCT o.id), 0) AS return_rate
FROM products p
JOIN order_items oi ON oi.product_id = p.id
JOIN orders o ON o.id = oi.order_id
GROUP BY p.id, p.name
HAVING COUNT(DISTINCT o.id) > 50
ORDER BY return_rate DESC;

Product margin

SELECT
    p.name,
    AVG(oi.price) AS avg_selling_price,
    p.cost,
    AVG(oi.price) - p.cost AS margin_per_unit,
    (AVG(oi.price) - p.cost) / AVG(oi.price) * 100 AS margin_pct
FROM products p
JOIN order_items oi ON oi.product_id = p.id
GROUP BY p.id, p.name, p.cost;

Customer LTV

WITH user_ltv AS (
    SELECT
        user_id,
        MIN(created_at) AS first_order,
        COUNT(*) AS total_orders,
        SUM(order_total) AS total_spent
    FROM orders_with_totals
    GROUP BY user_id
)
SELECT
    AVG(total_spent) AS ltv,
    AVG(total_orders) AS avg_orders_per_user
FROM user_ltv;

First-time vs repeat

WITH order_rank AS (
    SELECT
        id, user_id, order_total,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) AS order_num
    FROM orders
    WHERE status = 'completed'
)
SELECT
    CASE WHEN order_num = 1 THEN 'First-time' ELSE 'Repeat' END AS customer_type,
    COUNT(*) AS orders,
    AVG(order_total) AS aov,
    SUM(order_total) AS revenue
FROM order_rank
GROUP BY 1;

Categories analysis

SELECT
    p.category,
    SUM(oi.quantity * oi.price) AS revenue,
    COUNT(DISTINCT o.id) AS orders,
    COUNT(DISTINCT o.user_id) AS buyers,
    AVG(oi.quantity * oi.price) AS avg_line_value
FROM products p
JOIN order_items oi ON oi.product_id = p.id
JOIN orders o ON o.id = oi.order_id
WHERE o.status = 'completed'
GROUP BY p.category
ORDER BY revenue DESC;

Dashboard

E-commerce analyst typical dashboards:

  • GMV / revenue / orders
  • Conversion funnel (visit → cart → checkout → purchase)
  • AOV trend
  • Top products / categories
  • Customer segments (new vs returning)
  • Attribution per channel
  • Return rate
  • Inventory turnover (advanced)

На собесе

«Как бы measured e-commerce success?»

NSM often GMV или contribution margin.

Inputs: traffic, CR, AOV, repeat rate.

Drill: product mix, channels.

«Как investigate drop в sales?»

RCA: where drop? new vs repeat customers? specific categories? channels?

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

FAQ

Gross vs Net GMV?

Gross — before returns. Net — after. Clarify definition.

AOV или ARPU?

AOV per order. ARPU per user. Related но different.

Unit economics?

For physical goods — shipping, cost, margin, returns all matter. More complex than SaaS.


Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.