必须显式写rows between,否则默认range语义导致累计而非滚动;sum(x) over (order by dt)无边界定义时按range between unbounded preceding and current row执行,从首行累加至当前行,非滑动窗口。

必须显式写 ROWS BETWEEN,否则不是滚动窗口,而是累计统计。
为什么 SUM(x) OVER (ORDER BY dt) 算出来是累计和,不是滚动和?
因为没指定窗口边界,数据库默认按 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 执行——它从结果集第一行一直累加到当前行。哪怕你排序字段是日期,只要没写 ROWS 或 RANGE 子句,就不是滑动。
-
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW:严格取物理上“前两行 + 当前行”,共 3 行 -
ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING:当前行加下一行(适合错位对比) - 漏掉
ORDER BY:PostgreSQL/MySQL 8.0+ 会报错;SQLite 可能静默退化为无序聚合,结果不可靠
按月滚动 12 个月,为什么 ROWS BETWEEN 11 PRECEDING AND CURRENT ROW 还出错?
常见原因是 ORDER BY 字段不是真实 DATE 类型,而是字符串(如 '2023-1' 和 '2023-10' 混用),导致字典序排序错乱。
- 先检查:执行
SELECT MIN(order_month), MAX(order_month) FROM t,若返回'2023-1'和'2023-12',说明格式不统一 - 清洗方法:
CAST(order_month AS DATE)(通用)、TO_DATE(order_month, 'YYYY-MM')(PostgreSQL)、STR_TO_DATE(order_month, '%Y-%m')(MySQL) - 聚合到月再开窗:原始数据按日,但想按月滚动,得先
GROUP BY DATE_TRUNC('month', dt)(PG/BQ)或DATE_SUB(dt, INTERVAL DAYOFMONTH(dt)-1 DAY)(MySQL)
排序字段含 NULL 或重复值,ROWS 还可靠吗?
不可靠。NULL 的默认排序位置因数据库而异(PostgreSQL 排最前,MySQL 8.0+ 排最后),同一天多条记录时,物理顺序不确定,2 PRECEDING 可能跳过或重复抓取。
- 兜底方案一:在
ORDER BY中加入唯一字段,例如ORDER BY sale_date, id - 兜底方案二:用
ROW_NUMBER() OVER (ORDER BY sale_date, id)生成确定序号,再基于该序号开窗 - 避免用
RANGE处理时间窗口——相同时间戳的所有行都会被塞进同一窗口,行数失控
性能差、查询卡死?可能是窗口函数被滥用
每个 OVER 子句都触发一次独立排序扫描。写 3 个不同聚合却共用同一窗口定义,多数引擎并不会自动复用排序结果。
- 确认是否支持
WINDOW子句复用:SELECT AVG(x) OVER w, SUM(y) OVER w FROM t WINDOW w AS (ORDER BY ts ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)(PG/MySQL 8.0+ 支持;SQL Server 不支持) - 千万级表务必先
WHERE裁剪或按分区过滤,再开窗 - 别在明细行上直接套
ROWS BETWEEN做“周滚动”——先GROUP BY week_start聚合成周粒度,再对周汇总表开窗
真正难的不是写对语法,而是确保排序键在所有环节都保持确定性:类型一致、无 NULL、无歧义重复。一旦这里松动,ROWS BETWEEN 就从精确控制变成随机采样。










