窗口框架决定一次聚合能看到哪些行
窗口分区决定哪些行属于同一组,窗口帧决定其中哪些行参与当前行的某个帧敏感计算。把两种边界混淆,可能让逐笔累计变成重复显示的小计,也可能让 last_value 返回当前值,而报表原本需要的是分区最后一行的值。应先说明计算要回答什么,再选择排序和帧范围。 默认范围取决于是否排序 PostgreSQL 的默认帧是 RANGE:从分区开头到当前行的最后一个并列行。窗口 ORDER BY 中全部表达式都相等的行,属于同一个并列组。因此,有排序时的默认累计和,会包含当前行的全部同值伙伴,即使其中某些行在最终展示结果里排在后面,也已经进入当前帧。 如果窗口没有 ORDER BY,所有行都是并列行,默认帧就覆盖整个分区。因此,sum(amount) OVER (PARTITION BY account) 会重复显示账户总额;添加排序后,计算就变成有顺序的累计结果。“默认总是整个组”和“默认每次恰好多加一行”都不能同时描述这两种情况,不能把其中一种当成通用规则。 窗口顺序与最终输出顺序是分开的。外层 ORDER BY 可以改变展示排列,而不改变帧。反过来,在 RANGE 窗口内部加入唯一标识,可能把原来的并列组拆开,改变计算结果;这不只是让页面看起来稳定,而是修改了语义,应当按需求审阅。 按行、按值域和按并列组 ROWS 计算窗口排序中的位置。从前两行到当前行,最多包含三行;靠近分区起点时还会更少。它不表示三天或三分钟。如果多个事件时间戳相同,而业务需要确定的逐事件顺序,就应加入合适的唯一标识,明确同一时刻的事件谁先谁后。 RANGE 使用排序值和并列边界。指定偏移量时,PostgreSQL 要求只有一个排序表达式,并使用兼容的偏移类型,例如时间戳可以配合 interval。基于时间值域的帧按时间差纳入事件,忙时可能包含很多行,闲时可能很少。GROUPS 则按并列组计数,让帧沿不同排序值对应的组移动,同时保留组内全部并列行。这三者表达的是不同的计量单位。 first_value、last_value、nth_value,以及作为窗口函数使用的聚合,都依赖帧范围。排名和 lag、lead 的定义则基于分区顺序,并非相同规则;缩小帧不会让 lag 忘记分区中的前一行。解释某个窗口函数前,应查看该函数的语义,不能把“只看到帧内的行”套用到所有函数上。 帧成员范围不会覆盖函数本身的空值规则。PostgreSQL 18 的 last_value 及相关取值函数采用 RESPECT NULLS,末行为空时结果也为空;sum 等聚合通常忽略空输入。应先确定报表要末行取值、最后一个非空值,还是数值最大值。这些需求不能仅靠把帧延长到分区末尾就互相替代,改窗口之前要把空值案例写入验收条件。 ...