库存周转率=SUM(销售数量)/((期初库存+期末库存)/2),需用CTE分别取各产品在指定周期内最近的期初、期末库存快照,再关联销售汇总计算,注意防除零、NULL及整数除法截断。

库存周转率的SQL计算公式怎么写
库存周转率 = SUM(销售数量) / AVG(期初库存 + 期末库存) / 2,但实际中不能直接用 AVG() 对跨行库存求平均——必须先算出每个产品的期初、期末库存,再手动计算平均库存。常见错误是直接对 inventory 字段用 AVG(),结果把不同时间点的库存混在一起平均,完全失真。
核心思路:用子查询或 CTE 分别取出每个产品的最早(期初)和最晚(期末)库存快照,再与销售汇总表 JOIN 计算。假设你有三张表:products(产品主表)、inventory_snapshots(每日库存快照,含 product_id、date、quantity)、sales(销售记录,含 product_id、qty、sale_date)。
如何获取每个产品的期初和期末库存
不能依赖“某天快照=期初”,必须按业务周期定义时间范围(比如自然年、财年、或指定区间)。假设统计 2024 年全年,则:
- 期初库存 = 2024-01-01 当天的
quantity,若当天无记录,取最近一次date <= '2024-01-01'的快照 - 期末库存 = 2024-12-31 当天的
quantity,若无则取最近一次date <= '2024-12-31'
推荐用窗口函数避免多次子查询:
WITH stock_bounds AS (
SELECT
product_id,
FIRST_VALUE(quantity) OVER (PARTITION BY product_id ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS opening,
LAST_VALUE(quantity) OVER (PARTITION BY product_id ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS closing
FROM inventory_snapshots
WHERE date BETWEEN '2024-01-01' AND '2024-12-31'
)注意:FIRST_VALUE/LAST_VALUE 默认只在当前窗口帧生效,必须显式加 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 才能取到整个分区的首尾值。
销售总量怎么聚合才准确
销售表里可能有退货(qty 为负)、多币种、未确认订单等干扰项。直接 SUM(qty) 很危险。
- 过滤掉状态非
'completed'或'shipped'的记录 - 排除
qty < 0的退货单(或单独建退货表,不混在 sales 表里) - 确认
sale_date在目标周期内,且与库存周期严格对齐(例如销售日期在 2024-01-01 至 2024-12-31,库存也取同一区间)
示例聚合:
SELECT product_id, SUM(qty) AS total_sold FROM sales WHERE sale_date BETWEEN '2024-01-01' AND '2024-12-31' AND status = 'shipped' AND qty > 0 GROUP BY product_id
最终 SQL 要注意除零和 NULL
有些产品可能全年没卖出去(total_sold = 0),或没有库存快照(opening 或 closing 为 NULL),直接除会报错或返回 NULL。
- 用
NULLIF(avg_inventory, 0)防止除零 - 用
COALESCE(total_sold, 0)确保分子不为空 - 平均库存要显式写成
(opening + closing) / 2.0,避免整数除法截断(尤其在 PostgreSQL/SQL Server 中)
完整片段示意:
SELECT p.product_name, COALESCE(s.total_sold, 0) * 1.0 / NULLIF((ib.opening + ib.closing) / 2.0, 0) AS turnover_rate FROM products p LEFT JOIN stock_bounds ib ON p.product_id = ib.product_id LEFT JOIN sales_summary s ON p.product_id = s.product_id;
真正麻烦的不是写这个 SQL,而是确保 inventory_snapshots 的采集频率足够高、时间戳精确到日、且覆盖所有产品——漏一条快照,整个产品的周转率就不可信。

















