Как посчитать Items Per Order в SQL
WHERE country IN ('RU', 'KZ') для строки, где country IS NULL?Содержание:
Зачем Items Per Order
Items Per Order (IPO) — частный случай basket size, но более узкий: считает уникальные позиции (SKU), а не количество единиц товара. Это индикатор того, насколько широко клиенты осваивают ассортимент.
Формула
IPO (lines) = total_distinct_lines / total_orders
IPO (units) = total_quantities / total_ordersБазовый расчёт
WITH order_stats AS (
SELECT
order_id,
COUNT(*) AS lines,
SUM(quantity) AS units
FROM order_items
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY order_id
)
SELECT
COUNT(*) AS orders,
AVG(lines) AS avg_lines_per_order,
AVG(units) AS avg_units_per_order,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY lines) AS median_lines
FROM order_stats;Распределение
WITH order_lines AS (
SELECT order_id, COUNT(*) AS lines
FROM order_items
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY order_id
)
SELECT
CASE
WHEN lines = 1 THEN '1 SKU'
WHEN lines = 2 THEN '2 SKUs'
WHEN lines = 3 THEN '3 SKUs'
WHEN lines <= 5 THEN '4-5 SKUs'
ELSE '6+ SKUs'
END AS bucket,
COUNT(*) AS orders,
COUNT(*)::NUMERIC * 100 / SUM(COUNT(*)) OVER () AS pct
FROM order_lines
GROUP BY bucket
ORDER BY MIN(lines);Если 80%+ заказов состоят из одного SKU — кросс-селл не работает.
По каналам
SELECT
o.acquisition_channel,
COUNT(DISTINCT oi.order_id) AS orders,
COUNT(*)::NUMERIC / NULLIF(COUNT(DISTINCT oi.order_id), 0) AS avg_lines_per_order,
SUM(oi.quantity)::NUMERIC / NULLIF(COUNT(DISTINCT oi.order_id), 0) AS avg_units_per_order,
AVG(oi.total) AS aov
FROM order_items oi
JOIN orders o USING (order_id)
WHERE o.created_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY o.acquisition_channel
ORDER BY avg_lines_per_order DESC;Динамика IPO по месяцам
SELECT
DATE_TRUNC('month', order_date) AS month,
COUNT(DISTINCT order_id) AS orders,
COUNT(*)::NUMERIC / NULLIF(COUNT(DISTINCT order_id), 0) AS ipo_lines,
SUM(quantity)::NUMERIC / NULLIF(COUNT(DISTINCT order_id), 0) AS ipo_units,
-- AOV breakdown
AVG(unit_price * quantity) AS avg_line_value,
SUM(total) / NULLIF(COUNT(DISTINCT order_id), 0) AS aov
FROM order_items
WHERE order_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY 1
ORDER BY 1;Связь IPO и AOV
WITH order_summary AS (
SELECT
order_id,
COUNT(*) AS lines,
SUM(total) AS order_value
FROM order_items
WHERE order_date >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY order_id
)
SELECT
lines AS items_in_order,
COUNT(*) AS orders,
AVG(order_value) AS avg_order_value
FROM order_summary
WHERE lines <= 10
GROUP BY lines
ORDER BY lines;Связь обычно линейная: больше позиций — выше AOV. Но предельная ценность каждой следующей позиции в заказе часто снижается.
Частые ошибки
Ошибка 1. Позиции против единиц. Два одинаковых SKU — это одна позиция, но две единицы товара. Показывайте обе метрики.
Ошибка 2. Наборы (бандлы). Набор — это один SKU, но внутри него 5 отдельных товаров. Как считать?
Ошибка 3. Отменённые позиции. Часть товаров в заказе отменили. Считайте по факту оставшихся позиций (net).
Ошибка 4. Составные продукты. Подписочный набор — это «одна позиция»? Зависит от того, как вы его учитываете.
Ошибка 5. Компромисс между IPO и AOV. Высокий AOV можно получить и за счёт меньшего числа дорогих товаров, и за счёт большего числа позиций. Это стратегический выбор.
Связанные темы
- Как посчитать basket size в SQL
- Как посчитать AOV в SQL
- Как посчитать attach rate в SQL
- Как посчитать cross-sell rate в SQL
FAQ
Чем IPO отличается от Basket Size?
Часто это синонимы, но нюанс есть: IPO обычно считают по позициям (lines), а basket size — по единицам товара (units). Главное — заранее договориться, что именно вы измеряете.
Как увеличить IPO?
Работают три рычага: товарные рекомендации, блок «с этим товаром покупают» и порог бесплатной доставки, который мотивирует добрать корзину до нужной суммы.
В чём сложность с наборами?
Решите заранее, считать набор как один атомарный SKU или разбивать на составляющие товары. Если вы продаёте бандлы, эту методику нужно зафиксировать до расчётов, иначе цифры не сойдутся.
Как считать IPO в подписке?
В подписке из месяца в месяц идут одни и те же товары, поэтому IPO почти не меняется. Из-за этого эффект кросс-селла в подписке по IPO не виден — его нужно смотреть отдельно.
IPO падает — что значит?
Значит, растёт доля заказов из одного товара. Причины бывают разные: высокая стоимость доставки не даёт добирать корзину, либо акции гонят трафик на один конкретный товар.