Skip to content

窗口函数

窗口函数的语法:

function_name([expression [, expression ... ]]) OVER( [ PARTITION BY expr1 ] [ ORDER BY expr2 [ DESC ] ] )

  • [ PARTITION BY expr1 ] 可选参数,partition by 会将查询的数据进行分组,窗口函数会独立的处理每一个分组。
  • [ ORDER BY expr2 [ DESC ] ] 可选参数,根据expr2进行排序。
  • [ DESC ] 可选参数,默认会使用正序排序,使用DESC参数后结果将会使用倒序排序。

本文的介绍中,会使用下表的数据做为示例数据进行说明。

idzh_namedirectorprime_genreruntimerate_numvotesstarspubdate
1星际穿越克里斯托弗·诺兰剧情1697.75416252马修·麦康纳2014-11-12
2辛德勒的名单史蒂文·斯皮尔伯格剧情1957.97329749连姆·尼森1993-11-30
3唐伯虎点秋香李力持喜剧1024.25260928周星驰1993-07-01
4致命ID詹姆斯·曼高德剧情909.57271970约翰·库萨克2003-04-25
5指环王3:王者无敌彼得·杰克逊剧情2015.24255167维果·莫腾森2004-03-12
6城市之光查理·卓别林剧情870.1134216查理·卓别林1931-01-30
7这个男人来自地球理查德·沙因克曼剧情874.15147835大卫·李·史密斯2007-11-13
8上帝也疯狂加美·尤伊斯喜剧1092.6783463Marius Weyers1980-09-10
9大话西游之大圣娶亲刘镇伟喜剧953.91395680周星驰2014-10-24
10放牛班的春天克里斯托夫·巴拉蒂剧情979.92396189杰拉尔·朱诺2004-10-16

dense_rank

语法dense_rank() OVER( [ PARTITION BY expr1 ] [ ORDER BY expr2 [ DESC ] ] )
参数说明1. expr1:可选参数,用于分区的表达式,可选类型:任意类型;
2. expr2:可选参数,用于排序的表达式,可选类型:任意类型;
3. DESC:可选参数,指定按降序排序,默认升序
返回值类型数字
描述返回当前窗口的排名(不保持间隔), 计数从 1 开始。dense_rank与rank的区别在于dense_rank在出现重复排名时,下一个排名会仅接其上的序号,而rank则会跳过因为重复而没有出现的序号。
  • 示例
text
dense_rank() OVER (PARTITION BY {prime_genre} ORDER BY {runtime} ASC)

结果如下:

runtimeprime_genrevalue
169剧情4
195剧情5
102喜剧2
90剧情2
201剧情6
87剧情1
87剧情1
109喜剧3
95喜剧1
97剧情3

lag

语法lag(ARG, I) OVER( [ PARTITION BY expr1 ] [ ORDER BY expr2 [ DESC ] ] )
参数说明1. ARG:窗口框架,可选类型:任意类型;
2. I:指定记录与当前记录的距离,可选类型:数字;
3. expr1:可选参数,用于分区的表达式,可选类型:任意类型;
4. expr2:可选参数,用于排序的表达式,可选类型:任意类型;
5. DESC:可选参数,指定按降序排序,默认升序
返回值类型任意类型
描述窗口函数,向前获得相对于当前记录指定i距离的那条记录的 arg 数据
  • 示例
text
lag({zh_name}, 2) OVER (ORDER BY {runtime} ASC)

结果如下:

zh_nameruntimevalue
星际穿越169唐伯虎点秋香
辛德勒的名单195上帝也疯狂
唐伯虎点秋香102大话西游之大圣娶亲
致命ID90城市之光
指环王3:王者无敌201星际穿越
城市之光87null
这个男人来自地球87null
上帝也疯狂109放牛班的春天
大话西游之大圣娶亲95这个男人来自地球
放牛班的春天97致命ID

lead

语法lead(ARG, I)
参数说明1. 参与计算的参数,可选类型:任意类型;
2. 整数类型参数,可选类型:数字
返回值类型任意类型
描述窗口函数,向后获得相对于当前记录指定 i 距离的那条记录的 arg 数据
  • 示例
text
lead({zh_name}, 2) OVER (ORDER BY {runtime} ASC)

结果如下:

zh_nameruntimevalue
星际穿越169指环王3:王者无敌
辛德勒的名单195null
唐伯虎点秋香102星际穿越
致命ID90放牛班的春天
指环王3:王者无敌201null
城市之光87致命ID
这个男人来自地球87大话西游之大圣娶亲
上帝也疯狂109辛德勒的名单
大话西游之大圣娶亲95唐伯虎点秋香
放牛班的春天97上帝也疯狂

nth_value

语法nth_value(ARG, I) OVER( [ PARTITION BY expr1 ] [ ORDER BY expr2 [ DESC ] ] )
参数说明1. ARG:窗口框架,可选类型:任意类型;
2. I:想要获取的数据的行数,可选类型:数字;
3. expr1:可选参数,用于分区的表达式,可选类型:任意类型;
4. expr2:可选参数,用于排序的表达式,可选类型:任意类型;
5. DESC:可选参数,指定按降序排序,默认升序
返回值类型任意类型
描述获取窗口中第 i 个 arg 值
  • 示例
text
NTH_VALUE({zh_name}, 2) OVER( PARTITION BY {prime_genre} ORDER BY {runtime} DESC )

结果如下:

