hive中的高阶函数-窗口函数

窗口函数的使用,配合聚合函数使用,能够更加灵活的规约表的格式,大大减少工作量

说在前面

窗口函数,执行顺序是最后执行仅仅是在order by之前执行。

over函数子句的使用

准备的测试数据

jackma,2018-01-01,10
tonyma,2018-01-02,15
jackma,2018-02-03,23
tonyma,2018-01-04,29
jackma,2018-01-05,46
jackma,2018-04-06,42
tonyma,2018-01-07,50
jackma,2018-01-08,55
martma,2018-04-08,62
martma,2018-04-09,68
neilma,2018-05-10,12
martma,2018-04-11,75
neilma,2018-06-12,80
martma,2018-04-13,94

create table t_window(name string,orderdate string,cost double)
row format delimited fields terminated by ',' ;
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

测试函数

查看每个用户购买的总次数
select name,count(*) as num from t_window group by name ;

select name,count(*)  over(partition by name ) as num from t_window ;

 
 
  • 1
  • 2
  • 3
  • 4

结果:
jackma 5
martma 4
neilma 2
tonyma 3

partition by子句

查看每个用户的消费,每个月的消费的全部用户的消费额

select name,month(orderdate),cost,sum(cost)  over(partition by month(orderdate)) as num from t_window
 
 
  • 1

jackma 1 10.0 205.0
jackma 1 55.0 205.0
tonyma 1 50.0 205.0
jackma 1 46.0 205.0
tonyma 1 29.0 205.0
tonyma 1 15.0 205.0
jackma 2 23.0 23.0
martma 4 94.0 341.0
jackma 4 42.0 341.0
martma 4 75.0 341.0
martma 4 68.0 341.0
martma 4 62.0 341.0
neilma 5 12.0 12.0
neilma 6 80.0 80.0

查询结果 可以看到都是按照月 每个人的消费都是按照月进行汇总的,需要注意的是总的条数和原来的t_window表是完保持一致的。

order by 子句

在上面的例子中增加,要是需要累计日期每日的累计消费额 可以增加order by子句

我们首先要理解两个概念:
- 如果只使用partition by子句,未指定order by的话,我们的聚合是分组内的聚合.
- 使用了order by子句,未使用window子句的情况下,默认从起点到当前行.

select name,orderdate,cost,sum(cost)  over(partition by month(orderdate) order by orderdate) as num from t_window
 
 
  • 1

jackma 2018-01-01 10.0 10.0
tonyma 2018-01-02 15.0 25.0
tonyma 2018-01-04 29.0 54.0
jackma 2018-01-05 46.0 100.0
tonyma 2018-01-07 50.0 150.0
jackma 2018-01-08 55.0 205.0
jackma 2018-02-03 23.0 23.0
jackma 2018-04-06 42.0 42.0
martma 2018-04-08 62.0 104.0
martma 2018-04-09 68.0 172.0
martma 2018-04-11 75.0 247.0
martma 2018-04-13 94.0 341.0
neilma 2018-05-10 12.0 12.0
neilma 2018-06-12 80.0 80.0

order by子句会让输入的数据强制排序(文章前面提到过,窗口函数是SQL语句最后执行的函数,因此可以把SQL结果集想象成输入数据)。Order By子句对于诸如Row_Number(),Lead(),LAG()等函数是必须的,因为如果数据无序,这些函数的结果就没有任何意义。因此如果有了Order By子句,则Count(),Min()等计算出来的结果就没有任何意义

我们首先要理解两个概念:
- 如果只使用partition by子句,未指定order by的话,我们的聚合是分组内的聚合.
- 使用了order by子句,未使用window子句的情况下,默认从起点到当前行.

window子句

上面是使用了partition by 进行了分组,如果想进行更加细的划分,可以使用window子句。

当同一个select查
询中存在多个窗口函数时,他们相互之间是没有影响的.每个窗口函数应用自己的规则。

几个名词的解释:
window子句:
- PRECEDING:往前
- FOLLOWING:往后
- CURRENT ROW:当前行
- UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING:表示到后面的终点

