ORDER BY 在 SUM() OVER() 中是必需项,否则默认计算全表总和而非累计;需配合 PARTITION BY 实现分组内累计,且 ORDER BY 字段须确定、非空、类型正确以确保稳定排序。

ORDER BY 在 SUM OVER() 里不是可选项,是必须项
没写 ORDER BY 的 SUM() OVER() 会算出全表总和,每行都一样——这不是累计,是“复读机求和”。窗口函数默认的 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING 范围,本质就是整张表。要累计,就得靠 ORDER BY 定义逻辑顺序,让数据库知道“从哪开始加、加到哪”。
常见错误现象:SUM(amount) OVER(PARTITION BY user_id) 看起来分组了,但没 ORDER BY,结果每条记录的值都是该用户全部 amount 的总和,不是逐条累加。
- 使用场景:按时间排序的流水账、按序号递增的步骤日志、按分数排名后的积分累积
- ORDER BY 字段必须是确定性排序依据(比如带时间戳的字段,或加
id作二级排序防歧义) - 如果排序字段有重复值,且未加唯一二级排序,不同执行可能产生不同累计结果(MySQL 8.0+ / PostgreSQL 行为一致,但结果不稳定)
SUM OVER() 默认范围是“当前行及之前所有”,不是“当前行及之后”
很多人以为 SUM(amount) OVER(ORDER BY create_time) 是从第一行开始加到最后一行,其实它默认是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,也就是“从开头加到当前行”。这个行为对累计求和刚好合适,但容易误以为能反向累计(比如倒序求和),那得显式改范围。
- 想实现“剩余未处理金额”这类反向累计?得写成:
SUM(amount) OVER(ORDER BY create_time DESC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) - 性能影响:默认范围无需额外计算边界,效率高;一旦显式指定
ROWS BETWEEN ...,尤其跨大范围时,某些旧版 MySQL(如 5.7 不支持窗口函数)直接报错,PostgreSQL 和 MySQL 8.0+ 支持但要注意索引是否覆盖ORDER BY字段 - 兼容性提醒:SQLite 3.25+ 支持,但不支持
RANGE模式;如果用RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,遇到相同ORDER BY值会把它们全捆在一起算,行为和ROWS不同
GROUP BY 和 SUM OVER() 一起用时,别在 SELECT 里混用普通列和窗口函数
这是典型报错源头:SELECT user_id, SUM(amount), SUM(amount) OVER(ORDER BY create_time) —— 如果没 GROUP BY user_id,MySQL 会报 Expression #3 of SELECT list is not in GROUP BY clause;加了 GROUP BY 又会发现窗口函数结果乱套,因为 OVER() 在分组后执行,但 ORDER BY create_time 已经被聚合打平了。
- 正确做法:累计求和一般不和
GROUP BY同级混用;需要分组内累计?必须把PARTITION BY写进OVER(),例如:SUM(amount) OVER(PARTITION BY user_id ORDER BY create_time) - 注意
PARTITION BY是物理分片,ORDER BY是分片内排序,两者缺一不可才能得到“每个用户的独立累计线” - 如果先
GROUP BY再想累计(比如每日销售额的累计),得套一层子查询或 CTE,把聚合结果当新表再开窗口
ORDER BY 字段为空(NULL)会导致累计断层
NULL 在排序中默认排最前(MySQL/PostgreSQL 默认行为),但如果你的业务逻辑里 create_time IS NULL 表示“未生效”,这些记录就会被挤到累计序列开头,导致首几行累计值异常偏小甚至为 0,后面才突然跳升。
- 检查方式:运行
SELECT create_time, COUNT(*) FROM t GROUP BY create_time ORDER BY create_time,看NULL是否意外存在 - 稳妥写法:强制过滤或置默认值,例如
ORDER BY COALESCE(create_time, '1970-01-01'),或更推荐在 WHERE 中提前排除:WHERE create_time IS NOT NULL - 另一个坑:字符串时间字段(如
'2023-1-1')没补零,排序变成字典序,'2023-10-1' < '2023-2-1',累计顺序就错了——务必确保时间字段是DATE或DATETIME类型
ORDER BY 字段,得既是业务意义上的先后顺序,又能被数据库稳定排序。










