赞
踩
窗口函数可以拆分为【窗口+函数】。窗口函数官网指路:
- from ->
- join ->
- on ->
- where ->
- group by->
- with (可以在分组后面加上 with rollup,在分组之后对每个组进行全局汇总) ->
- select 后面的普通字段,聚合函数->
- having(having中可以使用select 字段别名) ->
- distinct ->
- order by ->
- limit
<窗口函数>window_name over ( [partition by 字段...] [order by 字段...] [窗口子句] )
- rows between unbounded preceding and unbounded following; -- 上无边界到下无边界(一般用于求 总和)
- rows between unbounded preceding and current row; --上无边界到当前记录(累计值)
- rows between 1 preceding and current row; --从上一行到当前行
- rows between 1 preceding and 1 following; --从上一行到下一行
- rows between current row and 1 following; --从当前行到下一行
ps: over()里面有order by子句,但没有窗口子句时 ,即: <窗口函数> over ( partition by 字段... order by 字段... ),此时窗口子句是有默认值的----> rows between unbounded preceding and current row (上无边界到当前行)。
此时窗口函数语法:<窗口函数> over ( partition by 字段... order by 字段... ) 等价于
<窗口函数> over ( partition by 字段... order by 字段... rows between unbounded preceding and current row)
需要注意有个特殊情况:当order by 后面跟的某个字段是有重复行的时候, <窗口函数> over ( partition by 字段... order by 字段... ) 不写窗口子句的情况下,窗口子句的默认值是:range between unbounded preceding and current row(上无边界到当前相同行的最后一行)。
因此,遇到order by 后面跟的某个字段出现重复行,且需要计算【上无边界到当前行】,那就需要手动指定窗口子句 rows between unbounded preceding and current row ,偷懒省略窗口子句会出问题~
总结如下:
- 1、窗口子句不能单独出现,必须有order by子句时才能出现。
- 2、当省略窗口子句时:
- a) 如果存在order by则默认的窗口是unbounded preceding and current row --当前组的第一行到当前行,即在当前组中,第一行到当前行
- b) 如果没有order by则默认的窗口是unbounded preceding and unbounded following --整个组
ps: 窗口函数的执行顺序是在where之后,所以如果where子句需要用窗口函数作为条件,需要多一层查询,在子查询外面进行。
【例如】求出登录记录出现间断的用户Id
- select
- id
- from (
- select
- id,
- login_date,
- lead(login_date, 1, '9999-12-31')
- over (partition by id order by login_date) next_login_date
- --窗口函数 lead(向后取n行)
- --lead(column1,n,default)over(partition by column2 order by column3) 查询当前行的后边第n行数据,如果没有就为null
- from (--用户在同一天可能登录多次,需要去重
- select
- id,
- date_format(`date`, 'yyyy-MM-dd') as login_date
- from user_log
- group by id, date_format(`date`, 'yyyy-MM-dd')
- ) tmp1
- ) tmp2
- where datediff(next_login_date, login_date) >=2
- group by id;
哪些函数可以是窗口函数呢?(放在over关键字前面的)
- sum(column) over (partition by .. order by .. 窗口子句);
- count(column) over (partition by .. order by .. 窗口子句);
- max(column) over (partition by .. order by .. 窗口子句);
- min(column) over (partition by .. order by .. 窗口子句);
- avg(column) over (partition by .. order by .. 窗口子句);
- collect_list (column) over (partition by .. order by .. 窗口子句);
- collect_set (column) over (partition by .. order by .. 窗口子句);
需要注意:
- 1.count(*)操作时会统计null值,count(column)会过滤掉null值;
- 2.事实上除了count(*)计算,剩余的聚合函数例如: max(column),min(column),avg(column),count(column) 函数会过滤掉null值
ps : 高级聚合函数:
collect_list 收集并形成list集合,结果不去重;
collect_set 收集并形成set集合,结果去重;
举例:
- --每个月的入职人数以及姓名
-
- select
- month(replace(hiredate,'/','-')),
- count(*) as cnt,
- collect_list(name) as name_list
- from employee
- group by month(replace(hiredate,'/','-'));
-
-
- /*
- 输出结果
- month cn name_list
- 4 2 ["宋青书","周芷若"]
- 6 1 ["黄蓉"]
- 7 1 ["郭靖"]
- 8 2 ["张无忌","杨过"]
- 9 2 ["赵敏","小龙女"]
- */
row_number() 、rank()、dense_rank() 函数不支持自定义窗口子句。
- -- 顺序排序——1、2、3
- row_number() over(partition by .. order by .. )
-
- -- 并列排序,跳过重复序号——1、1、3(横向加)
- rank() over(partition by .. order by .. )
-
- -- 并列排序,不跳过重复序号——1、1、2(纵向加)
- dense_rank() over(partition by .. order by .. )
lag和lead函数不支持自定义窗口子句。
- -- 取得column列的前n行,如果存在则返回,如果不存在,返回默认值default
- lag(column,n,default) over(partition by.. order by...) as lag_test
- -- 取得column列的后n行,如果存在则返回,如果不存在,返回默认值default
- lead(column,n,default) over(partition by.. order by...) as lead_test
- first_value(column,true) ---当前窗口column列的第一个数值,如果有null值,则跳过
- first_value(column,false) ---当前窗口column列的第一个数值,如果有null值,不跳过
- last_value(column,true) --- 当前窗口column列的最后一个数值,如果有null值,则跳过
- last_value(column,false) --- 当前窗口column列的最后一个数值,如果有null值,不跳过
rank/dense_rank/row_number 函数,一般用于求分组topN。
- -- 顺序排序——1、2、3
- row_number() over(partition by .. order by .. )
-
- -- 并列排序,跳过重复序号——1、1、3(横向加)
- rank() over(partition by .. order by .. )
-
- -- 并列排序,不跳过重复序号——1、1、2(纵向加)
- dense_rank() over(partition by .. order by .. )
根据学生成绩表,求出每个学生成绩第二高的科目。
- create table if not exists table5
- (
- class string comment '学科',
- student string comment '学生姓名',
- score int comment '成绩'
- )
- comment '学生成绩表';
-
- insert overwrite table table5 values
- ('a','吱吱1',100),
- ('a','吱吱2',60),
- ('b','吱吱1',80),
- ('b','吱吱2',70),
- ('c','吱吱2',50),
- ('c','吱吱3',90);
- 3种排序函数的区别:
-
- row_number (行号)-- 1 2 3 ;
-
- rank (重复跳过)--1 1 3;
-
- dense_rank (重复不跳过) --1 1 2
- select
- class,
- student
- from (
- select
- class,
- student,
- score,
- dense_rank() over (partition by student order by score desc) rn
- from table5
- ) tmp1
- where rn = 2;
排序函数在分组tpoN场景应用十分广泛,需要注意的是在sql语句中,窗口函数的执行顺序是在where过滤条件之后,所以如果where子句需要用窗口函数作为条件,需要多一层查询,在子查询外面进行。
查询订单明细表(order_detail)中销量(下单件数)排名第二的商品id,如果不存在返回null,如果存在多个排名第二的商品则需要全部返回。
- create table if not exists table19
- (
- order_detail_id string comment '订单明细id',
- order_id string comment '订单id',
- sku_id string comment '商品id',
- create_date string comment '商品的下单日期',
- price double comment '商品单价',
- sku_num int comment '商品件数'
- ) comment '订单明细表';
-
- insert overwrite table table19 values
- ('1','1','1','2021-09-30',2000.00,2),
- ('2','1','3','2021-09-30',5000.00,5),
- ('22','10','4','2020-10-02',6000.00,1),
- ('23','10','5','2020-10-02',500.00,24),
- ('24','10','6','2020-10-02',2000.00,5);
- with total_sku as (
- select
- sku_id,
- sum(sku_num) total_nums
- from table19
- group by sku_id
- ),
- rank_order as (
- select
- sku_id,
- dense_rank() over (order by total_nums desc) dr
- from total_sku
- )
- select
- sku_id
- from rank_order
- where dr = 2;
在写hql的时候,可以通过多段的with as 语句,使得整体的代码块结构清晰,易理解。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。