用GROUP BY配合SUM()和AVG()做库存趋势汇总时,须按业务实际分组、过滤无效订单、限定90天时间范围、按SKU而非SPU分组、对齐补货周期的时间粒度,并严格WHERE过滤quantity>0等异常值。

GROUP BY 配合 SUM() 和 AVG() 做基础库存趋势汇总
直接用 SUM() 或 AVG() 算出每类商品的总销量或平均单日出库量,是预测库存消耗节奏最常用的起点。关键不是“能不能算”,而是分组维度是否贴合业务实际。
常见错误现象:GROUP BY product_id 后发现数据量暴增或归零——往往因为没过滤掉退货、调拨、测试单等干扰订单类型。
- 必须先用
WHERE order_status IN ('shipped', 'delivered')排除无效单据 - 时间范围建议固定为最近 90 天,避免拉太长引入季节性噪音(比如只取 12 月数据会高估全年均值)
- 如果商品有规格变体(如颜色/尺码),
GROUP BY sku_code比GROUP BY product_id更准,否则库存预测会严重失真
用 DATE_TRUNC() 或 DATE_FORMAT() 对齐时间粒度做滚动预测
库存预测本质是“未来几天会不会断货”,所以时间分组不能只按自然月或年,得对齐你的补货周期。PostgreSQL 用 DATE_TRUNC('day', order_time),MySQL 用 DATE_FORMAT(order_time, '%Y-%m-%d'),目的都是把订单压到可比的时间桶里。
使用场景:你想看“过去 7 天每天平均卖出多少件”,而不是“上个月总共卖了多少”——后者对补货决策基本没用。
- 别用
YEAR(order_time)或MONTH(order_time)做分组,它们不支持跨年连续计算,滚动窗口会断掉 - 如果数据库不支持
DATE_TRUNC()(比如旧版 MySQL),用CAST(order_time AS DATE)更稳妥,兼容性好且语义清晰 - 注意时区:所有时间字段必须统一转成业务所在地时区再截断,否则凌晨下单被算进前一天,日均值就偏高
WHERE 子句漏写导致聚合结果包含异常值
聚合函数本身不会拒绝脏数据,SUM(quantity) 照样把负数退货、超大测试单(比如 quantity = 99999)全加进去,结果直接失真。
典型错误现象:某 SKU 的日均销量突然变成 5000+,查发现是内部压测订单没打标记,混在生产表里。
- 强制加过滤:
WHERE quantity > 0 AND quantity (根据业务设定合理上限) - 退货单通常有
is_return = true或order_type = 'return'字段,必须显式排除 - 如果用的是埋点日志表而非订单表,还要排除
event_type NOT IN ('click', 'view')这类非交易行为
用窗口函数加滑动平均替代简单 AVG() 提升预测稳定性
单纯用 AVG() OVER (PARTITION BY sku_code ORDER BY sale_date) 只是累计平均,真正有用的是近 7 天滚动均值,它能快速响应销量突变。
性能影响:窗口函数比普通 GROUP BY 多一次排序扫描,但只要 sale_date 有索引,90% 场景下延迟增加不到 200ms。
- PostgreSQL 写法:
AVG(quantity) OVER (PARTITION BY sku_code ORDER BY sale_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) - MySQL 8.0+ 才支持
ROWS BETWEEN,低版本只能用自连接或子查询模拟,性能差一倍以上 - 注意 NULL 处理:窗口内若某天无销售,
AVG()默认跳过该行,不是补 0,所以结果天数可能少于 7 —— 要补零得额外用COALESCE(AVG(...), 0)
复杂点在于,真实库存还受采购在途、质检滞留、仓库调拨影响,SQL 只能算出“理论消耗速度”。一旦上游系统没把在途单同步进数据库,预测结果再准也没用。

















