Как посчитать inventory days on hand в SQL
Содержание:
Зачем days on hand
Days on hand (DOH) показывает, на сколько дней хватит текущего inventory при historical sales velocity. 60 days — overstock (cash в товаре). 5 days — risk of stockout. Sweet spot обычно 15-30 days для fast-movers.
Формула
DOH = current_inventory / average_daily_salesaverage_daily_sales обычно за last 30-90 days.
DOH в SQL
WITH current_inventory AS (
SELECT sku_id, quantity_on_hand AS current_qty
FROM inventory_current
),
sales_velocity AS (
SELECT
sku_id,
COUNT(*) FILTER (WHERE sold_at >= CURRENT_DATE - INTERVAL '90 days') / 90.0 AS avg_daily_sales
FROM sales
GROUP BY sku_id
)
SELECT
i.sku_id,
i.current_qty,
s.avg_daily_sales,
i.current_qty / NULLIF(s.avg_daily_sales, 0) AS days_on_hand,
CASE
WHEN i.current_qty / NULLIF(s.avg_daily_sales, 0) < 7 THEN 'understock'
WHEN i.current_qty / NULLIF(s.avg_daily_sales, 0) < 30 THEN 'healthy'
WHEN i.current_qty / NULLIF(s.avg_daily_sales, 0) < 90 THEN 'overstock'
ELSE 'dead_stock'
END AS verdict
FROM current_inventory i
LEFT JOIN sales_velocity s USING (sku_id)
ORDER BY days_on_hand;Sort by understock — приоритет для reorder. Top dead_stock — кандидаты на discount/clearance.
Overstock vs understock
WITH categorized AS (
SELECT
CASE
WHEN days_on_hand < 7 THEN 'understock'
WHEN days_on_hand < 30 THEN 'healthy'
WHEN days_on_hand < 90 THEN 'overstock'
ELSE 'dead_stock'
END AS category,
SUM(current_qty * unit_cost) AS cash_value
FROM doh_with_cost
GROUP BY 1
)
SELECT * FROM categorized;Cash в dead_stock = liability. Это «замороженные» деньги.
По категориям
SELECT
product_category,
AVG(days_on_hand) AS avg_doh,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY days_on_hand) AS median_doh,
SUM(current_qty * unit_cost) AS total_inventory_value
FROM doh_table
JOIN products USING (sku_id)
GROUP BY product_category
ORDER BY avg_doh DESC;Категория с high avg DOH = либо seasonal (норма), либо проблема merchandising.
Частые ошибки
Ошибка 1. Sales velocity без сезонной корректировки. Winter coat DOH в июне выглядит overstock. Учитывайте сезонность.
Ошибка 2. Включать damaged/returned inventory. Reserved + damaged inventory нельзя продать. Filter to «sellable».
Ошибка 3. Velocity на коротком окне. 7-day velocity слишком шумная. 30-90 days стандарт.
Ошибка 4. Игнорировать lead time. Если reorder lead time 30 days, DOH меньше 30 = stockout risk.
Ошибка 5. DOH сам по себе. Combine с margin: overstock low-margin — small loss. Overstock high-margin — большая.
Связанные темы
- Как посчитать inventory turnover в SQL
- Как посчитать stockout rate в SQL
- Как посчитать sell-through rate в SQL
- Как посчитать fill rate в SQL
FAQ
Какой DOH хороший?
15-30 days для fast-movers. 30-60 для seasonal. 60+ может быть overstock.
DOH vs turnover ratio?
DOH = inverted. Turnover 12× = DOH ≈ 30 days. Эквивалентны.
Сезонность учитывать?
Yes — используйте last-year-same-period velocity, не plain 30-day.
Stockout vs DOH?
Stockout — текущая нехватка. DOH — прогноз нехватки.
Cash on dead stock?
Считайте отдельно — это immobilized capital. Discount или write-off.