库存周转率=期间销售成本/平均库存,不能直接对各产品周转率取avg(),必须先按产品计算再排序取最低;平均库存需用(期初+期末)/2,销售成本须与周期匹配,零库存时周转率应为null。

什么是库存周转率,为什么不能直接用 AVG() 计算
库存周转率 = 期间销售成本 / 平均库存,不是简单对单个产品的周转率取平均。直接对各产品周转率用 AVG() 会掩盖低周转单品——比如一个产品周转率为 0.1,另一个为 10,平均是 5.05,但你真正要找的是那个 0.1 的滞销品。
所以必须先按产品计算各自周转率,再排序取最低。关键点在于:平均库存需用期初 + 期末库存 / 2,不能只用期末库存;销售成本必须对应同一周期。
- 确保有字段:
product_id、cost_of_goods_sold(该周期内)、beginning_inventory、ending_inventory - 若只有期末库存快照,且无期初数据,可用上期期末当本期期初(需时间字段对齐)
- 分母为 0(即平均库存 = 0)时,周转率为
NULL或需显式排除,否则ORDER BY可能排到最前
SQL写法:用子查询或 CTE 先算每个产品的周转率
不能在 GROUP BY 后直接 ORDER BY 周转率表达式,因为涉及除法和跨行计算,必须先生成中间结果。
推荐用 CTE(兼容 PostgreSQL / SQL Server / MySQL 8.0+):
WITH turnover AS (
SELECT
product_id,
cost_of_goods_sold,
(beginning_inventory + ending_inventory) / 2.0 AS avg_inventory,
CASE
WHEN (beginning_inventory + ending_inventory) = 0 THEN NULL
ELSE cost_of_goods_sold / ((beginning_inventory + ending_inventory) / 2.0)
END AS inventory_turnover
FROM sales_and_inventory
WHERE cost_of_goods_sold IS NOT NULL
)
SELECT product_id, inventory_turnover
FROM turnover
WHERE inventory_turnover IS NOT NULL
ORDER BY inventory_turnover ASC
LIMIT 1;
-
/ 2.0强制浮点运算,避免整数除法截断(如 SQLite/MySQL 中5/2 = 2) -
CASE处理零库存场景,避免除零错误或Inf值干扰排序 - 务必加
WHERE inventory_turnover IS NOT NULL,否则NULL可能在ORDER BY ... ASC中排第一(行为因数据库而异)
MySQL 5.7 或旧版不支持 CTE 怎么办
用内联视图(子查询)替代,逻辑一致但嵌套一层:
SELECT product_id, inventory_turnover
FROM (
SELECT
product_id,
cost_of_goods_sold,
(beginning_inventory + ending_inventory) / 2.0 AS avg_inventory,
CASE
WHEN (beginning_inventory + ending_inventory) = 0 THEN NULL
ELSE cost_of_goods_sold / ((beginning_inventory + ending_inventory) / 2.0)
END AS inventory_turnover
FROM sales_and_inventory
WHERE cost_of_goods_sold IS NOT NULL
) AS t
WHERE inventory_turnover IS NOT NULL
ORDER BY inventory_turnover ASC
LIMIT 1;
- 注意外层
SELECT不能引用子查询中未SELECT出的字段(如别名avg_inventory在外层不可直接用) - MySQL 5.7 对
LIMIT+ORDER BY支持良好,但若需 Top N 且版本太老,可能要用变量模拟,不推荐 - Oracle 用户注意:用
ROWNUM = 1替代LIMIT 1,且必须嵌套两层才能保证先排序再取行
容易被忽略的业务陷阱:时间范围和数据口径是否对齐
查出“最低”不等于“最滞销”。常见错位:
- 销售成本是自然年,库存快照却是财年末——时间跨度不一致,周转率失真
-
cost_of_goods_sold包含退货冲销?还是毛销售额?必须确认是净销售成本 - 多仓库场景下,
beginning_inventory和ending_inventory是否已按产品合并汇总?未聚合会导致重复计算 - 新品(期初库存为 0)或清仓品(期末为 0)天然周转率异常,建议额外加条件过滤:
AND beginning_inventory > 0 AND ending_inventory > 0
真正卡住人的往往不是 SQL 写法,而是搞不清手头这三列数据到底代表什么时间、什么范围、什么定义。