zh_nameruntimeprime_genrevalue
星际穿越169剧情辛德勒的名单
辛德勒的名单195剧情辛德勒的名单
唐伯虎点秋香102喜剧唐伯虎点秋香
致命ID90剧情辛德勒的名单
指环王3:王者无敌201剧情null
城市之光87剧情辛德勒的名单
这个男人来自地球87剧情辛德勒的名单
上帝也疯狂109喜剧null
大话西游之大圣娶亲95喜剧唐伯虎点秋香
放牛班的春天97剧情辛德勒的名单

rank

语法rank() OVER( [ PARTITION BY expr1 ] [ ORDER BY expr2 [ DESC ] ] )
参数说明1. expr1:可选参数,用于分区的表达式,可选类型:任意类型;
2. expr2:可选参数,用于排序的表达式,可选类型:任意类型;
3. DESC:可选参数,指定按降序排序,默认升序
返回值类型数字
描述返回当前窗口的排名(保持间隔),计数从 1 开始
  • 示例
text
rank() OVER (PARTITION BY {prime_genre} ORDER BY {runtime} ASC)

结果如下:

runtimeprime_genrevalue
169剧情5
195剧情6
102喜剧2
90剧情3
201剧情7
87剧情1
87剧情1
109喜剧3
95喜剧1
97剧情4

row_number

语法row_number() OVER( [ PARTITION BY expr1 ] [ ORDER BY expr2 [ DESC ] ] )
参数说明1. expr1:可选参数,用于分区的表达式,可选类型:任意类型;
2. expr2:可选参数,用于排序的表达式,可选类型:任意类型;
3. DESC:可选参数,指定按降序排序,默认升序
返回值类型数字
描述返回当前窗口的行数,计数从 1 开始
  • 示例
text
ROW_NUMBER() OVER (PARTITION BY {prime_genre} ORDER BY {runtime} ASC)

结果如下:

runtimeprime_genrevalue
169剧情5
195剧情6
102喜剧2
90剧情3
201剧情7
87剧情2
87剧情1
109喜剧3
95喜剧1
97剧情4

支持窗口计算的聚合函数

部分聚合函数也能够作为窗口函数使用。仅当聚集函数调用后面跟着一个OVER子句时,聚合函数会进行窗口计算。函数列表如下:

函数名描述
SUM计算数值列的总和
AVG计算数值列的平均值
MAX计算数值列的最大值
MIN计算数值列的最小值
CORR计算两列数值的相关系数
COVAR_POP计算两列数值的总体协方差
COVAR_SAMP计算两列数值的样本协方差
REGR_AVGX计算线性回归中自变量的平均值
REGR_AVGY计算线性回归中因变量的平均值
REGR_COUNT计算线性回归中非空数据点的数量
REGR_INTERCEPT计算线性回归方程的截距
REGR_R2计算线性回归的决定系数(R²)
REGR_SLOPE计算线性回归方程的斜率
REGR_SXX计算线性回归中自变量的平方和
REGR_SXY计算线性回归中自变量与因变量的交叉乘积和
REGR_SYY计算线性回归中因变量的平方和
STDDEV_POP计算数值列的总体标准差
STDDEV_SAMP计算数值列的样本标准差
VAR_POP计算数值列的总体方差(总体标准差的平方)
VAR_SAMP计算数值列的样本方差(样本标准差的平方)
STRING_AGG将字符串列按指定方式聚合连接
JSON_AGG将数据聚合为JSON格式数组
COUNT计算数据的行数(可指定列,忽略NULL值;或用*包含所有行)

order by 子句

提示

这里有一个与窗口函数相关的重要概念:对于每一行,在它的分区中的行集合被称为它的窗口帧。
一些窗口函数只作用在窗口帧中的行上,而不是整个分区。此类函数如果同时使用ORDER BY,则帧包括从分区开始到当前行的所有行,以及后续任何与当前行在ORDER BY子句上相等的行。
所有的聚合函数在使用OVER子句中的ORDER BY表达式时,最终的返回结果均会与不使用ORDER BY表达式会有各种各样的差异。

  • 示例1

获取每个班学生的总人数。

COUNT(1) OVER (PARTITION BY {class})
  • 示例2

将各个班级的学生姓名按照成绩顺序拼接成一个JSON数组。

JSON_AGG({student_name}) OVER (PARTITION BY {class} ORDER BY {student_score})

窗口函数注意事项

使用窗口函数做为图表指标时的注意事项:

  1. 窗口函数不是聚合函数,不能作为聚合函数所用。
  2. 图表中有维度列的时候,如果窗口函数使用了 OVER 子句[PARTITION BY expr1]/[ORDER BY expr2],非聚合运算的 expr1 或者 expr2 都必须是图表的维度列,否则会报错。
  3. 假设图表有维度 a,b, 度量为 MAX(COUNT({书籍编号})) OVER (***)。计算的逻辑是:
    • 以 a, b 分组,计算 COUNT({书籍编号}) 的值, 得到一个中间结果: (a, b, count_tmp)
    • 度量是 MAX(COUNT({书籍编号})) OVER (), 计算结果是:在 count_tmp 列中找出最大值
    • 度量是 MAX(COUNT({书籍编号})) OVER (PARTITION BY a), 计算结果是:从(a, tmp) 这个数组中,以a的值为划分依据,找出每个组内tmp的最大值
    • 度量是 MAX(COUNT({书籍编号})) OVER (PARTITION BY b), 计算结果是:从(b, tmp) 这个数组中,以b的值为划分依据,找出每个组内tmp的最大值

衡石分析平台使用手册