直接用SUM() OVER()会算错库存,因库存是累积净变化量而非简单累加;正确做法是先构造in_qty-out_qty的delta字段,再按create_time、id排序累计,并显式指定ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,同时用COALESCE处理NULL、CAST确保类型一致,并为排序字段建联合索引。

为什么直接用 SUM() OVER() 会算错库存?
因为库存是累积净变化量,不是简单累加所有出入库数量。常见错误是把 in_qty 和 out_qty 分开累计,或者没统一成单字段净变动值。正确做法是先构造一个 delta 字段:入库为正、出库为负,再用窗口函数按时间顺序累加。
- 必须确保排序字段(如
create_time或id)能反映真实业务时序,否则累计结果错乱 - 同一时刻多笔记录时,需额外指定二级排序(比如
id),避免窗口函数非确定性排序 - 如果存在“期初库存”,得在窗口累计前手动叠加——
SUM(delta) OVER(...)只算变动,不包含期初
怎么写带期初库存的完整流水SQL?
假设期初库存为 100,表名 stock_log 含字段 id、in_qty、out_qty、create_time:
SELECT
id,
in_qty,
out_qty,
100 + SUM(in_qty - out_qty) OVER (
ORDER BY create_time, id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS current_stock
FROM stock_log;注意:ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 是显式指定累计范围,比默认 RANGE 更安全——尤其当 create_time 有重复时,RANGE 可能意外包含同时间其他行。
遇到 NULL 值或数据类型不一致怎么办?
in_qty 或 out_qty 为 NULL 会导致整行 delta 变成 NULL,进而让后续累计中断(SUM 遇到 NULL 返回 NULL)。必须提前处理:
- 用
COALESCE(in_qty, 0)和COALESCE(out_qty, 0)替换空值 - 若字段是字符串(比如导出数据没转类型),先
CAST(in_qty AS DECIMAL),否则窗口函数可能报错或静默截断 - MySQL 8.0+ 支持窗口函数,但低版本不支持——得用变量模拟,逻辑更重且并发不安全
性能卡在百万级数据上怎么优化?
窗口函数本身不走索引,ORDER BY 字段必须有联合索引支撑,否则排序阶段会磁盘临时表暴增:
- 建索引:
CREATE INDEX idx_time_id ON stock_log(create_time, id); - 避免在窗口函数里嵌套复杂表达式(如
ROUND(COALESCE(...))),先在子查询里算好delta - 如果只查某天库存,别用全表窗口,改用自连接或 LAG() 计算当日变动再叠加期初
真正难的不是语法,而是时间排序的业务含义是否被严格定义——比如审核时间 vs 创建时间,退库冲正要不要反向计入 delta,这些逻辑一旦漏掉,数字对不上根本没法调。

















