sum() over()的核心行为是保持原表行数不变,为每行计算窗口内累计和;必须配合order by才能实现逐行动态累计,否则仅返回分组总和。

什么是 SUM() OVER() 的核心行为
SUM() OVER() 不是普通聚合函数,它不会压缩行数,而是在保持原表结构的前提下,为每一行计算一个基于窗口定义的累计和。关键在于:**结果是否“动态”取决于 ORDER BY 子句是否存在以及如何定义 ROWS BETWEEN**。没写 ORDER BY,就等价于对全分区求和(静态);写了,才可能产生逐行递增的累计效果。
必须显式指定 ORDER BY 才能获得逐行累计
常见错误是只写 SUM(amount) OVER(PARTITION BY category),结果每组内所有行都显示相同总和——这不是累计,是分组总和。要动态累计,ORDER BY 不可省略,且排序字段应反映业务逻辑顺序(如时间、序号):
SELECT date, amount, SUM(amount) OVER(ORDER BY date ROWS UNBOUNDED PRECEDING) AS running_total FROM sales;
-
ROWS UNBOUNDED PRECEDING是默认行为,可省略,但建议显式写出以明确意图 - 若用
RANGE替代ROWS,遇到相同date值时会把它们“捆在一起”求和,可能跳变,多数场景应坚持用ROWS - 排序字段必须有确定性:如果
date有重复,需追加唯一字段(如id)避免结果不稳定:ORDER BY date, id
分组内独立累计要用 PARTITION BY + ORDER BY
比如按月份统计每日销售累计,不能只靠全局排序。必须先分区再排序:
SELECT
month,
day,
amount,
SUM(amount) OVER(
PARTITION BY month
ORDER BY day
ROWS UNBOUNDED PRECEDING
) AS daily_running_in_month
FROM daily_sales;
-
PARTITION BY决定“重置点”,ORDER BY决定“累加方向”,二者缺一不可 - 分区键和排序键的数据类型要合理:用字符串月份(如
'2024-01')比用数字202401更安全,避免隐式转换导致排序错乱 - 某些数据库(如 MySQL 8.0+)要求
ORDER BY必须存在才能用ROWS;PostgreSQL 允许无ORDER BY,但此时窗口帧无效,退化为全分区和
性能与空值:两个容易被忽略的细节
SUM() OVER() 在大数据量下可能比预期慢,尤其当排序字段无索引或分区键选择不当。另外,NULL 值默认被忽略(符合 SQL 标准),但若业务要求将 NULL 视为 0,必须提前处理:
- 在窗口函数外层用
COALESCE(amount, 0),不要在OVER内部写SUM(COALESCE(...))—— 后者仍会因NULL导致该行不参与累计(SUM 本身跳过 NULL,但位置仍在) - 若累计列用于后续过滤(如
WHERE running_total > 1000),注意窗口函数不能直接出现在WHERE中,需套一层子查询或 CTE - 在 Hive/Spark SQL 中,
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW和简写形式性能一致;但在旧版 Oracle 中,显式写全帧定义更稳妥
真正决定“动态性”的从来不是函数名,而是你有没有给它一条清晰的排序路径和明确的边界指令。











