正确写法是SUM(amount) OVER (PARTITION BY salesperson ORDER BY sale_date ROWS UNBOUNDED PRECEDING),因需分组逐行累加,避免同日期跳变、语法错误及性能问题。

直接结论:用 SUM(amount) OVER (ORDER BY date ROWS UNBOUNDED PRECEDING),不是 SUM(amount) 或 SUM(amount) OVER () 单独写——前者不累计,后者全表求和,两者相除或嵌套错位都会出错。
为什么不能直接写 SUM(amount) OVER (ORDER BY date) 就完事?
MySQL 8.0 默认帧子句是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,但显式写出 ROWS UNBOUNDED PRECEDING 更安全。省略它时,某些兼容模式或未来版本可能按 RANGE 解释,遇到相同 date 值(比如多笔同天订单)会把所有同天记录“打包累加”,导致跳变。你想要的是逐行累加,不是按日期值分组累加。
- 错误现象:
2023-01-01有 3 笔订单,金额分别是 100、200、300,SUM(amount) OVER (ORDER BY sale_date)可能直接给出 600(而非 100→300→600 的递进) - 正确写法必须带
ROWS UNBOUNDED PRECEDING,哪怕它是默认行为 - 如果日期字段含时分秒,且你只关心“日粒度累计”,先用
DATE(sale_date)转成日期类型再排序,避免同一天内因时间不同被拆开
按产品/销售员分组累计,PARTITION BY 必须放在 ORDER BY 前面
窗口函数执行顺序是:先 PARTITION BY 切块,再在每块内按 ORDER BY 累加。漏掉 PARTITION BY 就变成全表累计;顺序颠倒(比如 ORDER BY ... PARTITION BY ...)语法报错。
- 场景:每个销售员独立计算自己从最早到最近的累计销售额
- 正确写法:
SUM(amount) OVER (PARTITION BY salesperson ORDER BY sale_date ROWS UNBOUNDED PRECEDING) - 常见错误:写成
SUM(amount) OVER (ORDER BY sale_date PARTITION BY salesperson)→ 语法错误 - 性能注意:
PARTITION BY字段和ORDER BY字段最好合建联合索引,例如(salesperson, sale_date)
聚合后累计(如按月统计)必须两层查询,不能在原始明细上硬套
如果你要的是“每月销售额 + 截止当月的累计销售额”,就不能对原始订单行直接开窗——那样会把同一月份的多条订单重复计入累计过程,导致当月累计值虚高。
- 正确路径:先
GROUP BY得到月度汇总,再对这个结果集开窗 - 示例中
SUM(SUM(amount)) OVER (...)是合法的:内层SUM(amount)是分组聚合,外层SUM()是窗口函数,作用于已聚合的月度行 - 错误写法:
SUM(amount) OVER (ORDER BY DATE_FORMAT(sale_date,'%Y-%m'))→ 同一月份多行,每行都触发一次窗口计算,逻辑混乱 - 月份字段务必规范:用
DATE_FORMAT(sale_date,'%Y-%m'),别用CONCAT(YEAR(), '-', MONTH()),否则'2023-1'和'2023-10'字符串比较会错序
除法算累计占比时,类型截断和 NULL 处理最容易翻车
累计百分比不是简单加个 / SUM(amount) OVER () 就完事。整数除整数得整数,NULL 不参与求和但会让整行结果变 NULL,这些细节不处理,报表就全是 0 或空。
- 避免整数截断:写成
* 100.0 / SUM(amount) OVER ()或CAST(... AS DECIMAL(5,2)) -
amount列含NULL?SUM()默认忽略,但若你想把NULL当 0 算,得提前COALESCE(amount, 0) - 分母用
SUM(amount) OVER ()(无ORDER BY),绝不能用SUM(amount)—— 后者会强制触发隐式GROUP BY,整个查询报错或结果只剩一行 - 小数位控制用
ROUND(..., 2),别依赖前端格式化,浮点误差可能让 99.99999 显示成 100.00 或 99.99
真正麻烦的从来不是语法,而是 ORDER BY 字段的业务含义是否唯一、是否可排序、是否被索引覆盖——这些决定了累计值是平滑递进,还是突然跳变、卡顿、超慢。写完 SQL 后,务必用 EXPLAIN 看一眼是否走了索引,尤其当数据量过百万时。


















