什么是窗口函数
hive中开窗函数通过over
关键字声明;窗口函数,准确地说,函数在窗口中的应用;比如sum
函数不仅可在group by
后聚合,在可在窗口中应用;
hive中groupby算子和开窗over,shuffle的逻辑都是一样的;map时生成键值对,key在groupby中是group by
后跟的字段,在over
中是partition by
后跟的字段;
select group_name,sum(sales) as sum_sales from dw_sec_saler_info group by group_name
中分组(key)字段是group_name
select group_name,id,sum(sales) over(partition by group_name) as sum_sales from dw_sec_saler_info
分组(key)字段也是group_name
;如果开窗没有指定partition by
窗口,则函数是对全局数据应用;
与groupby不同的是,groupby每个key返回一条记录,而开窗函数,在开窗之前数据有多少行返回多少行;类似于在窗口中对每行数据应用了一个map函数,map函数传入的是指定窗口的数据,返回窗口函数计算的值;
一个select子句中,如果有多个开窗函数,尽管函数不同,倘若开窗分组字段是一样的(partition by字段是一样的),这几个开窗函数在逻辑计划可能是由一个mr实现的,因为shuffle的key是一样的;在程序逻辑上后面的开窗不需要再次数据混洗,map后,在reduce依次完成该分组多个开窗函数计算
比如:partition by
的字段都是class
sql
select name
,class -- 班级
,english_score -- 英语成绩
,math_score -- 数学成绩
,row_number() over(order by english_score+math_score) as total_rank -- 总排名,该窗口由一个mr完成
,row_number() over(partiton by class order by english_score) as english_class_rank -- 班级中英语排名
,row_number() over(partiton by class order by math_score) as math_class_rank -- 班级中数学排名
from dw_cus_class_score_info
以上sql,通常由两个mr完成;一个mr完成class开窗逻辑,计算english_class_rank
和 math_class_rank
,另外一个mr计算total_rank
;具体看执行计划;
开窗函数应用
语法:函数 + over( [partition by ...] [order by ...] [窗口子句] )
over
:开窗关键字
partition by
:声明窗口划分依据,把partition by后字段相同的数据划到同一个窗口;如果没有指定分组字段,则是对全局数据应用函数;
order by
:排序字段;需要注意的是,没有指定该关键字,每次返回排序可能不一样;
窗口子句:可以进一步限定范围;语法:(rows | range) between ... and ...
row就行的相对位置,range 表示的是值, 表示比这个值小n的行,比这个值大n的行即range between 是以当前值为锚点进行计算
如果没有窗口子句,则是窗口所有数据;等价于rows between unbounded preceding and unbounded following
(rows | range) between (unbounded | [num]) preceding and ([num] preceding | current row | (unbounded | [num]) following
(rows | range) between current row and (current row | (unbounded | [num]) following)
(rows | range) between [num] following and (unbounded | [num]) following
其中:
unbounded preceding:组内第一行数据
n preceding:组内当前行的前n行数据
current row:当前行数据
n following:组内当前行的后n行数据
unbounded following:组内后一行数据
图片转自:https://zhuanlan.zhihu.com/p/401242504
比如各个小组按日期升序排列对销售额累计求和:
sql
select sales,dt,group_name
,sum(sales) over(partition by group_name -- 窗口分组字段是group_name
order by dt asc -- 按日期升序排列
rows between unbounded preceding and current_now -- 窗口是第一行到当前行
) as consum_sales
from table_name
常用窗口函数
1. 排名函数
- row_number
连续不重复排序,比如:1,2,3,4,5 - rank
重复跨越排序,如果两个数据是一样的,排名是一样的;比如:1,2,3,3,5;有两个3,占了两个位置,所以下一个排序是5 - desne_rank
重复连续排序,如果两个数据是一样的,排名是一样的,但下一个排名数字是紧挨着上一个排名,比如:1,2,3,3,4
2. 聚合计算函数
- sum
求和 - avg
平均数 - count
计数 - max/min
最大/最小值
3. 序列函数
- lag
返回当前数据行的上一行数据,如果当前数据行是第一行没有上一行则返回null - lead
返回当前数据行的下一行数据,没有下一行则返回null - first_value
取分组内排序后,截止到当前行,第一个值 - last_value
分组内排序后,截止到当前行,后一个值 - ntile
一个分位函数,将分组的数据按照顺序切分成n片,返回当前切片值。如果取top10%,可以先先拆成10份,开窗后再筛选分位函数结果是1这部分。
开窗后对窗口函数返回字段筛选,这个筛选逻辑是不需要再起mr的,
比如row_number后(返回字段rnk),再取rnk=1。在sql上需要嵌套一层子查询。但在实际执行时这个rnk=1的逻辑是在开窗计算reduce里完成的筛选,不需要再起一个mr。具体可查看执行计划