Hive窗口函数小结
文章目录
- 一.窗口函数作用
- 1.1.测试数据
- 1.2.应用场景
- 二.窗口函数概念
- 2.1.语法结构
- 2.2.分析函数
- 2.3.over函数的参数
- 三.窗口函数入门
- 3.1.over(partition by)
- 3.2.over(order by)
- 3.3.lag函数求上次购买时间
- 3.4.ntile函数查前20%订单
- 3.5.lead统计下次购买时间
- 3.6.fist_value和last_value
- 3.7.rank排名函数
- 四.窗口函数进阶
- 4.1.测试数据
- 4.2.需求1描述和实现
- 4.3.需求2描述和实现
- 五.小结
- 5.1.全文小结
一.窗口函数作用
1.1.测试数据
首先我们看下面一组数据: business表中有一组消费流水记录。
1.2.应用场景
需求:求在2017年4月份购买过的顾客及总人数 从上面数据可以看出,4月份mart和jack购买过,所以总人数是2人。
此时通过传统的group by显然无法实现:
select name ,count(*) as sum_people
from business
where substring(orderdate,1,7) = '2017-04'
group by name;
或者需要用下面这样一个复杂的子查询才能完成:
select t_name.name,t_count.count from
(
select name
from business
where substring(orderdate,1,7) = '2017-04'
group by name
) as t_name
,
(
select count(*) as count from
(
select name
from business
where substring(orderdate,1,7) = '2017-04'
group by name
)as t3
)as t_count
- 用窗口函数实现上述需求sql代码:
select name ,count(*) over()
from business
where SUBSTRING(orderdate,1,7) = '2017-04'
group by name;
这里的over()函数就是窗口函数,这里没有传其他参数,默认窗口范围是所有数据。可以理解为,整个表先按照group by 聚合,然后where条件再限定,得到的结果为:
jack
mart
然后计算count()的作用范围就是over()函数指定的范围,这里范围就是上面结果的全部数据,所以count()得到2.
下面将详细介绍窗口函数,并通过蚂蚁金服的hive面试题进行深入理解。
二.窗口函数概念
2.1.语法结构
[分析函数] over(partition by 列名 order by 列名 rows between 开始位置 and 结束位置))
窗口函数over一般紧跟在分析函数后面,over中的partition by 等用于确定窗口的范围,下面将详细介绍。
2.2.分析函数
分析函数的特点是多进一出,常见的有下面一些。
聚合类
avg() -- 求平均 sum() -- 求和 max() -- 最大值 min() -- 最小值
排名类
row_number()--按照值排序时产生一个自增编号,不会重复`(如:1、2、3、4、5、6)` rank() --按照值排序时产生一个自增编号,值相等时会重复,会产生空位`(如:1、2、3、3、3、6)` dense_rank() --按照值排序时产生一个自增编号,值相等时会重复,不会产生空位`(如:1、2、3、3、3、4)`
其他类
first_value(列名) -- 第一个值 last_value(列名) -- 第二个值 lag(列名,往前的行数,[行数为null时的默认值,不指定为null])-- 用于统计窗口内往上第n行值,可以计算用户上次购买时间,或者用户下次购买时间。 lead(列名,往后的行数,[行数为null时的默认值,不指定为null]) --与LAG相反LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值 ntile(n) -- 把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,ntile返回此行所属的组的编号
2.3.over函数的参数
over()函数中包括三个函数:包括
分区
partition by 列名
、排序
order by 列名
、指定窗口范围
rows between 开始位置 and 结束位置
。
范围参数说明:
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(表示当前行到终点)
我们在使用over()窗口函数时,over()函数中的这三个函数可组合使用也可以不使用。partition by
也可以用distribute by
代替
over()函数中如果不使用这三个函数,窗口大小是针对查询产生的所有数据,如果指定了分区,窗口大小是针对每个分区的数据。
其中partition by 类似于group by,这样窗口大小就是每个分组大小。
order by 这个比较难理解,采用了order by,其效果和 rows between UNBOUNDED PRECEDING AND CURRENT ROW 一样,窗口大小都是从起点到当前行。如果同时指定了分区,该规则限定在分区内。
三.窗口函数入门
3.1.over(partition by)
需求:查询顾客的购买明细及购买总额
因为是要求购买明细,所以每条记录都需要返回,显然group by不能满足,所以用窗口函数就非常合适。
每条记录最后一列就是对应用户的购买总金额。
3.2.over(order by)
需求:查询顾客购买明细,以及截止前日期的购买综合
因为购买是有日期顺序,所以是一个累加过程,窗口是移动的,每条数据的最后一列计算的是当前日期之前的购买总和,所以可以看出order by的窗口范围
是当前行之前的所有数据。而且这里用partition by 限定了order by的范围,这是最常用的用法。
这个需求也可以用下面的语句实现,结果一样:
select
bs.name,bs.orderdate,bs.cost,sum(bs.cost) over(partition by bs.name rows between UNBOUNDED PRECEDING AND CURRENT ROW)
from business bs;
rows between UNBOUNDED PRECEDING AND CURRENT ROW
表示从起点开始到当前行,和order by是一样的。当然,范围参数更加灵活,参加上面范围参数。
3.3.lag函数求上次购买时间
需求:查询顾客明细和上次购买时间
lag用于统计窗口内往上第n行值
3.4.ntile函数查前20%订单
需求:查询前日期排名前20%的明细
这里ntile将数据分为5个组,图中只执行了选中的部分,可以看出结果会落在五个组,用字段sorted标识,然后执行完整sql过滤出sorted=1的就可以计算20%的订单,当然了如果数量不是5的倍数,结果只是约等于。
3.5.lead统计下次购买时间
需求:查询用户购买明细和下次购买时间。
强行为了使用该函数,场景并不典型,凑合看吧。这里当没有下一次时,用“最后一次” 填充
与LAG相反LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值
3.6.fist_value和last_value
需求:查询每个用户购买明细和首次购买花费的金额。last_value正好相反取窗口内最后一个值。
3.7.rank排名函数
create table score(name string,subject string,score int
)
row format delimited fields terminated by '\t'
location '/user/hive/warehouse/test.db/score';load data local inpath '/opt/soft/hiveData/score.txt' into table score;
use db_test;
show tables;
select * from score;select
sc.name,sc.subject,sc.score,
RANK() over(partition by sc.subject order by sc.score desc) as rk,
DENSE_RANK () over(partition by sc.subject order by sc.score desc) as d_rk,
row_number() over(partition by sc.subject order by sc.score desc) as rnfrom score sc;show functions;
desc function get_json_object;
四.窗口函数进阶
这里用蚂蚁金服的一道hive面试题作为小结。
4.1.测试数据
这里涉及两张表:
表1:植物换购表。
create table if not exists plant_carbon(plant_id string comment '植物编号',plant_name string comment '植物名称',low_carbon int comment '换购所需碳量'
)comment '植物换购表'
row format delimited fields terminated by '\t';
表2:用户减碳量领取表 下面数据截图显示了一部分。需要原始数据的记得留言。
create table if not exists user_low_carbon(user_id string comment '用户id',data_dt string comment '日期',low_carbon int comment '减少碳排放量(g)'
) comment '碳领取流水表'
row format delimited fields terminated by '\t';
4.2.需求1描述和实现
问题:假设2017年1月1日开始记录低碳数据(user_low_carbon),假设2017年10月1日之前满足申领条件的用户都申领了一颗p004-胡杨,
剩余的能量全部用来领取“p001-梭梭树” 。
统计在10月1日累计申领“p001-梭梭树” 排名前10的用户信息;以及他比后一名多领了几颗梭梭树。
得到的统计结果如下表样式:
user_id plant_count less_count(比后一名多领了几颗梭梭树)
u_101 1000 100
u_088 900 400
u_103 500 …
解读:这里如果能量够买胡杨,会先买一棵胡杨,所以计算梭梭树时需要减掉。
实现效果:
sql:
--10月1各个用户累计碳量
select
user_id,
sum(low_carbon)
from user_low_carbon
group by user_id;--10月1号累计总量>=215的用户会领一棵胡杨。所以可以减去胡杨的消耗,看剩余的购买梭梭树(需要17能量)的能量有多少。
select t1.user_id,t1.total-t2.low_carbon
from
(
select
user_id,
sum(low_carbon) as total
from user_low_carbon
group by user_id
)as t1,plant_carbon t2 where t2.plant_name='胡杨';--在上一步基础上,计算能购买的梭梭树数量,并进行排名并利用窗口函数进行计算差值。
select
t5.user_id,
t5.count_ss as plant_count,
max(t5.count_ss)-min(t5.count_ss) OVER(order by t5.count_ss desc ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) as less_count
from
(
select t3.user_id,FLOOR(t3.total_ss/t4.low_carbon) as count_ss
from
(
select t1.user_id,t1.total-t2.low_carbon as total_ss
from
(
select
user_id,
sum(low_carbon) as total
from user_low_carbon
group by user_id
)as t1,plant_carbon t2 where t2.plant_name='胡杨'
)as t3,plant_carbon t4 where t4.plant_name='梭梭树'
)as t5
group by t5.user_id,t5.count_ss;
4.3.需求2描述和实现
问题:查询user_low_carbon表中每日流水记录,条件为:
用户在2017年,连续三天(或以上)的天数里,
每天减少碳排放(low_carbon)都超过100g的用户低碳流水。
需要查询返回满足以上条件的user_low_carbon表中的记录流水。
例如用户u_002符合条件的记录如下,因为2017/1/2~2017/1/5连续四天的碳排放量之和都大于等于100g:
seq(key) user_id data_dt low_carbon
xxxxx10 u_002 2017/1/2 150
xxxxx11 u_002 2017/1/2 70
xxxxx12 u_002 2017/1/3 30
xxxxx13 u_002 2017/1/3 80
xxxxx14 u_002 2017/1/4 150
xxxxx14 u_002 2017/1/5 101
实现效果:这里就是分步骤逐渐找到最终答案,分解问题。
sql:
--第一步:先合并同一天收集多次的记录,然后找到2017每天碳值>=100的记录,并按日期排名select
user_id,
data_dt,
sum(low_carbon) as low_carbon ,
rank() over(partition by user_id order by data_dt) as rk
from user_low_carbon
where low_carbon >=100 and data_dt BETWEEN '2017/1/1' and '2027/12/31'
group by user_id ,data_dt;--第二步:在上面基础,计算日期和排名的差值 diffselect
t1.user_id,
t1.data_dt,
t1.low_carbon,
t1.rk,
date_sub(concat_ws('-',split(t1.data_dt,'/')),t1.rk) as diff
from
(
select
user_id,
data_dt,
sum(low_carbon) as low_carbon,
rank() over(partition by user_id order by data_dt) as rk
from user_low_carbon
where low_carbon >=100 and data_dt BETWEEN '2017/1/1' and '2027/12/31'
group by user_id ,data_dt
) as t1--第三步:上面基础上,按user_id和diff分组:则分组内就是连续的数据集。并过滤出连续>=3日的数据。select
t2.user_id,
t2.diff
from
(
select
t1.user_id,
t1.data_dt,
t1.low_carbon,
t1.rk,
date_sub(concat_ws('-',split(t1.data_dt,'/')),t1.rk) as diff
from
(
select
user_id,
data_dt,
sum(low_carbon) as low_carbon,
rank() over(partition by user_id order by data_dt) as rk
from user_low_carbon
where low_carbon >=100 and data_dt BETWEEN '2017/1/1' and '2027/12/31'
group by user_id ,data_dt
) as t1
) as t2
group by t2.user_id,t2.diff
having count(*)>=3;--第四步:用二、三两步结构进行joinselect
t_left.user_id,
t_left.data_dt,
t_left.low_carbon
from
(
select
t1.user_id,
t1.data_dt,
t1.low_carbon,
t1.rk,
date_sub(concat_ws('-',split(t1.data_dt,'/')),t1.rk) as diff
from
(
select
user_id,
data_dt,
sum(low_carbon) as low_carbon,
rank() over(partition by user_id order by data_dt) as rk
from user_low_carbon
where low_carbon >=100 and data_dt BETWEEN '2017/1/1' and '2027/12/31'
group by user_id ,data_dt
) as t1
) as t_left
join
(
select
t2.user_id,
t2.diff
from
(
select
t1.user_id,
t1.data_dt,
t1.low_carbon,
t1.rk,
date_sub(concat_ws('-',split(t1.data_dt,'/')),t1.rk) as diff
from
(
select
user_id,
data_dt,
sum(low_carbon) as low_carbon,
rank() over(partition by user_id order by data_dt) as rk
from user_low_carbon
where low_carbon >=100 and data_dt BETWEEN '2017/1/1' and '2027/12/31'
group by user_id ,data_dt
) as t1
) as t2
group by t2.user_id,t2.diff
having count(*)>=3
)as t_right
on t_left.user_id=t_right.user_id and t_left.diff=t_right.diff;
五.小结
5.1.全文小结
hive的函数使用关键就是实践,动手写必不可少。上述几个需求,其实对应了具体的业务场景,非常实用。
Hive窗口函数小结相关推荐
- 大数据技术-hive窗口函数详解
有不少同学一听这个标题,hive窗口函数是什么鬼?没听说过还有窗口函数这个东西啊,其实它的用处可大了,下面听小千慢慢道来. hive窗口函数 窗口函数指定了函数工作的数据窗口大小(当前行的上下多少行) ...
- HiveQL学习笔记(四):Hive窗口函数
本系列是本人对Hive的学习进行一个整理,主要包括以下内容: 1.HiveQL学习笔记(一):Hive安装及Hadoop,Hive原理简介 2.HiveQL学习笔记(二):Hive基础语法与常用函数 ...
- hive窗口函数使用
hive窗口函数的使用 前言 一.hive窗口函数语法 1.over()窗口函数的语法结构 1.1.over()函数中的三个函数讲解 2.常与over()一起使用的分析函数 2.1.聚合类 2.2.排 ...
- Hive 窗口函数 实现原理
Hive 窗口函数 实现原理 hive中窗口函数的实现,主要是借助于一个叫做 Windowing Table Function 的Partitioned Table Function Partitio ...
- hive 窗口函数(持续更新)
hive窗口函数语法 avg().sum().max().min()等是分析函数,而over()才是窗口函数,下面我们来看看over()窗口函数的语法结构.及常与over()一起使用的分析函数: 1. ...
- HIVE 窗口函数和分析函数
**HIVE 窗口函数和分析函数** 第一篇,试试水: 一.介绍 分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是:对于每个组返回多行,而聚合函数对于每个组只返回一行. 开窗函数指定了分析 ...
- Hive窗口函数之累积值、平均值、首尾值的计算学习
Hive窗口函数可以计算一定范围内.一定值域内.或者一段时间内的累积和以及移动平均值等:可以结合聚集函数SUM() .AVG()等使用:可以结合FIRST_VALUE() 和LAST_VALUE(), ...
- hive 窗口函数_Datatist科技专栏 | Hive排序窗口函数速学教程!
作者:原上野 设计:Cindy 编辑:AI君 在开发过程中经常会遇见排序的场景,比如取top N的问题,这时候row_number(),rank,dense_ran()这三个函数就派上用场了,其中,r ...
- hive窗口函数_Hive sql窗口函数源码分析
在了解了窗口函数实现原理 spark.hive中窗口函数实现原理复盘 和 sparksql比hivesql优化的点(窗口函数)之后,今天又撸了一遍hive sql 中窗口函数的源码实现,写个笔记记录一 ...
最新文章
- 使用MySQL的LAST_INSERT_ID--转
- spring中基于Java容器配置注解的区别及使用场景
- 微信小程序---实现输入手机验证码功能
- 利用python进行数据分析之准备工作(1)
- 数据库设计中常见表结构的设计技巧(转)
- 4G5G学习过程中整理的专业名词的符号简称
- php值班系统,php简单的值日值班处理方法
- STM32之485通信
- CameraLink简介
- fudanNLP keyword Extraction
- 【JavaSE8 高级编程 多线程】多线程入门级解析 2019_7_27
- MySQL的 初步认识 - 细节狂魔
- Android ADB常用指令
- Sketch 插件篇(1)——Sketch Measure
- 见过贪婪的,没见过这么贪婪的
- 淘宝、天猫、1688图片识别API接口。
- ansible———playbook剧本
- QIIME2-傻瓜式安装
- SQL Server-------数据库恢复技术
- 前端读取mysql数据库_Servlet读取MySQL数据库并在前端调用
热门文章
- Open3D-GUI系列教程(三)界面布局
- Guitar Pro2023Win/Mac中文吉他/贝斯打谱识谱软件
- 第二季4:初始化MPP系统(step12)
- 【DL】时间序列的深度学习
- java读取注册表_Java通过CMD方式读取注册表任意键值对代码实践
- 高校图书馆管理系统 php 漏洞,某通用图书馆管理系统SQL注入_MySQL
- auto.exe病毒的快速解决办法
- php 对全角字符的过滤,php在做敏感词过滤时怎么解决用特殊符号分割、简繁体、半角全角,来绕开过滤的问题?...
- Sql Server 2008 R2 完全卸载步骤
- java如何读取.properties配置文件