必须显式使用partition by才能实现分组内累计和,否则sum() over(order by ...)会跨全表累加;order by需用有顺序语义的列(如时间戳),并建议多列排序保证稳定性;缺乏(x,y)复合索引将导致性能急剧下降。

为什么 SUM(col) OVER(ORDER BY col) 统计的不是你想要的“分组内累计和”
直接写 SUM(col) OVER(ORDER BY col) 会跨整个结果集排序累加,根本没按业务分组切开。比如订单表里有多个用户,你本意是“每个用户的订单金额逐笔累加”,但这个写法会把所有用户混在一起按 col 排序后从头加到尾——结果既不按人分,也不符合时间顺序。
必须显式加上 PARTITION BY 才算真正分组累计
累计和要分组,核心就是告诉数据库:“先按某列切块,再在每块里单独排序累加”。PARTITION BY 就是干这个的,它必须和 ORDER BY 同时出现才有意义。
-
SUM(amount) OVER(PARTITION BY user_id ORDER BY create_time):按用户切分,每人在自己订单里按创建时间累加 - 漏掉
PARTITION BY,哪怕ORDER BY写对了,也是全表扫一遍,数据逻辑就乱了 - 注意
ORDER BY列必须有明确顺序语义(如时间戳、自增ID),用无序字段(如随机UUID、状态码)会导致累计结果不可预测
ORDER BY 中用多列或带方向时要注意什么
实际业务中单列排序往往不够,比如同一用户同秒下多笔订单,仅靠 create_time 无法稳定排序,累加值可能每次执行都不一样。
- 补上二级排序:
ORDER BY create_time, id,确保顺序唯一 - 升序(
ASC)是默认行为,降序(DESC)会从最新一笔开始累加,容易误解——比如想看“截至当前的总消费”,却写出ORDER BY create_time DESC,结果第一行就是全部金额 - 窗口函数不支持
ORDER BY ... NULLS FIRST/LAST这种写法(PostgreSQL 支持,但 MySQL 8.0 和 SQL Server 不认),NULL 值位置得提前用COALESCE或CASE处理
性能陷阱:没加索引时 OVER 窗口会很慢
数据库执行 SUM() OVER(PARTITION BY x ORDER BY y) 时,本质要对每个分组做局部排序。如果 (x, y) 没复合索引,就得临时排序,数据量一过百万,查询可能从毫秒级变成秒级甚至超时。
- 建索引优先级:
CREATE INDEX idx_user_time ON orders(user_id, create_time); - 别只建单列
user_id索引——它能加速分组,但排序仍要额外排序操作 - MySQL 8.0+ 对窗口函数优化较好,但 SQL Server 和旧版 PostgreSQL 仍可能生成大量临时表,执行前务必看
EXPLAIN
真正难的不是写对语法,而是想清楚“累计”的业务边界在哪——是每个用户?每个地区?还是每个产品类目?一旦 PARTITION BY 列选错,后面所有累加都失去业务意义,而且这种错误很难被肉眼发现。










