Hive窗口函数详解—及3套案例练习

可爱的猫

目录:
一、hive窗口函数语法
----over()窗口函数的语法结构
----常与over()一起使用的分析函数
二、hive窗口函数练习28道题

视频录制好了,建议大家边看视频边阅读文章,文章中的窗口函数练习题也有相应的视频。点击蓝色字体可跳转到视频。

前言:我们在学习hive窗口函数的时候,一定要先了解窗口函数的结构。而不是直接百度sum() over()、row_number() over()、或者count() over()的用法,如果这样做,永远也掌握不到窗口函数的核心,当然我刚开始的时候也是这样做的,包括去年自己在接触ORACLE分析函数时也是这样搜索。

还好我比较顽强,在HIVE窗口函数问题上折腾了半个月、看了很多文章后才知道over()才是窗口函数,而sum、row_number、count只是与over()搭配的分析函数,当然除了这三个函数还有其他的函数。

一、hive窗口函数语法

在前言中我们已经说了avg()、sum()、max()、min()是分析函数,而over()才是窗口函数,下面我们来看看over()窗口函数的语法结构、及常与over()一起使用的分析函数

1、over()窗口函数的语法结构
2、常与over()一起使用的分析函数
3、窗口函数总结

1、over()窗口函数的语法结构

分析函数 over(partition by 列名 order by 列名 rows between 开始位置 and 结束位置)

over()窗口函数由三部分组成:包括分区partition by 列名排序order by 列名从分区中选择指定的多条记录rows between 开始位置 and 结束位置【也叫窗口帧】。
我们在使用over()窗口函数时,上面三个函数可组合使用也可以不使用。

over()函数中如果不使用这三个函数,窗口大小是针对查询产生的所有数据,如果指定了分区,窗口大小是针对每个分区的数据。

1.1、over()函数中的三个函数讲解

1)partition by分区:
partition by可理解为group by 分组。over(partition by 列名)搭配分析函数时,分析函数按照每一组每一组的数据进行计算的。

2)order by排序:
order by是排序的意思,针对该窗口

3)窗口帧
窗口帧用于从分区中选择指定的多条记录,也就是对分区中的数据范围进行限定,供分析函数处理。Hive 提供了两种定义窗口帧的形式:ROWS 和 RANGE。两种类型都需要配置上界和下界。

例如:rows between 开始位置 and 结束位置
比如第一行到当前行ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(表示从起点到当前行),常用该窗口来计算累加。

PRECEDING:往前
FOLLOWING:往后
CURRENT ROW:当前行
UNBOUNDED:起点(一般结合PRECEDING,FOLLOWING使用)
UNBOUNDED PRECEDING 表示该窗口最前面的行(起点)
UNBOUNDED FOLLOWING:表示该窗口最后面的行(终点)
比如说:
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(表示从起点到当前行)
ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING(表示往前2行到往后1行)
ROWS BETWEEN 2 PRECEDING AND 1 CURRENT ROW(表示往前2行到当前行)
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING(表示当前行到终点)

因为rows between工作中使用比较少,比较难理解,这里举几个案例。

案例1:求当前日期销售额和后面所有日期的销售额总和、以及当前日期销售额和前面所有日期的销售额总和

SELECT  
  sid,  
  day_time,  --时间
  sales_volume,   --销售额
--1)当前日期销售额和后面所有日期的销售额总和
-- unbounded following 最后一行
  sum(sales_volume) over(rows between current row and unbounded following) sum_sales ,

--2)求当前日期销售额和前面所有日期的销售额总和
-- unbounded preceding就是第一行数据,current row当前行
-- 这个是累计求和,好像通过 sum()over(partition by 字段 order by 字段)也可以实现,验证后补充
sum(sales_volume) over(rows between unbounded preceding and current row) sum_sales2  
FROM 
dw_sale_data; 

rows between current row and unbounded following
这里面current row就是指当前行,unbounded following就是指最后一行,unbounded preceding就是第一行数据。
and前面和后面的关系就是范围,从and后面的到and前面,但是and前面是不可以使用unbounded following的,这样使用是错误的。

案例2:求当前日期和后面两天销售额的总和,以及当前日期和前两天销售额的总和

