最常见原因是没写order by子句;窗口函数默认对整个分区求和,结果每行相同;必须用over(order by date)或over(partition by category order by date)定义顺序才能实现累计和。

为什么 SUM() OVER() 算出来不是累计和?
最常见原因是没写 ORDER BY 子句。窗口函数默认使用 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,也就是对整个分区所有行求和,结果每行都一样。要累计,必须显式指定排序逻辑。
-
OVER (ORDER BY date):按日期升序,从第一行累加到当前行 -
OVER (PARTITION BY category ORDER BY date):先按类别分组,再在组内按日期累计 - 如果
date有重复值且未进一步排序(比如加id),同一日期的多行可能获得相同累计值,顺序不确定
SUM() OVER() 和自连接/子查询比有什么坑?
性能上通常好很多,但要注意执行计划是否真的用了窗口函数优化。某些旧版 MySQL(ERROR 1064 (42000);PostgreSQL 9.4+、SQL Server 2012+、Oracle 与现代 ClickHouse 都支持。
- 自连接容易因笛卡尔积导致超时,尤其数据量过万后
-
SUM() OVER()在内存中流式计算,无临时表开销,但若PARTITION BY分区太多(如百万级唯一用户ID),可能触发磁盘 spill - MySQL 8.0 默认启用
cte_max_recursion_depth不影响窗口函数,但别和 CTE 混用搞错作用域
如何确保累计和严格按业务时间线推进?
真实场景中,“时间”字段常含空值、重复或时区混杂,直接 ORDER BY created_at 可能跳变或倒序。
- 先
WHERE created_at IS NOT NULL过滤掉脏数据 - 用
COALESCE(created_at, '1970-01-01')做兜底(慎用,可能把异常数据拉进累计) - 复合排序更稳:
ORDER BY DATE(created_at), id,避免同天多笔订单顺序不可控 - 如果业务要求“按确认时间累计而非创建时间”,得换字段——窗口函数不会自动识别语义,只认你写的列
累计和突然归零或跳变,可能是这些原因
不是函数写错了,而是数据或逻辑断层了。典型表现是某行累计值比前一行还小,或中间空了几行。
-
PARTITION BY分组键有隐式类型转换,比如字符串'1'和数字1被当不同组 - 排序字段存在
NULL,而数据库默认把NULL排最前(PostgreSQL)或最后(MySQL),造成首尾断层 - 漏了
WHERE条件,把测试数据或已删除记录(is_deleted = 1)算进去了 - 前端分页时只取了 LIMIT 结果,但窗口函数在全集上计算,导致页面内累计值“看起来不连续”
SELECT id, amount, SUM(amount) OVER (ORDER BY id) AS cumsum FROM orders LIMIT 10 看前10行是否符合直觉。累计逻辑一旦嵌套进多层 CTE 或视图,排查成本会指数上升。











