ROWS BETWEEN 必须配合 ORDER BY 使用,否则报错或结果不可预测;因SQL标准要求窗口帧依赖确定性排序,数据库无法识别“前一行”,ORDER BY 字段需为时间戳或自增ID等确定性列。

ROWS BETWEEN 必须带 ORDER BY,否则直接报错或结果错乱
不写 ORDER BY 的 ROWS BETWEEN 在 MySQL 8.0+、PostgreSQL、SQL Server 上基本都会报错,比如 Window 'w' lacks an ORDER BY clause;即使某些引擎(如旧版 PostgreSQL)没报错,结果也按物理存储顺序取行,完全不可控。
根本原因:数据库无法凭空知道“前一行”是谁——ROWS BETWEEN 1 PRECEDING AND CURRENT ROW 是位置偏移指令,不是时间或值逻辑。没有排序,就没有“前”和“后”。
-
ORDER BY字段必须是确定性列:优先用时间戳(event_time)、自增 ID(id),避免用可能重复的name或无序字段 - 若排序字段有重复(如秒级时间戳下多条记录),追加唯一键二级排序:
ORDER BY event_time, event_id - 别指望
GROUP BY或主键隐式提供顺序——窗口函数不认这个
ROWS BETWEEN 的数字是“偏移量”,不是“总行数”
写 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 意味着取当前行 + 前面紧挨着的 2 行,共 3 行;写 3 PRECEDING AND CURRENT ROW 就是 4 行。很多人误以为“3”代表总数,结果窗口宽了 1 行。
- “过去 N 天移动平均” ≠
ROWS BETWEEN N PRECEDING AND CURRENT ROW:那是“最近 N+1 条记录”,和时间无关 - 想取当前行及前后各 1 行(共 3 行):用
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING - 首尾边界自动截断:第一行没有 “1 PRECEDING”,就只算自己和下一行;不会补 NULL,也不会报错
别把 ROWS 和 RANGE 混用,语义完全不同
ROWS 算的是排序后的位置偏移,RANGE 算的是排序字段的值范围。同一句 ORDER BY created_at 下,ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 和 RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW 可能返回完全不同的行数——尤其当某天数据暴增或缺失时。
- 要“最近 7 条订单均值”:必须用
ROWS - 要“过去 7 天所有订单均值”:应优先用
RANGE BETWEEN INTERVAL '7 days' PRECEDING(PostgreSQL/Oracle 支持;MySQL 不支持该语法) - 用
RANGE但排序字段大量重复(如DATE(created_at)):窗口会吞掉同一天所有行,导致实际行数远超预期
跨数据库兼容性差,先查版本再写
ROWS BETWEEN 看似标准,但各库支持程度差异大:MySQL 5.7 完全不支持窗口函数;SQLite 3.25+ 支持 ROWS 却不认 FOLLOWING 关键字;SQL Server 对 ORDER BY 中的 NULLS LAST 直接忽略。
- 检查 MySQL 版本:
SELECT VERSION();—— 必须 ≥ 8.0.2 - 检查 SQLite:
SELECT sqlite_version();—— 必须 ≥ 3.25,且避免写1 FOLLOWING,改用UNBOUNDED FOLLOWING或具体数字 - PostgreSQL 用户可放心用
EXCLUDE CURRENT ROW(14+),但 MySQL/SQL Server 仍需手动拆解:(SUM(col) OVER win - col) / NULLIF(COUNT(*) OVER win - 1, 0)
ORDER BY 列没索引时,窗口计算会全表排序,百万行以上延迟明显——这点容易被忽略,但比语法错误更伤生产性能。

















