最近在搞一些面试和课程答辩的时候,问什么是窗口函数,知道哪些窗口函数?最多的答案就是row_number、rank、dense_rank,在问一下还有其他的吗?这时同学就蒙了,还有其他的窗口函数?其实上面的回答也只是专用窗口函数,并不是窗口函数的整体定义,那今天我们就来好好聊聊窗口函数。
1.窗口函数概念
我们首先来谈谈什么是窗口函数,窗口函数是指,在指定的数据滑动窗口中,实现各种统计分析的操作。窗口函数是与分析函数一起使用,或按照专用窗口函数使用,组成比如:窗口聚合函数、窗口排序函数等实用函数。
常用的分析函数:sum()、max()、min()、avg()、count()、......
专用窗口函数:row_number()、rank()、dense_rank()......
具体语法
这个很重要,只要满足这个语法的都算窗口函数,具体使用语法如下:
分析函数/专用窗口函数 over(partition by 列名 order by 列名 rows between 开始位置 and 结束位置);
窗口函数的3个组成部分可以单独使用,也可以混合使用,也可以全都不用,下面是三部分的详细解释。
1.partition by 字段
是对指定的字段进行分组,后续都会以组为单位,把每个分组单独作为一个窗口进行统计分析操作。划分的范围被称为窗口,这也是窗口函数的由来。
# 案例 01:对窗口中的数据求和,并把求和结果分别分发到对应窗口的每一条数据中
with temp as(
select 'A' as col1,1 as col2
union all
select 'A' as col1,1 as col2
union all
select 'B' as col1,1 as col2
)
select
col1
,sum(col2) over(partition by col1) as '对窗口中的数据求和'
from temp
输出结果:
col 对窗口中的数据求和
A 2
A 2
B 1
案例 02:对整体数据求和,并把求和结果分发到每一条数据中
with temp as(
select 'A' as col1,1 as col2
union all
select 'A' as col1,1 as col2
union all
select 'B' as col1,1 as col2
)
select
col1
,sum(col2) over() as '对整体数据求和'
from temp
输出结果:
col 对整体数据求和
A 3
A 3
B 3
注意:聚合函数是将多条记录聚合为一条;窗口函数是每条记录都会执行,有几条记录执行完还是几条。窗口函数兼具GROUP BY 子句的分组功能以及ORDER BY 子句的排序功能。但是,PARTITION BY 子句并不具备 GROUP BY 子句的汇总功能。
2.order by 字段
大家都知道order by 是排序字段,(这里多说一句四个不要的区别理解了吗?),它用在窗口函数里会有不一样的效果。
情景一:order by 与 partition by 连用的时候,可以对各个分组内的数据,按照指定的字段进行排序。如果没有 partition by 指定分组字段,那么会对全局的数据进行排序。
with temp as(
select 'A' as col1,1 as col2
union all
select 'C' as col1,1 as col2
union all
select 'B' as col1,1 as col2
)
select col1,row_number() over(order by col1 desc) as 排序 from temp
输出结果:
col1 排序
C 1
C 2
B 3
A 4
情景二:当为聚合函数,如max,min,count等时,over中的order by不仅起到窗⼝内排序,还起到窗⼝内从当前⾏到之前所有⾏的聚合(多了⼀个范围)。
案例 01:对数据进行全局排序
with temp_01 as(
select 'A' as user_id,1 as cnt
union all
select 'D' as user_id,2 as cnt
union all
select 'D' as user_id,3 as cnt
union all
select 'B' as user_id,4 as cnt
union all
select 'B' as user_id,5 as cnt
)
select user_id,sum(cnt) over(partition by user_id) as sum_all from temp_01
select user_id,sum(cnt) over(partition by user_id order by cnt) as sum_all from temp_01
情景三:当排序的维度不存在重复的情况下,即 order by 指定的字段,使用 order by + 分析函数 sum(),可以产生求整体累计数的效果。但是当 order by 指定的字段组合,数据存在重复的时候,会在不重复的数据中产生累计效果,而重复的数据中,也是会把整体的累计结果分发到每条重复的数据中。
with temp_01 as(
select 'A' as user_id,1 as cnt
union all
select 'D' as user_id,2 as cnt
union all
select 'D' as user_id,3 as cnt
union all
select 'B' as user_id,4 as cnt
union all
select 'B' as user_id,4 as cnt
)
select user_id,sum(cnt) over(partition by user_id order by cnt) as sum_all from temp_01
3.rows between 开始位置 and 结束位置
rows between 是指划分窗口中,函数具体的作用数据范围。rows between 常用的参数如下:
n preceding:往前
n following:往后
current row:当前行
unbounded:起点(一般结合preceding,following使用)
###########
unbounded preceding:表示该窗口最前面的行(起点)
unbounded following:表示该窗口最后面的行(终点)
这些参数需要好好记忆,使用例子如下:
1.rows between unbounded preceding and current row(表示从起点到当前行的数据进行);
2.rows between current row and unbounded following(表示当前行到终点的数据进行);
3.rows between unbounded preceding and unbounded following (表示起点到终点的数据);
rows between unbounded preceding and current row与 partition by 、order by 连用,可以产生对窗口中的数据求累计数的效果。
with temp_01 as(
select 'D' as user_id,2 as cnt
union all
select 'D' as user_id,2 as cnt
union all
select 'B' as user_id,4 as cnt
union all
select 'B' as user_id,5 as cnt
union all
select 'A' as user_id,1 as cnt
)
select user_id,cnt,sum(cnt) over(partition by user_id order by cnt rows between unbounded preceding and current row) as sum_all from temp_01
2.窗口函数分类
说过了什么是窗口函数,明白什么是窗口函数,所以以后面试过程中问到什么是窗口函数,不要在简单的说排序啦,接下来我们在谈谈具体有哪些函数。
2.1 排序窗口函数
这个就是大家最熟悉,或者也只能回答出来的函数了;
排序并产生自增编号,自增编号不重复且连续:我们可以使用函数:row_number() 、over()。
排序并产生自增编号,自增编号会重复且不连续:我们可以使用函数:rank() 、over()。
排序并产生自增编号,自增编号会重复且连续:我们可以使用函数:dense_rank() 、over()。
2.2 聚合窗口函数
聚合函数配置over形成的窗口函数,可以在是我们实际工作中用到累计,窗口中平均值、窗口中最大值最小值等的场景。
求窗口中的累计值
我们可以使用:sum() over();
求窗口中 3 天的平均价格
我们可以使用 avg() over();
输出结果:
求分组中的最大值/最小值
max() over() as 窗口中的最大值
min() over() as 窗口中的最小值
求分组中的总记录数
我们可以使用 count() over()
举了两个简单的例子,可以参考例子更容易让大家理解。
with temp_01 as(
select 'A' as col1,10 as col2
union all
select 'C' as col1,10 as col2
union all
select 'C' as col1,20 as col2
union all
select 'A' as col1,20 as col2
union all
select 'A' as col1,20 as col2
)
select
col1
,col2
,max(col2) over(partition by col1) as 窗口中的最大值
,min(col2) over(partition by col1) as 窗口中的最小值
from temp_01
结果
输出结果:
col1 col2 窗口中的最大值 窗口中的最小值
A 10 20 10
A 20 20 10
A 20 20 10
C 10 20 10
C 20 20 10
2.3 位移窗口函数
获取分组中往前 n 行的值
基础语法:lead(field,n,default_value) over()
获取分组中往后 n 行的值
基础语法:lag(field,n, default_value) over()
2.4 极值窗口函数
获取分组内第一行的值
我们可以使用first_value(col,true/false) over(),作用是:取分组内排序后,截止到当前行,第一个值。
注意:
1.当第二个参数为 true 的时候,会跳过空值;
2.当 over() 中不指定排序的时候,会默认使用表中数据的原排序。
获取分组内最后一行的值
我们可以使用last_value(col,true/false) over(),作用是:取分组内排序后,截止到当前行,最后一个值。所以,如果使用 order by 排序的时候,想要取最后一个值,需要与 rows between unbounded preceding and unbounded following 连用。
注意:
1.当第二个参数为 true 的时候,会跳过空值;
2.当 over() 中不指定排序的时候,会默认使用表中数据的原排序。
3.当 over() 中指定排序的时候,要与 rows between unbounded preceding and unbounded following 连用。
2.5 分箱窗口函数
ntile() over() 分箱窗口函数,多用于统计百分比,用于将分组数据按照顺序切分成 n 片,返回当前切片值,如果切片不均匀,默认增加到第一个切片中。
案例:查询考试成绩前 20% 的人。
输出结果
相信介绍到这里,我们对于什么是窗口函数,有哪些窗口函数都有了一个全面的认识了,面试中我们就按照这样的分类一一介绍,来打动我们的面试官。
含泪整理的超全窗口函数:数据开发必备