SELECT  
sid,   --订单id
day_time,   --时间
sales_volume,   --销售额
-- 1)求当前日期和后面两天销售额的总和,
-- current row是指当前行,2 following就是后两行数据
sum(sales_volume) over(rows between current row and 2 following) sum_sales ,

-- 2)求当前日期和前两天销售额的总和
-- 1 preceding 就是指上一行数据,current row就是指当前行数据
sum(sales_volume) over(rows between 1 preceding and current row) sum_sales2 ,
FROM dw_sale_data; 

2、常与over()一起使用的分析函数:

2.1、聚合类

avg()、sum()、max()、min()

2.2、排名类

row_number()按照值排序时产生一个自增编号,不会重复(如:1、2、3、4、5、6)
rank() 按照值排序时产生一个自增编号,值相等时会重复,会产生空位(如:1、2、3、3、3、6)
dense_rank() 按照值排序时产生一个自增编号,值相等时会重复,不会产生空位(如:1、2、3、3、3、4)

2.3、其他类

** lag() 、lead() 、ntile(n) 、first_value() 、last_value()**
1) lag()
LAG(col,n,DEFAULT) 用于统计窗口内往上第n行值
第一个参数为列名,第二个参数为往上第n行(可选,默认为1),第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL)

lag(列名,往前第几行的数据,[行数为null时的默认值,不指定为null]),可以计算用户上次购买时间,或者用户下次购买时间。

2)lead()
与LAG相反,LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值

第一个参数为列名,第二个参数为往下第n行(可选,默认为1),第三个参数为默认值(当往下第n行为NULL时候,取默认值,如不指定,则为NULL)

lead(列名,往后第几行的数据,[行数为null时的默认值,不指定为null])

3)ntile(n):
NTILE(n),用于将分组数据按照顺序切分成n片,返回当前切片值

把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,ntile返回此行所属的组的编号.
NTILE不支持ROWS BETWEEN,比如 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)

SELECT   
cookieid,  
createtime,  
pv,  
NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn1,    --分组内将数据分成2片  
NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn2,  --分组内将数据分成3片  
NTILE(4) OVER(ORDER BY createtime) AS rn3        --将所有数据分成4片  
FROM dw_window_data   
ORDER BY cookieid,createtime;  

4)first_value()
取分组内排序后,截止到当前行,第一个值

SELECT cookieid,  
createtime,  
url,  
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,  
FIRST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS first1   
FROM dw_window_data_02;

5) last_value()
取分组内排序后,截止到当前行,最后一个值

SELECT cookieid,  
createtime,  
url,  
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,  
LAST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS last1   
FROM dw_window_data_02;

3、窗口函数总结:

其实窗口函数逻辑比较绕,我们可以把窗口理解为对表中的数据进行分组,排序等计算。要真正的理解HIVE窗口函数,还是要结合练习题才行。下面我们开始HIVE窗口函数的练习吧!

二、hive窗口函数练习28道题

第一套练习:hive之简单窗口函数 over()

1、使用 over() 函数进行数据统计, 统计每个用户及表中数据的总数
2、求用户明细并统计每天的用户总数
3、计算从第一天到现在的所有 score 大于80分的用户总数
4、计算每个用户到当前日期分数大于80的天数

测试数据

20191020,11111,85
20191020,22222,83
20191020,33333,86
20191021,11111,87
20191021,22222,65
20191021,33333,98
20191022,11111,67
20191022,22222,34
20191022,33333,88
20191023,11111,99
20191023,22222,33

建表并导入数据:

create table test_window
(logday string,    #logday时间
userid string, 
score int)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

#加载数据
load data local inpath '/home/xiaowangzi/hive_test_data/test_window.txt' into table test_window;

我们先看下表中的数据:

select  *  from test_window;
test_window
1、使用 over() 函数进行数据统计, 统计每个用户及表中数据的总数
select *, count(userid)over() as total  from  test_window;

这里使用 over() 与 select count(*) 有相同的作用,好处就是,在需要计算总数时不用再进行一次关联。

2、求用户明细并统计每天的用户总数

可以使用 partition by 按日期列对数据进行分区处理,如:over(partition by logday)

select  *,count()over(partition by logday)as day_total from  test_window;

