必须指定order by,否则sum() over()返回整组总和而非累计值;正确写法为sum(sales) over (partition by region order by order_date, order_id),其中order by确保逐行累加,二级排序解决重复日期歧义,mysql 8.0+和postgresql支持,sqlite需3.25.0+。

窗口函数 SUM() 配合 ORDER BY 才能算出正确累计值
不加 ORDER BY 的 SUM() OVER() 默认是整组求和,不是累计。比如按日期统计销售额,必须明确告诉数据库“按时间顺序累加”,否则结果每行都一样。
常见错误是写成 SUM(sales) OVER (PARTITION BY region) —— 这只分组求和,没累计;正确写法要加上 ORDER BY order_date,且通常还要搭配 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(虽然省略时默认如此,但显式写出更安全)。
-
SUM(sales) OVER (PARTITION BY region ORDER BY order_date):按地区分组、按日期累计 -
SUM(sales) OVER (ORDER BY order_date):全表按日期累计(不分组) - 如果日期有重复,建议加二级排序,如
ORDER BY order_date, order_id,避免窗口边界模糊
MySQL 8.0+ 和 PostgreSQL 支持完整语法,但 SQLite 需要 3.25.0+
老版本 MySQL(ERROR 1064: You have an error in your SQL syntax。PostgreSQL 从 9.4 就支持,基本无兼容性问题。
SQLite 在 3.25.0(2018 年发布)才加入窗口函数,用之前先查版本:SELECT sqlite_version();。低于该版本只能用自连接或子查询模拟,性能差很多。
- 确认 MySQL 版本:
SELECT VERSION(); - PostgreSQL 检查:
SELECT version(); - 别在 WHERE 或 GROUP BY 中引用窗口函数结果——它们在逻辑执行顺序中晚于 WHERE 和 GROUP BY
累计值跳变?检查 NULL 值和排序字段唯一性
如果累计销售额某几行突然变小或归零,大概率是 order_date 字段含 NULL,或者多行日期相同但未指定次级排序。数据库对 NULL 的排序行为因引擎而异(MySQL 默认排最前,PostgreSQL 默认排最后),会导致窗口范围错乱。
- 用
COALESCE(order_date, '1970-01-01')或WHERE order_date IS NOT NULL过滤掉空值 - 日期相同时,补上唯一字段如
ORDER BY order_date, sale_id,确保窗口逐行推进 - 测试时用
ROW_NUMBER() OVER (...)对照看排序是否符合预期
想排除当前行只算“历史累计”?改用 ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
默认的累计包含当前行,但有些场景(比如计算“截至昨日的总销量”)需要排除当前行。这时不能靠减法硬算,应直接调整窗口帧定义。
例如:SUM(sales) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)。注意:当第一行没有“前一行”时,结果为 NULL,需用 COALESCE(..., 0) 处理。
- 别写成
SUM(sales) OVER (...) - sales——若存在NULL,整行结果变NULL -
1 PRECEDING是相对偏移,不是固定行号;配合PARTITION BY时,它只在当前分区内生效 - 某些旧版 PostgreSQL 不支持
1 PRECEDING写法,需升级或换用RANGE(但 RANGE 对日期不精确)
实际用的时候,先跑通基础累计,再根据业务要不要排除当前行、要不要分组、有没有 NULL,一层层加约束。窗口函数本身不难,但排序逻辑和 NULL 处理漏一点,结果就全偏了。