select name,orderdate,cost,
sum(cost) over() as sample1,--所有行相加
sum(cost) over(partition by name) as sample2,--按name分组,组内数据相加
sum(cost) over(partition by name order by orderdate) as sample3,--按name分组,按照日期从小到大进行 cost的累加
sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and current row )  as sample4 ,--和sample3一样,由起点到当前行的聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING   and current row) as sample5, --当前行和前面一行做聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING   AND 1 FOLLOWING  ) as sample6,--当前行和前边一行及后面一行
sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING ) as sample7 --当前行及后面所有行
from t_window;
 
 

jackma 2018-01-01 10.0 661.0 176.0 10.0 10.0 10.0 56.0 176.0
jackma 2018-01-05 46.0 661.0 176.0 56.0 56.0 56.0 111.0 166.0
jackma 2018-01-08 55.0 661.0 176.0 111.0 111.0 101.0 124.0 120.0
jackma 2018-02-03 23.0 661.0 176.0 134.0 134.0 78.0 120.0 65.0
jackma 2018-04-06 42.0 661.0 176.0 176.0 176.0 65.0 65.0 42.0
martma 2018-04-08 62.0 661.0 299.0 62.0 62.0 62.0 130.0 299.0
martma 2018-04-09 68.0 661.0 299.0 130.0 130.0 130.0 205.0 237.0
martma 2018-04-11 75.0 661.0 299.0 205.0 205.0 143.0 237.0 169.0
martma 2018-04-13 94.0 661.0 299.0 299.0 299.0 169.0 169.0 94.0
neilma 2018-05-10 12.0 661.0 92.0 12.0 12.0 12.0 92.0 92.0
neilma 2018-06-12 80.0 661.0 92.0 92.0 92.0 92.0 92.0 80.0
tonyma 2018-01-02 15.0 661.0 94.0 15.0 15.0 15.0 44.0 94.0
tonyma 2018-01-04 29.0 661.0 94.0 44.0 44.0 44.0 94.0 79.0
tonyma 2018-01-07 50.0 661.0 94.0 94.0 94.0 79.0 79.0 50.0

序列函数
NTILE
  • NTILE(n),用于将分组数据按照顺序切分成n片,返回当前切片值
  • NTILE不支持ROWS BETWEEN,

比如 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)

select name,orderdate,cost,
       ntile(3) over() as sample1 , --全局数据切片
       ntile(3) over(partition by name), -- 按照name进行分组,在分组内将数据切成3份
       ntile(3) over(order by cost),--全局按照cost升序排列,数据切成3份
       ntile(3) over(partition by name order by cost ) --按照name分组,在分组内按照cost升序排列,数据切成3份
from t_window ;
 
 

jackma 2018-01-01 10.0 3 1 1 1
jackma 2018-02-03 23.0 3 1 1 1
jackma 2018-04-06 42.0 2 2 2 2
jackma 2018-01-05 46.0 2 2 2 2
jackma 2018-01-08 55.0 2 3 2 3
martma 2018-04-08 62.0 2 1 2 1
martma 2018-04-09 68.0 1 2 3 1
martma 2018-04-11 75.0 1 3 3 2
martma 2018-04-13 94.0 1 1 3 3
neilma 2018-05-10 12.0 1 2 1 1
neilma 2018-06-12 80.0 1 1 3 2
tonyma 2018-01-02 15.0 3 2 1 1
tonyma 2018-01-04 29.0 3 3 1 2
tonyma 2018-01-07 50.0 2 1 2 3

最一列=1的就是消费最少的人的1/3 ,因为一共是有14条记录,所以之后分布不均的,分配大了第一部分。

row_number、rank、dense_rank

这三个窗口函数的使用场景非常多
- row_number()从1开始,按照顺序,生成分组内记录的序列,row_number()的值不会存在重复,当排序的值相同时,按照表中记录的顺序进行排列
- RANK() 生成数据项在分组中的排名,排名相等会在名次中留下空位
- DENSE_RANK() 生成数据项在分组中的排名,排名相等会在名次中不会留下空位

    select name,orderdate,cost,
    row_number() over(partition by name order by cost asc) ,
    rank() over(partition by name order by cost asc),
    dense_rank() over(partition by name order by cost asc)
    from t_window;
 
 

