窗口框架决定一次聚合能看到哪些行
窗口分区决定哪些行属于同一组,窗口帧决定其中哪些行参与当前行的某个帧敏感计算。把两种边界混淆,可能让逐笔累计变成重复显示的小计,也可能让 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 忘记分区中的前一行。解释某个窗口函数前,应查看该函数的语义,不能把“只看到帧内的行”套用到所有函数上。 坏掉的样子 有序默认帧中的 last_value,通常取得当前帧末尾的值,并不是整个分区末尾的值。排序唯一时,这恰好就是当前行。若要在每一行重复显示分区最后一个值,需要把结束边界写为 UNBOUNDED FOLLOWING,并提供能够定义“最后”的顺序。按时间排序后的末行值,与该字段的数值最大值,也不是同一个问题。 ...