postgresql 12起支持range between interval,但要求order by列为timestamp或date类型;低版本不支持,int型时间戳会报错。

PostgreSQL 中 RANGE BETWEEN INTERVAL 不支持跨版本混用
PostgreSQL 12 之前完全不支持 RANGE BETWEEN INTERVAL 'N' DAY PRECEDING 这类基于时间间隔的帧定义,错误提示是 frame starting offset must be a non-negative integer。从 12 开始才引入对 INTERVAL 的支持,但仅限于 RANGE 模式且要求排序列是 TIMESTAMP 或 DATE 类型。如果排序列是 INT(比如 Unix 时间戳整数),哪怕语义等价,也会报错 cannot use INTERVAL with non-timestamp ORDER BY expression。
- 必须显式使用
TIMESTAMP类型列排序,FLOOR(EXTRACT(EPOCH FROM created_at))::BIGINT这种转整数写法会直接让RANGE BETWEEN INTERVAL失效 - PostgreSQL 16 虽增强兼容性,但仍拒绝
RANGE与GROUPS混用,例如RANGE BETWEEN INTERVAL '1d' PRECEDING AND CURRENT ROW GROUPS是语法错误 -
GROUPS帧只在 PostgreSQL 14+ 支持,且必须配合ORDER BY中存在可分组的重复值(如相同status),否则行为退化为ROWS
ROWS BETWEEN 不能用于无 ORDER BY 的窗口
即使写了 PARTITION BY dept,只要没写 ORDER BY,ROWS BETWEEN 1 PRECEDING AND CURRENT ROW 就会报错 window frame with offset must have ORDER BY clause。这不是限制,而是逻辑必然:没有顺序,就无法定义“前一行”或“后一行”。数据库不会按物理存储顺序隐式排序。
- 想用
ROWS帧,ORDER BY是强制项,不能省略 - 若业务上真不需要排序(比如只求分区总和),那就别用
ROWS,直接用默认帧:SUM(x) OVER (PARTITION BY dept) - 强行加
ORDER BY ctid虽能通过语法检查,但结果不可靠——ctid在 VACUUM 后会变,且不保证插入顺序
GROUPS 帧在低基数排序列下失效
GROUPS 按排序列的值“分组”,把相同值的行视为一个逻辑单元。但如果 ORDER BY status 中 status 只有 'active' 和 'inactive' 两个值,整个分区可能被压缩成最多两个 GROUPS。这时 GROUPS BETWEEN 1 PRECEDING AND CURRENT GROUP 实际覆盖范围远超预期,甚至退化为全分区扫描。
- 执行计划里若出现
Sort Method: external merge或Partitions: 100000+,大概率是GROUPS遇到低基数列导致窗口膨胀 - 用
EXPLAIN (ANALYZE)查看WindowAgg节点的Groups字段,确认实际分组数是否合理 - 替代方案:改用
ROWS+ROW_NUMBER()构造人工序号,再基于序号做偏移,可控性更强
WINDOW 子句中不能引用列别名
在 WINDOW w AS (PARTITION BY dept_name ORDER BY created_at) 中,dept_name 必须是表中真实列名或表达式,不能是 SELECT 列表里定义的别名(如 department AS dept_name)。否则报错 column "dept_name" does not exist in window specification。
- 这个限制容易被忽略,因为 CTE 或子查询里别名可用,但
WINDOW子句解析早于 SELECT 列别名绑定 - 若需转换类型(如
category::text),必须在WINDOW定义里显式写出,不能依赖外层别名 - 复用窗口时,所有
OVER w都继承原始定义;想换排序逻辑,只能另起一个窗口名,比如WINDOW w1 AS (), w2 AS ()
RANGE BETWEEN INTERVAL 却没检查排序列类型,或以为 GROUPS 能智能适配任意字段。这些帧定义背后是执行引擎对数据分布的强假设,一旦假设不成立,就不是报错,而是算出错结果。