jackma 2018-01-01 10.0 1 1 1
jackma 2018-01-01 10.0 2 1 1
jackma 2018-02-03 23.0 3 3 2
jackma 2018-04-06 42.0 4 4 3
jackma 2018-01-05 46.0 5 5 4
jackma 2018-01-08 55.0 6 6 5
martma 2018-04-08 62.0 1 1 1
martma 2018-04-09 68.0 2 2 2
martma 2018-04-11 75.0 3 3 3
martma 2018-04-13 94.0 4 4 4
neilma 2018-05-10 12.0 1 1 1
neilma 2018-06-12 80.0 2 2 2
tonyma 2018-01-02 15.0 1 1 1
tonyma 2018-01-02 15.0 2 1 1
tonyma 2018-01-04 29.0 3 3 2
tonyma 2018-01-07 50.0 4 4 3

LAG和LEAD函数

可以查看当前行的上一行或者下一行的记录;

select name,orderdate,cost,
lag(orderdate,1,'1900-01-01') over(partition by name order by orderdate ) as time1,
lag(orderdate,2) over (partition by name order by orderdate) as time2
from t_window;
 
 

jackma 2018-01-01 10.0 1900-01-01 NULL
jackma 2018-01-01 10.0 2018-01-01 NULL
jackma 2018-01-05 46.0 2018-01-01 2018-01-01
jackma 2018-01-08 55.0 2018-01-05 2018-01-01
jackma 2018-02-03 23.0 2018-01-08 2018-01-05
jackma 2018-04-06 42.0 2018-02-03 2018-01-08
martma 2018-04-08 62.0 1900-01-01 NULL
martma 2018-04-09 68.0 2018-04-08 NULL
martma 2018-04-11 75.0 2018-04-09 2018-04-08
martma 2018-04-13 94.0 2018-04-11 2018-04-09
neilma 2018-05-10 12.0 1900-01-01 NULL
neilma 2018-06-12 80.0 2018-05-10 NULL
tonyma 2018-01-02 15.0 1900-01-01 NULL
tonyma 2018-01-02 15.0 2018-01-02 NULL
tonyma 2018-01-04 29.0 2018-01-02 2018-01-02
tonyma 2018-01-07 50.0 2018-01-04 2018-01-02

lag(orderdate,1,’1900-01-01’ ),参数的定义 orderdate 选定的字段,往上查找的行数,默认值(不给就是null)

lead函数就是向下取多少行,使用方式都是一致的。

first_value和last_value

first_value取分组内排序后,截止到当前行,第一个值
last_value取分组内排序后,截止到当前行,最后一个值

select name,orderdate,cost ,
first_value(cost) over(partition by name order by cost),--取分组内的第一个值
first_value(cost) over (partition by name order by cost desc) --取分组内最后垂岸的一个值(一定要排序不能按照自然顺序来去不然会报错)
from t_window;
 
 

要是取分组内的第一个值和最后一个值(都是采用first value来实现):
jackma 2018-01-08 55.0 10.0 55.0
jackma 2018-01-05 46.0 10.0 55.0
jackma 2018-04-06 42.0 10.0 55.0
jackma 2018-02-03 23.0 10.0 55.0
jackma 2018-01-01 10.0 10.0 55.0
jackma 2018-01-01 10.0 10.0 55.0
martma 2018-04-13 94.0 62.0 94.0
martma 2018-04-11 75.0 62.0 94.0
martma 2018-04-09 68.0 62.0 94.0
martma 2018-04-08 62.0 62.0 94.0
neilma 2018-06-12 80.0 12.0 80.0
neilma 2018-05-10 12.0 12.0 80.0
tonyma 2018-01-07 50.0 15.0 50.0
tonyma 2018-01-04 29.0 15.0 50.0
tonyma 2018-01-02 15.0 15.0 50.0
tonyma 2018-01-02 15.0 15.0 50.0

在使用窗口函数中的分析函数,比如row_number,last_value函数d的时候一定要试用order by,不然最后的结果可能和你想要的结果有出入。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值