目錄:
一、hive窗口函數(shù)語(yǔ)法
----over()窗口函數(shù)的語(yǔ)法結(jié)構(gòu)
----常與over()一起使用的分析函數(shù)
二疫剃、hive窗口函數(shù)練習(xí)28道題
視頻錄制好了,建議大家邊看視頻邊閱讀文章硼讽,文章中的窗口函數(shù)練習(xí)題也有相應(yīng)的視頻巢价。點(diǎn)擊藍(lán)色字體可跳轉(zhuǎn)到視頻。
前言:我們?cè)趯W(xué)習(xí)hive窗口函數(shù)的時(shí)候理郑,一定要先了解窗口函數(shù)的結(jié)構(gòu)蹄溉。而不是直接百度sum() over()咨油、row_number() over()您炉、
或者count() over()
的用法,如果這樣做役电,永遠(yuǎn)也掌握不到窗口函數(shù)的核心赚爵,當(dāng)然我剛開(kāi)始的時(shí)候也是這樣做的,包括去年自己在接觸ORACLE分析函數(shù)時(shí)也是這樣搜索法瑟。
還好我比較頑強(qiáng)冀膝,在HIVE窗口函數(shù)問(wèn)題上折騰了半個(gè)月、看了很多文章后才知道over()才是窗口函數(shù)霎挟,而sum窝剖、row_number、count
只是與over()
搭配的分析函數(shù)酥夭,當(dāng)然除了這三個(gè)函數(shù)還有其他的函數(shù)赐纱。
一、hive窗口函數(shù)語(yǔ)法
在前言中我們已經(jīng)說(shuō)了avg()熬北、sum()疙描、max()、min()是分析函數(shù)讶隐,而over()才是窗口函數(shù)起胰,下面我們來(lái)看看over()窗口函數(shù)的語(yǔ)法結(jié)構(gòu)、及常與over()一起使用的分析函數(shù)
1巫延、over()窗口函數(shù)的語(yǔ)法結(jié)構(gòu)
2效五、常與over()一起使用的分析函數(shù)
3、窗口函數(shù)總結(jié)
1炉峰、over()窗口函數(shù)的語(yǔ)法結(jié)構(gòu)
分析函數(shù) over(partition by 列名 order by 列名 rows between 開(kāi)始位置 and 結(jié)束位置)
over()窗口函數(shù)由三部分組成:包括分區(qū)partition by 列名
火俄、排序order by 列名
、從分區(qū)中選擇指定的多條記錄rows between 開(kāi)始位置 and 結(jié)束位置
【也叫窗口幀】讲冠。
我們?cè)谑褂胦ver()窗口函數(shù)時(shí)瓜客,上面三個(gè)函數(shù)可組合使用也可以不使用。
over()函數(shù)中如果不使用這三個(gè)函數(shù),窗口大小是針對(duì)查詢(xún)產(chǎn)生的所有數(shù)據(jù)谱仪,如果指定了分區(qū)玻熙,窗口大小是針對(duì)每個(gè)分區(qū)的數(shù)據(jù)。
1.1疯攒、over()函數(shù)中的三個(gè)函數(shù)講解
1)partition by分區(qū):
partition by
可理解為group by 分組嗦随。over(partition by 列名)
搭配分析函數(shù)時(shí),分析函數(shù)按照每一組每一組的數(shù)據(jù)進(jìn)行計(jì)算的敬尺。
2)order by排序:
order by是排序的意思枚尼,針對(duì)該窗口
3)窗口幀
窗口幀用于從分區(qū)中選擇指定的多條記錄,也就是對(duì)分區(qū)中的數(shù)據(jù)范圍進(jìn)行限定砂吞,供分析函數(shù)處理署恍。Hive 提供了兩種定義窗口幀的形式:ROWS 和 RANGE。兩種類(lèi)型都需要配置上界和下界蜻直。
例如:rows between 開(kāi)始位置 and 結(jié)束位置
比如第一行到當(dāng)前行ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(表示從起點(diǎn)到當(dāng)前行)
盯质,常用該窗口來(lái)計(jì)算累加。
PRECEDING:往前
FOLLOWING:往后
CURRENT ROW:當(dāng)前行
UNBOUNDED:起點(diǎn)(一般結(jié)合PRECEDING概而,F(xiàn)OLLOWING使用)
UNBOUNDED PRECEDING 表示該窗口最前面的行(起點(diǎn))
UNBOUNDED FOLLOWING:表示該窗口最后面的行(終點(diǎn))
比如說(shuō):
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(表示從起點(diǎn)到當(dāng)前行)
ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING(表示往前2行到往后1行)
ROWS BETWEEN 2 PRECEDING AND 1 CURRENT ROW(表示往前2行到當(dāng)前行)
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING(表示當(dāng)前行到終點(diǎn))
因?yàn)閞ows between工作中使用比較少呼巷,比較難理解,這里舉幾個(gè)案例赎瑰。
案例1:求當(dāng)前日期銷(xiāo)售額和后面所有日期的銷(xiāo)售額總和王悍、以及當(dāng)前日期銷(xiāo)售額和前面所有日期的銷(xiāo)售額總和
SELECT
sid,
day_time, --時(shí)間
sales_volume, --銷(xiāo)售額
--1)當(dāng)前日期銷(xiāo)售額和后面所有日期的銷(xiāo)售額總和
-- unbounded following 最后一行
sum(sales_volume) over(rows between current row and unbounded following) sum_sales ,
--2)求當(dāng)前日期銷(xiāo)售額和前面所有日期的銷(xiāo)售額總和
-- unbounded preceding就是第一行數(shù)據(jù),current row當(dāng)前行
-- 這個(gè)是累計(jì)求和餐曼,好像通過(guò) sum()over(partition by 字段 order by 字段)也可以實(shí)現(xiàn)压储,驗(yàn)證后補(bǔ)充
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
就是指當(dāng)前行,unbounded following
就是指最后一行,unbounded preceding就是第一行數(shù)據(jù)。
and前面和后面的關(guān)系就是范圍,從and后面的到and前面,但是and前面是不可以使用unbounded following的,這樣使用是錯(cuò)誤的晋辆。
案例2:求當(dāng)前日期和后面兩天銷(xiāo)售額的總和渠脉,以及當(dāng)前日期和前兩天銷(xiāo)售額的總和
SELECT
sid, --訂單id
day_time, --時(shí)間
sales_volume, --銷(xiāo)售額
-- 1)求當(dāng)前日期和后面兩天銷(xiāo)售額的總和,
-- current row是指當(dāng)前行,2 following就是后兩行數(shù)據(jù)
sum(sales_volume) over(rows between current row and 2 following) sum_sales ,
-- 2)求當(dāng)前日期和前兩天銷(xiāo)售額的總和
-- 1 preceding 就是指上一行數(shù)據(jù),current row就是指當(dāng)前行數(shù)據(jù)
sum(sales_volume) over(rows between 1 preceding and current row) sum_sales2 瓶佳,
FROM dw_sale_data;
2芋膘、常與over()一起使用的分析函數(shù):
2.1、聚合類(lèi)
avg()霸饲、sum()为朋、max()、min()
2.2厚脉、排名類(lèi)
row_number()按照值排序時(shí)產(chǎn)生一個(gè)自增編號(hào)习寸,不會(huì)重復(fù)(如:1、2傻工、3霞溪、4孵滞、5、6)
rank() 按照值排序時(shí)產(chǎn)生一個(gè)自增編號(hào)鸯匹,值相等時(shí)會(huì)重復(fù)坊饶,會(huì)產(chǎn)生空位(如:1、2殴蓬、3匿级、3、3染厅、6)
dense_rank() 按照值排序時(shí)產(chǎn)生一個(gè)自增編號(hào)痘绎,值相等時(shí)會(huì)重復(fù),不會(huì)產(chǎn)生空位(如:1肖粮、2孤页、3、3尿赚、3散庶、4)
2.3蕉堰、其他類(lèi)
** lag() 凌净、lead() 、ntile(n) 屋讶、first_value() 冰寻、last_value()**
1) lag()
LAG(col,n,DEFAULT) 用于統(tǒng)計(jì)窗口內(nèi)往上第n行值
第一個(gè)參數(shù)為列名,第二個(gè)參數(shù)為往上第n行(可選皿渗,默認(rèn)為1)斩芭,第三個(gè)參數(shù)為默認(rèn)值(當(dāng)往上第n行為NULL時(shí)候,取默認(rèn)值乐疆,如不指定划乖,則為NULL)
lag(列名,往前第幾行的數(shù)據(jù),[行數(shù)為null時(shí)的默認(rèn)值,不指定為null]),可以計(jì)算用戶(hù)上次購(gòu)買(mǎi)時(shí)間挤土,或者用戶(hù)下次購(gòu)買(mǎi)時(shí)間琴庵。
2)lead()
與LAG相反,LEAD(col,n,DEFAULT) 用于統(tǒng)計(jì)窗口內(nèi)往下第n行值
第一個(gè)參數(shù)為列名仰美,第二個(gè)參數(shù)為往下第n行(可選迷殿,默認(rèn)為1),第三個(gè)參數(shù)為默認(rèn)值(當(dāng)往下第n行為NULL時(shí)候咖杂,取默認(rèn)值庆寺,如不指定,則為NULL)
lead(列名,往后第幾行的數(shù)據(jù),[行數(shù)為null時(shí)的默認(rèn)值诉字,不指定為null])
3)ntile(n):
NTILE(n)懦尝,用于將分組數(shù)據(jù)按照順序切分成n片知纷,返回當(dāng)前切片值
把有序分區(qū)中的行分發(fā)到指定數(shù)據(jù)的組中,各個(gè)組有編號(hào)陵霉,編號(hào)從1開(kāi)始屈扎,對(duì)于每一行,ntile返回此行所屬的組的編號(hào).
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, --分組內(nèi)將數(shù)據(jù)分成2片
NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn2, --分組內(nèi)將數(shù)據(jù)分成3片
NTILE(4) OVER(ORDER BY createtime) AS rn3 --將所有數(shù)據(jù)分成4片
FROM dw_window_data
ORDER BY cookieid,createtime;
4)first_value()
取分組內(nèi)排序后撩匕,截止到當(dāng)前行鹰晨,第一個(gè)值
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()
取分組內(nèi)排序后,截止到當(dāng)前行止毕,最后一個(gè)值
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模蜡、窗口函數(shù)總結(jié):
其實(shí)窗口函數(shù)邏輯比較繞,我們可以把窗口理解為對(duì)表中的數(shù)據(jù)進(jìn)行分組扁凛,排序等計(jì)算忍疾。要真正的理解HIVE窗口函數(shù),還是要結(jié)合練習(xí)題才行谨朝。下面我們開(kāi)始HIVE窗口函數(shù)的練習(xí)吧卤妒!
二、hive窗口函數(shù)練習(xí)28道題
第一套練習(xí):hive之簡(jiǎn)單窗口函數(shù) over()
1字币、使用 over() 函數(shù)進(jìn)行數(shù)據(jù)統(tǒng)計(jì), 統(tǒng)計(jì)每個(gè)用戶(hù)及表中數(shù)據(jù)的總數(shù)
2则披、求用戶(hù)明細(xì)并統(tǒng)計(jì)每天的用戶(hù)總數(shù)
3、計(jì)算從第一天到現(xiàn)在的所有 score 大于80分的用戶(hù)總數(shù)
4洗出、計(jì)算每個(gè)用戶(hù)到當(dāng)前日期分?jǐn)?shù)大于80的天數(shù)
測(cè)試數(shù)據(jù)
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
建表并導(dǎo)入數(shù)據(jù):
create table test_window
(logday string, #logday時(shí)間
userid string,
score int)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
#加載數(shù)據(jù)
load data local inpath '/home/xiaowangzi/hive_test_data/test_window.txt' into table test_window;
我們先看下表中的數(shù)據(jù):
select * from test_window;
1士复、使用 over() 函數(shù)進(jìn)行數(shù)據(jù)統(tǒng)計(jì), 統(tǒng)計(jì)每個(gè)用戶(hù)及表中數(shù)據(jù)的總數(shù)
select *, count(userid)over() as total from test_window;
這里使用 over() 與 select count(*) 有相同的作用,好處就是翩活,在需要計(jì)算總數(shù)時(shí)不用再進(jìn)行一次關(guān)聯(lián)阱洪。
2、求用戶(hù)明細(xì)并統(tǒng)計(jì)每天的用戶(hù)總數(shù)
可以使用 partition by 按日期列對(duì)數(shù)據(jù)進(jìn)行分區(qū)處理菠镇,如:over(partition by logday)
select *,count()over(partition by logday)as day_total from test_window;
求每天的用戶(hù)數(shù)可以使用select logday, count(userid) from recommend.test_window group by logday
冗荸,但是當(dāng)想要得到 userid 信息時(shí),這種用法的優(yōu)勢(shì)就很明顯利耍。
3蚌本、計(jì)算從第一天到現(xiàn)在的所有 score 大于80分的用戶(hù)總數(shù)
此時(shí)簡(jiǎn)單的分區(qū)不能滿(mǎn)足需求,需要將 order by 和 窗口定義結(jié)合使用堂竟。
select *,count()over(order by logday rows between unbounded preceding and current row)as total
from test_window
where score > 80;
通過(guò) over() 計(jì)算出按日期的累加值魂毁。
其實(shí)自己剛開(kāi)始的時(shí)候就計(jì)算我思路是錯(cuò)了,我就想的是不用累加出嘹,直接
select *,count()over()as total from test_window where score > 80;
這樣計(jì)算席楚,如果這樣計(jì)算的話(huà)只會(huì)顯示表中所有大于80的人數(shù),如果我想看20191021或者看20191022的人數(shù)看不見(jiàn)税稼。
4烦秩、計(jì)算每個(gè)用戶(hù)到當(dāng)前日期分?jǐn)?shù)大于80的天數(shù)
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;
第二套練習(xí)
1垮斯、查詢(xún)?cè)?017年4月份購(gòu)買(mǎi)過(guò)的顧客及總?cè)藬?shù)
2、查詢(xún)顧客的購(gòu)買(mǎi)明細(xì)及月購(gòu)買(mǎi)總額
3只祠、查詢(xún)顧客的購(gòu)買(mǎi)明細(xì)及到目前為止每個(gè)顧客購(gòu)買(mǎi)總金額
4兜蠕、查詢(xún)顧客上次的購(gòu)買(mǎi)時(shí)間----lag()over()偏移量分析函數(shù)的運(yùn)用
5、查詢(xún)前20%時(shí)間的訂單信息
測(cè)試數(shù)據(jù)
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
建表并加載數(shù)據(jù)
create table business
(
name string,
orderdate string,
cost int
)ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
#加載數(shù)據(jù)
load data local inpath "/home/xiaowangzi/hive_test_data/business.txt" into table business;
查看表數(shù)據(jù)
select * from business;
1抛寝、查詢(xún)?cè)?017年4月份購(gòu)買(mǎi)過(guò)的顧客及總?cè)藬?shù)
分析:按照日期過(guò)濾熊杨、分組count求總?cè)藬?shù)(分組為什么不是用group by?自己思考)
select *,count()over() as total from business
where substr(orderdate,1,7) = '2017-04';
2盗舰、查詢(xún)顧客的購(gòu)買(mǎi)明細(xì)及月購(gòu)買(mǎi)總額
是計(jì)算每月每個(gè)用戶(hù)的消費(fèi)金額
select
*,
sum(cost) over(partition by name,substr(orderdate,1,7)) total_amount
from
business;
3晶府、查詢(xún)顧客的購(gòu)買(mǎi)明細(xì)及到目前為止每個(gè)顧客購(gòu)買(mǎi)總金額
分析:按照顧客分組、日期升序排序钻趋、組內(nèi)每條數(shù)據(jù)將之前的金額累加
select
*,
sum(cost) over(partition by name order by orderdate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) total_amount
from
business;
4川陆、查詢(xún)顧客上次的購(gòu)買(mǎi)時(shí)間----lag()over()偏移量分析函數(shù)的運(yùn)用
lag()over()偏移量分析函數(shù)的運(yùn)用
select
name,
orderdate,
cost,
lag(orderdate,1) over(partition by name order by orderdate) last_date
from
business;
5蛮位、查詢(xún)前20%時(shí)間的訂單信息
select *
from
(select *,
ntile(5)over(order by cost)sortgroup_num from business)t
where t.sortgroup_num = 1;
第三套練習(xí):
1较沪、每門(mén)學(xué)科學(xué)生成績(jī)排名(是否并列排名、空位排名三種實(shí)現(xiàn))
2失仁、每門(mén)學(xué)科成績(jī)排名top n的學(xué)生
原始數(shù)據(jù)(學(xué)生成績(jī)信息)
name subject score
孫悟空 語(yǔ)文 87
孫悟空 數(shù)學(xué) 95
孫悟空 英語(yǔ) 68
大海 語(yǔ)文 94
大海 數(shù)學(xué) 56
大海 英語(yǔ) 84
宋宋 語(yǔ)文 64
宋宋 數(shù)學(xué) 86
宋宋 英語(yǔ) 84
婷婷 語(yǔ)文 65
婷婷 數(shù)學(xué) 85
婷婷 英語(yǔ) 78
建表并加載數(shù)據(jù)
create table score
(
name string,
subject string,
score int
) row format delimited fields terminated by "\t";
#加載數(shù)據(jù)
load data local inpath '/home/xiaowangzi/hive_test_data/score.txt' into table score;
查看數(shù)據(jù)
select * from score;
1尸曼、每門(mén)學(xué)科學(xué)生成績(jī)排名(是否并列排名、空位排名三種實(shí)現(xiàn))
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陶因、每門(mén)學(xué)科成績(jī)排名top n的學(xué)生
select
*
from
(
select
*,
row_number() over(partition by subject order by score desc) rmp
from score
) t
where t.rmp<=3;
參考文章1:Hive窗口函數(shù)案例詳解
參考文章2:hive表之簡(jiǎn)單窗口函數(shù) over()