必须用sum() over(partition by category order by created_at),缺partition by则全表累计,缺order by则组内无序导致结果不可复现;排序字段重复时需追加唯一字段(如order_id)保序。

用窗口函数 SUM() OVER() 实现组内累计求和
SQL 中按组计算 Running Total,核心就是 SUM() OVER() 配合 PARTITION BY 和 ORDER BY。不加 PARTITION BY 就是全表累计;漏掉 ORDER BY 会导致结果非确定——数据库可能按任意顺序累加,结果不可复现。
实操建议:
-
ORDER BY必须明确指定业务逻辑上的排序依据(如order_date、seq_no),不能依赖主键或插入顺序 - 若分组字段是
category,排序字段是created_at,则写法为:SUM(amount) OVER (PARTITION BY category ORDER BY created_at) - 注意
NULL值在ORDER BY中的默认位置(MySQL 默认NULLS FIRST,PostgreSQL 默认NULLS LAST),必要时显式声明
MySQL 8.0+ 和 PostgreSQL 的行为一致,但旧版 MySQL 不支持
MySQL 在 8.0 之前没有窗口函数,强行用变量(如 @running_total := @running_total + amount)模拟,极易出错:在 ORDER BY 和变量赋值顺序不一致时,结果完全错乱,且无法保证执行计划稳定。
实操建议:
- 确认版本:
SELECT VERSION();,低于 8.0 的 MySQL 应优先升级或改用应用层聚合 - PostgreSQL 9.4+、SQL Server 2005+、Oracle 8i+ 均支持标准窗口语法,无需额外适配
- SQLite 从 3.25.0 开始支持,但需确认实际部署版本(很多嵌入式环境仍用旧版)
遇到 ORDER BY 字段重复时,累计值可能“跳变”
当排序字段存在重复值(比如多个订单同一天创建,且仅按 order_date 排序),窗口函数会把这批记录视为“同一行位置”,导致它们共享同一个累计值(即“并列累计”),而不是逐行递增。
实操建议:
- 在
ORDER BY中加入唯一字段兜底,例如:ORDER BY order_date, order_id - 避免仅用低精度时间字段(如
DATE(created_at))排序,容易引发重复 - 如果业务上允许并列,这个行为是符合预期的;但报表类需求通常需要严格逐行,必须补唯一排序键
性能敏感场景下,PARTITION BY 字段要有索引
窗口函数本身不走索引,但优化器在生成执行计划时,会优先选择已排序的扫描路径。如果 PARTITION BY + ORDER BY 字段组合上有复合索引,性能提升明显,尤其在大表分组多、每组数据量大的情况下。
实操建议:
- 建索引示例(以 PostgreSQL 为例):
CREATE INDEX idx_category_created ON sales (category, created_at); - 避免在
PARTITION BY字段上使用函数(如PARTITION BY UPPER(category)),会导致索引失效 - 如果分组字段基数极高(如用户 ID),而每组只有一两行,窗口函数开销几乎可忽略;但若每组数千行,索引就非常关键
真正麻烦的是排序字段和分组字段都无索引,又跑在千万级表上——这时候即使语法写对了,查询也可能卡住几秒甚至超时。别只盯着 SQL 写法,先看执行计划里的 Sort 节点代价。











