目录
一、窗口函数的知识点
1.1 窗户函数的定义
1.2 窗户函数的语法
1.3 窗口函数分类
1.4 前后函数:lag/lead
二、实际案例
2.1 股票的波峰波谷
0 问题描述
1 数据准备
2 数据分析
3 小结
2.2 前后列转换(面试题)
0 问题描述
1 数据准备
2 数据分析
3 小结
窗口函数可以拆分为【窗口+函数】。窗口函数官网指路:LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual+WindowingAndAnalytics
<窗口函数>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 ,偷懒省略窗口子句会出问题~
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 ( partition by order by 窗口子句 )的执行顺序:over -> partition by -> order by -> 窗口子句 -> 函数
哪些函数可以是窗口函数呢?(放在over关键字前面的)
sum(column) over ();
count(column) over;
max(column) over ;
min(column) over;
avg(column) over;
row_number() : 顺序排序——1、2、3
rank() : 并列排序,跳过重复序号——1、1、3(横向加)
dense_rank() : 并列排序,不跳过重复序号——1、1、2(纵向加)
first_value/last_value:分组内排序后,截止到当前行第一个/最后一个值
-- 取得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值,不跳过
lead和lag函数,这两个函数一般用于计算差值,上面已介绍其语法。
-- 取得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
求股票的波峰Crest 和 波谷trough
波峰:当天的股票价格大于前一天和后一天
波谷:当天的股票价格小于前一天和后一天
create table if not exists table2
(
id int comment '股票id',
dt string comment '日期',
price int comment '价格'
)
comment '股票价格波动信息';
insert overwrite table table2 values
(1,'2019-01-01',10001),
(1,'2019-01-03',1001),
(1,'2019-01-02',1001),
(1,'2019-01-04',1000),
(1,'2019-01-05',1002),
(1,'2019-01-06',1003),
(1,'2019-01-07',1004),
(1,'2019-01-08',998),
(1,'2019-01-09',997),
(2,'2019-01-01',1002),
(2,'2019-01-02',1003),
(2,'2019-01-03',1004),
(2,'2019-01-04',998),
(2,'2019-01-05',999),
(2,'2019-01-06',997),
(2,'2019-01-07',996);
此题容易理解,利用lag()和lead()函数便可以解决。
select
id,
dt,
price,
case
when price > lag_price and price > lead_price then 'crest'
when price < lag_price and price < lead_price then 'trough'
end as price_type
from (
select
id,
dt,
price,
lag(price, 1) over (partition by id order by dt) as lag_price,
lead(price, 1) over (partition by id order by dt) as lead_price
from table2
) tmp1;
lead和lag函数一般用于计算当前行与上一行,或者当前行与下一行之间的差值。在用户间断登陆问题中也遇到过此函数。指路:HiveSQL题——用户连续登陆-CSDN博客文章浏览阅读220次,点赞4次,收藏3次。HiveSQL题——用户连续登陆https://blog.csdn.net/SHWAITME/article/details/135900251?spm=1001.2014.3001.5501
表temp包含A,B 两列,使用SQL对该B列进行处理,形成C列。按照A列顺序,B列值不变,C列累计技术 B列值变化,则C列重新开始计数,如图所示
with table4 as (
select 2010 as A,1 as B
union all
select 2011 as A,1 as B
union all
select 2012 as A,1 as B
union all
select 2013 as A,0 as B
union all
select 2014 as A,0 as B
union all
select 2015 as A,1 as B
union all
select 2016 as A,1 as B
union all
select 2017 as A,1 as B
union all
select 2018 as A,0 as B
union all
select 2019 as A,0 as B
)
with table4 as (
select 2010 as A,1 as B
union all
select 2011 as A,1 as B
union all
select 2012 as A,1 as B
union all
select 2013 as A,0 as B
union all
select 2014 as A,0 as B
union all
select 2015 as A,1 as B
union all
select 2016 as A,1 as B
union all
select 2017 as A,1 as B
union all
select 2018 as A,0 as B
union all
select 2019 as A,0 as B
)
select
A,
B,
row_number() over (partition by T order by A) as C
from (
select
A,
B,
--over (order by A) 本质是 :over(order by rows between unbounded preceding and current row )
--省略的是:上无边界到当前行
sum(change) over (order by A) T
from (
select
A,
B,
-- 向上取一行,取不到的记为0
lag(B, 1, 0) over (order by A) as Lag,
case
when B <> lag(B, 1, 0) over (order by A) then 1
else 0
end as change
from table4
) tmp1
) tmp2;
lead /lag函数常用于差值计算。