4.2.8. 窗口函数调用
一个窗口函数调用表示在一个查询选择的行的某个部分上应用一个聚集类的函数。和非窗口聚集函数调用不同,这不会被约束为将被选择的行分组为一个单一的输出行 — 在查询输出中每一个行仍保持独立。不过,窗口函数能够根据窗口函数调用的分组声明(PARTITION BY列表)访问属于当前行所在分组中的所有行。一个窗口函数调用的语法是下列之一:
function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )
其中window_definition的语法是
[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]
可选的frame_clause是下列之一
{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ]
{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]
其中frame_start和frame_end可以是下面形式中的一种
UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset FOLLOWING
UNBOUNDED FOLLOWING
而frame_exclusion可以是下列之一
EXCLUDE CURRENT ROW
EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS
这里,expression表示任何自身不含有窗口函数调用的值表达式。
window_name是对定义在查询的WINDOW子句中的一个命名窗口声明的引用。还可以使用在WINDOW子句中定义命名窗口的相同语法在圆括号内给定一个完整的window_definition,详见SELECT参考页。值得指出的是,OVER wname并不严格地等价于OVER (wname …),后者表示复制并修改窗口定义,并且在被引用窗口声明包括一个帧子句时会被拒绝。
PARTITION BY选项将查询的行分组成为分区,窗口函数会独立地处理它们。PARTITION BY工作起来类似于一个查询级别的GROUP BY子句,不过它的表达式总是只是表达式并且不能是输出列的名称或编号。如果没有PARTITION BY,该查询产生的所有行被当作一个单一分区来处理。ORDER BY选项决定被窗口函数处理的一个分区中的行的顺序。它工作起来类似于一个查询级别的ORDER BY子句,但是同样不能使用输出列的名称或编号。如果没有ORDER BY,行将被以未指定的顺序被处理。
frame_clause指定构成窗口帧的行集合,它是当前分区的一个子集,窗口函数将作用在该帧而不是整个分区。帧中的行集合会随着哪一行是当前行而变化。在RANGE、ROWS或者GROUPS模式中可以指定帧,在每一种情况下,帧的范围都是从frame_start到frame_end。如果frame_end被省略,则末尾默认为CURRENT ROW。
UNBOUNDED PRECEDING的一个frame_start表示该帧开始于分区的第一行,类似地UNBOUNDED FOLLOWING的一个frame_end表示该帧结束于分区的最后一行。
在RANGE或GROUPS模式中,CURRENT ROW的一个frame_start表示帧开始于当前行的第一个平级行(被窗口的ORDER BY子句排序为与当前行等效的行),而CURRENT ROW的一个frame_end表示帧结束于当前行的最后一个平级行。在ROWS模式中,CURRENT ROW就表示当前行。
在offset PRECEDING以及offset FOLLOWING帧选项中,offset必须是一个不包含任何变量、聚集函数或者窗口函数的表达式。offset的含义取决于帧模式:
在ROWS模式中,offset必须得到一个非空、非负的整数,并且该选项表示帧开始于当前行之前或者之后指定数量的行。
在GROUPS模式中,offset也必须得到一个非空、非负的整数,并且该选项表示帧开始于当前行的平级组之前或者之后指定数量的平级组,这里平级组是在ORDER BY顺序中等效的行集合(要使用GROUPS模式,在窗口定义中就必须有一个ORDER BY子句)。
在RANGE模式中,这些选项要求ORDER BY子句正好指定一列。offset指定当前行中那一列的值与它在该帧中前面或后面的行中的列值的最大差值。offset表达式的数据类型会随着排序列的数据类型而变化。对于数字的排序列,它通常是与排序列相同的类型,但对于日期时间排序列它是一个interval。例如,如果排序列是类型date或者timestamp,我们可以写RANGE BETWEEN ‘1 day’ PRECEDING AND ‘10 days’ FOLLOWING。offset仍然要求是非空且非负,不过“非负”的含义取决于它的数据类型。
在任何一种情况下,到帧末尾的距离都受限于到分区末尾的距离,因此对于离分区末尾比较近的行来说,帧可能会包含比较少的行。
注意在ROWS以及GROUPS模式中,0 PRECEDING和0 FOLLOWING与CURRENT ROW等效。通常在RANGE模式中,这个结论也成立(只要有一种合适的、与数据类型相关的“零”的含义)。
frame_exclusion选项允许当前行周围的行被排除在帧之外,即便根据帧的开始和结束选项应该把它们包括在帧中。EXCLUDE CURRENT ROW会把当前行排除在帧之外。EXCLUDE GROUP会把当前行以及它在顺序上的平级行都排除在帧之外。EXCLUDE TIES把当前行的任何平级行都从帧中排除,但不排除当前行本身。EXCLUDE NO OTHERS只是明确地指定不排除当前行或其平级行的这种默认行为。
默认的帧选项是RANGE UNBOUNDED PRECEDING,它和RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同。如果使用ORDER BY,这会把该帧设置为从分区开始一直到当前行的最后一个ORDER BY平级行的所有行。如果不使用ORDER BY,就意味着分区中所有的行都被包括在窗口帧中,因为所有行都成为了当前行的平级行。
限制是frame_start不能是UNBOUNDED FOLLOWING、frame_end不能是UNBOUNDED PRECEDING,并且在上述frame_start和frame_end选项的列表中frame_end选择不能早于frame_start选择出现 — 例如不允许RANGE BETWEEN CURRENT ROW AND offset PRECEDING,但允许ROWS BETWEEN 7 PRECEDING AND 8 PRECEDING,虽然它不会选择任何行。
如果指定了FILTER,那么只有对filter_clause计算为真的输入行会被交给该窗口函数,其他行会被丢弃。只有是聚集的窗口函数才接受FILTER 。
内建的窗口函数在表 9.60中介绍。用户可以加入其他窗口函数。此外,任何内建的或者用户定义的通用聚集或者统计性聚集都可以被用作窗口函数(有序集和假想集聚集当前不能被用作窗口函数)。
使用的语法被用来把参数较少的聚集函数当作窗口函数调用,例如count() OVER (PARTITION BY x ORDER BY y)。星号(*)通常不被用于窗口相关的函数。窗口相关的函数不允许在函数参数列表中使用DISTINCT或ORDER BY。
只有在SELECT列表和查询的ORDER BY子句中才允许窗口函数调用。
更多关于窗口函数的信息可以在第 3.5 节、第 9.21 节以及第 7.2.5 节中找到。
9.21. 窗口函数
窗口函数提供在与当前查询行相关的行集合上执行计算的能力。有关这个特性的介绍请见第 3.5 节。 语法细节则请见第 4.2.8 节。
表 9.60列出了内建的窗口函数。注意必须使用窗口函数的语法调用这些函数; 一个OVER子句是必需的。
在这些函数之外,任何内建的或者用户定义的通用或者统计性聚集(即非有序集和假想集聚集)都可以被用作一个窗口函数,内建聚集的列表请见第 9.20 节。仅当聚集函数调用后面跟着一个OVER子句时,聚集函数才会像窗口函数那样工作,否则它们会按非窗口聚集的方式运行并且为整个集合返回一个单一行。
表 9.60. 通用窗口函数
函数 | 返回类型 | 描述 |
---|---|---|
row_number() | bigint | 当前行在其分区中的行号,从1计 |
rank() | bigint | 带间隙的当前行排名; 与该行的第一个同等行的row_number相同 |
dense_rank() | bigint | 不带间隙的当前行排名; 这个函数计数同等组 |
percent_rank() | double precision | 当前行的相对排名: (rank- 1) / (总行数 - 1) |
cume_dist() | double precision | 累积分布:(在当前行之前或者平级的分区行数) / 分区行总数 |
ntile(num_buckets integer) | integer | 从1到参数值的整数范围,尽可能等分分区 |
lag(value anyelement [, offset integer [, default anyelement ]]) | 和value的类型相同 | 返回value,它在分区内当前行的之前offset个位置的行上计算;如果没有这样的行,返回default替代(必须和value类型相同)。offset和default都是根据当前行计算的结果。如果忽略它们,则offset默认是1,default默认是空值 |
lead(value anyelement [, offset integer [, default anyelement ]]) | 和value类型相同 | 返回value,它在分区内当前行的之后offset个位置的行上计算;如果没有这样的行,返回default替代(必须和value类型相同)。offset和default都是根据当前行计算的结果。如果忽略它们,则offset默认是1,default默认是空值 |
first_value(value any) | same type as value | 返回在窗口帧中第一行上计算的value |
last_value(value any) | 和value类型相同 | 返回在窗口帧中最后一行上计算的value |
nth_value(value any, nth integer) | 和value类型相同 | 返回在窗口帧中第nth行(行从1计数)上计算的value;没有这样的行则返回空值 |