求每天的用户数可以使用select logday, count(userid) from recommend.test_window group by logday,但是当想要得到 userid 信息时,这种用法的优势就很明显。

3、计算从第一天到现在的所有 score 大于80分的用户总数

此时简单的分区不能满足需求,需要将 order by 和 窗口定义结合使用。

select  *,count()over(order by logday rows between unbounded preceding and current row)as total 
from  test_window
where score > 80;


通过 over() 计算出按日期的累加值。
其实自己刚开始的时候就计算我思路是错了,我就想的是不用累加,直接select *,count()over()as total from test_window where score > 80;这样计算,如果这样计算的话只会显示表中所有大于80的人数,如果我想看20191021或者看20191022的人数看不见。

4、计算每个用户到当前日期分数大于80的天数
select *,
count()over(partition  by userid order by logday rows between unbounded preceding and current row) as total
from test_window
where score > 80  order by logday, userid;

第二套练习

1、查询在2017年4月份购买过的顾客及总人数
2、查询顾客的购买明细及月购买总额
3、查询顾客的购买明细及到目前为止每个顾客购买总金额
4、查询顾客上次的购买时间----lag()over()偏移量分析函数的运用
5、查询前20%时间的订单信息

测试数据

jack,2017-01-01,10
tony,2017-01-02,15
jack,2017-02-03,23
tony,2017-01-04,29
jack,2017-01-05,46
jack,2017-04-06,42
tony,2017-01-07,50
jack,2017-01-08,55
mart,2017-04-08,62
mart,2017-04-09,68
neil,2017-05-10,12
mart,2017-04-11,75
neil,2017-06-12,80
mart,2017-04-13,94

建表并加载数据

create table business
(
name string, 
orderdate string,
cost int
)ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

#加载数据
load  data local inpath "/home/xiaowangzi/hive_test_data/business.txt" into table business;

查看表数据

select   *  from  business;
查看表数据

1、查询在2017年4月份购买过的顾客及总人数

分析:按照日期过滤、分组count求总人数(分组为什么不是用group by?自己思考)

select   *,count()over() as total  from  business 
where substr(orderdate,1,7) = '2017-04';

2、查询顾客的购买明细及月购买总额

是计算每月每个用户的消费金额

select 
*,
sum(cost) over(partition by name,substr(orderdate,1,7)) total_amount
from 
business;

3、查询顾客的购买明细及到目前为止每个顾客购买总金额

分析:按照顾客分组、日期升序排序、组内每条数据将之前的金额累加

select 
*,
sum(cost) over(partition by name order  by  orderdate 
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) total_amount
from 
business;

4、查询顾客上次的购买时间----lag()over()偏移量分析函数的运用

lag()over()偏移量分析函数的运用

select 
name,
orderdate,
cost,
lag(orderdate,1) over(partition by name order by orderdate) last_date
from 
business;
image.png

5、查询前20%时间的订单信息

select  *  
from 
(select   *,
ntile(5)over(order  by  cost)sortgroup_num from  business)t 
where t.sortgroup_num = 1;

第三套练习:

1、每门学科学生成绩排名(是否并列排名、空位排名三种实现)
2、每门学科成绩排名top n的学生

原始数据(学生成绩信息)

name    subject score
孙悟空 语文  87
孙悟空 数学  95
孙悟空 英语  68
大海  语文  94
大海  数学  56
大海  英语  84
宋宋  语文  64
宋宋  数学  86
宋宋  英语  84
婷婷  语文  65
婷婷  数学  85
婷婷  英语  78

建表并加载数据

create table score
(
name string,
subject string, 
score int
) row format delimited fields terminated by "\t";

#加载数据
load data local inpath '/home/xiaowangzi/hive_test_data/score.txt' into table score;

查看数据

select  *  from  score;

1、每门学科学生成绩排名(是否并列排名、空位排名三种实现)

select  *,
row_number()over(partition by subject order by score desc),
rank()over(partition by subject order by score desc),
dense_rank()over(partition by subject order by score desc)
from score

2、每门学科成绩排名top n的学生

select 
*
from 
(
select 
*,
row_number() over(partition by subject order by score desc) rmp
from score
) t
where t.rmp<=3;

参考文章1:Hive窗口函数案例详解
参考文章2:hive表之简单窗口函数 over()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容