sql中有一类函数叫做聚合函数,例如sum()、avg()、max()等等,这类函数可以将多行数据按照规则聚集为一行,一般来讲聚集后的行数是要少于聚集前的行数的.但是有时我们想要既显示聚集前的数据,又要显示聚集后的数据,这时我们便引入了窗口函数.
开窗函数一般就是说的是over()函数,其窗口是由一个 OVER 子句 定义的多行记录,其作用就如同它的名字,就是限定出一个窗口。
分析函数种类较多,指的是对数据进行各种处理,分析的函数,大体上包括但不仅限于:聚合函数,排名函数,取一些特定位置范围的数值的函数等等。
聚合函数的作用域是由 GROUP BY 子句聚合的组,而窗口函数则作用于一个窗口,。聚合函数对其所作用的每一组记录输 出一条结果,而窗口函数对其所作用的窗口中的每一行记录输出一条结果,简单理解,就是对查询的结果多出一列,这一列可以是聚合值,也可以是排序值。
开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化
可以理解为一个分析函数必然要搭配一个窗口函数,以指定原数据的规则,便于分析函数实现。
有很多文档或资料中,对以上函数的叫法较为混乱,现做以下整理(不断补充):
窗口函数:一般指的是分析函数,有时指的是开窗+分析
聚合开窗函数,排序开窗函数:指的是开窗+分析,是安装分析函数的不同类型进行的命名。聚合开窗函数指的分析函数类型是聚合函数,排序开窗函数指的分析函数是排序函数。
over()开窗函数,其括号内部主要有两种形式,固定搭配,不能更改:
over(distribute by…sort by…) 和 over(partition by…order by…)
patition by是按照一个一个reduce去处理数据的,所以要使用全局排序order by
distribute by是按照多个reduce去处理数据的,所以对应的排序是局部排序sort by
窗口大小:hive的窗口大小默认是从起始行到当前行的
select dt,name,score,
min(score)
over(distribute by dt sort by score desc)
from student;
注意点:此处的min()函数是分析函数,而不是聚合函数,所以其作用范围是由开窗函数over()限定的。
结果为:
此时最后一列最小分数的结果是有问题的,问题根源就在于窗口大小的变化,由默认的窗口大小规则可知,此处的排序规则应定为升序排序。
1 分析函数的使用一定要注意窗口的范围,因为窗口的范围限定了分析函数的作用范围
2 分析函数会对窗口中的每一行数据输出一个结果,即会对查询的结果多出一列,这一列可以是聚合结果,也可以是排序结果
和原来的聚合函数是一样的效果。
书写顺序: sum( ) over( )
注意:排名函数不支持window子句,即不支持自定义窗口大小
row_number(): 没有并列,相同名次顺序排序
rank(): 有并列,相同名次空位(即类似于1 1 3)
dense_rank(): 有并列,相同名次不空位(即类似于1 1 2)
注意:窗口函数的别名位置是在over()子句之后
lag()
LAG(col,n,DEFAULT) 用于统计窗口内往上第n行值
第一个参数为列名,第二个参数为往上第n行(可选,默认为1),第三个参数为默认值(当往上n行之内,若当某一行为NULL时候,取默认值,如不指定,则为NULL)
lead()
与LAG相反
LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值
第一个参数为列名,第二个参数为往下第n行(可选,默认为1),第三个参数为默认值(当往下第n行为NULL时候,取默认值,如不指定,则为NULL)
first_value()
取分组内排序后,截止到当前行,第一个值.
如果不指定ORDER BY,则默认按照记录在文件中的偏移量进行排序,会出现错误的结果
last_value()
取分组内排序后,截止到当前行,最后一个值
如果不指定ORDER BY,则默认按照记录在文件中的偏移量进行排序,会出现错误的结果
当在查询过程中,在嵌套子查询中使用了窗口函数以后,那么在嵌套子查询外,一定要使用上窗口字段,否则,最后的结果是和使用过窗口函数那个表是毫无关系的。
举例:
//环境是spark环境,但是原理一样
val df2: DataFrame = scc.sql("select id,time,url from (" +
"select *,dense_rank() over(distribute by id sort by unix_timestamp(time,'HH:mm:ss') asc) rk1 " +
"from URL) where rk1<>0")
/**
此处,本身我不需要排序字段,只要排序后,将其他字段拿出即可,但是如果不加rk1字段,是跟原来一模一样,和没使用开窗函数是一样的。
所以,我最后为了最后结果,要在where上加上rk1字段,而且,跟嵌套子查询嵌了几层没关系,只要使用了,然后嵌套外层又没使用窗口结果字段,那么就不行
*/