日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Hive窗口函数进阶指南

發(fā)布時(shí)間:2023/12/14 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Hive窗口函数进阶指南 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作為一名數(shù)據(jù)小哥,在寫SQL的漫漫路上,窗口函數(shù)猶如一把披荊斬棘的利劍,幫助作者解決了很多繁瑣復(fù)雜的需求,在此對窗口函數(shù)表示感謝。

本文在介紹了窗口函數(shù)的同時(shí),著重介紹Hive窗口函數(shù)的使用,希望讀者在看完本篇文章之后,對窗口函數(shù)的使用能夠有所掌握。

值得注意的是本文中的例子使用的是HQL(Hive SQL),本文需要一定的SQL基礎(chǔ),如果想了解基礎(chǔ)SQL,請移步本人的數(shù)據(jù)分析師之快速掌握SQL基礎(chǔ) 。

兩個(gè)問題

對于數(shù)據(jù)工作者來說,窗口函數(shù)或多或少都使用過,但是可能沒有系統(tǒng)的去總結(jié)它的用法。

如果讀者對于窗口函數(shù)有一點(diǎn)了解的話,不妨先看看針對下表的兩個(gè)問題,如何使用SQL去解決;如果讀者對于窗口函數(shù)一點(diǎn)都不了解,那請您直接跳過這一部分,直接從什么是窗口函數(shù)開始閱讀。

針對上面一張學(xué)生成績表(class),有year-學(xué)年,class-課程,student-學(xué)生,score-分?jǐn)?shù)這四個(gè)字段,請看問題:

問題1:每年每門學(xué)科排名第一的學(xué)生是?

問題2:每年總成績都有所提升的學(xué)生是?

對于問題1來說比較簡單,既可以使用聚合函數(shù)來統(tǒng)計(jì),也可以使用窗口函數(shù)來統(tǒng)計(jì),其中窗口函數(shù)給了兩種解法:

--使用聚合函數(shù) select?a.year,a.class,b.student from ( select?year,class,max(score)?as?max_score from?class group?by?year,class )?a?join?class?b? on?a.year?=?b.year?and?a.class?=?b.class? and?a.max_score?=?b.score order by a.year

執(zhí)行結(jié)果如下,如果有相同成績的話都會保留。

--使用窗口函數(shù)max select?a.year,a.class,a.student from ( select?year,class,score,student ,max(score)?over? (partition?by?year,class)?as?max_score? --增加一列為聚合后的最高分from?`class` )?a? where a.score = max_score --保留與最高分相同的記錄數(shù)

執(zhí)行結(jié)果如下,同樣的如果有相同記錄也會保留下來。

--使用窗口函數(shù)first_value select?distinct?year,class ,first_value(student)?over? (partition?by?year,class? order?by?score?desc)?as?student from class

執(zhí)行結(jié)果,需要注意的是如果有相同成績,只會取一條記錄。

對比兩種寫法可以發(fā)現(xiàn):

? 使用窗口函數(shù)的SQL代碼量少

? 避免了與原表的join

對于問題2,是一個(gè)相對復(fù)雜但是比較常見的需求,無法只使用聚合函數(shù)來統(tǒng)計(jì),只能配合窗口函數(shù)來統(tǒng)計(jì)。

select student from (select year,student,if((sum_score - lag(sum_score,1,0) over (partition by student order by year))?>?0,1,0)?as?flag,(sum_score - lag(sum_score,1,0) over (partition by student order by year)) as flag1--按照student進(jìn)行分區(qū)并進(jìn)行year正序排序--,找到每個(gè)學(xué)生的上一條學(xué)年總成績--,并與當(dāng)年成績相減,如果小于--,則將flag值置為1,否則置為0from(select year,student,sum(score) as sum_score --按照學(xué)年和學(xué)生進(jìn)行成績匯總from classgroup by year,student) a ) b group by student having avg(flag) = 1 --平均值為1則代表是每年都有增長

執(zhí)行結(jié)果:

通過上面兩個(gè)問題,可以對窗口函數(shù)的特征做一個(gè)簡單的小結(jié):

? 聚合函數(shù)可以作為窗口函數(shù)使用

? 具有計(jì)算和取值的功能

? 不改變記錄數(shù)

什么是窗口函數(shù)

相信看了上面的兩個(gè)問題后,對窗口函數(shù)的使用有一個(gè)大概的了解。下面從理論方面來詳細(xì)了解下窗口函數(shù)。

理論

窗口函數(shù)也稱為OLAP(Online Analytical Processing)函數(shù),是對一組值進(jìn)行操作,不需要使用Group by子句對數(shù)據(jù)進(jìn)行分組,還能在同一行返回原來行的列和使用聚合函數(shù)得到的聚合列

那為什么叫窗口函數(shù)呢?因?yàn)榇翱诤瘮?shù)將表以窗口為單位進(jìn)行分割,并在其中進(jìn)行各種分析操作,為了讓大家快速形成直觀印象,才起了這樣一個(gè)容易理解的名稱

SQL語法

<窗口函數(shù)>() OVER ([PARTITION BY <列清單>][ORDER BY <排序用清單列>] [ASC/DESC](ROWS | RANGE) <范圍條件> )

如上代碼所示,窗口函數(shù)的語法分為四個(gè)部分

函數(shù)子句:指明具體操作,如sum-求和,first_value-取第一個(gè)值;

partition by子句:指明分區(qū)字段,如果沒有,則將所有數(shù)據(jù)作為一個(gè)分區(qū);

order by子句:指明了每個(gè)分區(qū)排序的字段和方式,也是可選的,沒有就是按照表中的順序;

窗口子句:指明相對當(dāng)前記錄的計(jì)算范圍,可以向上(preceding),可以向下(following),也可以使用between指明,上下邊界的值,沒有的話默認(rèn)為當(dāng)前分區(qū)。有些場景比較特殊,后文會講到這種場景。

窗口函數(shù)分類

下面的思維導(dǎo)圖基本包含了Hive所有的窗口函數(shù),按照窗口函數(shù)的功能分為:計(jì)算、取值、排序、序列四種,前三種的使用場景比較常見,容易理解,最后一種(序列)的使用場景比較少。

窗口函數(shù)使用場景

介紹了這么多,那窗口函數(shù)到底可以幫我們做什么呢?

結(jié)合實(shí)際場景看看怎么用窗口函數(shù)來解決問題。下面針對不同的使用場景,將窗口函數(shù)的使用呈現(xiàn)給大家。所有例子的數(shù)據(jù)均來自下圖這張表。

用于輔助計(jì)算

主要的用法是在原有表的基礎(chǔ)上,增加一列聚合后的值,輔以后續(xù)的計(jì)算。

例如:統(tǒng)計(jì)出不同產(chǎn)品類型售價(jià)最高的產(chǎn)品。

具體代碼如下:

--使用窗口函數(shù)max select a.product_type,a.product_name from (select product_name,product_type,sale_price,max(sale_price) over (partition by product_type)?as?max_sale_price?--增加一列為聚合后的最高售價(jià)from product ) a where?a.sale_price?=?a.max_sale_price;? --保留與最高售價(jià)相同的記錄數(shù)

執(zhí)行結(jié)果:

幾乎所有的窗口函數(shù)都可以用于輔助計(jì)算

累積計(jì)算

標(biāo)準(zhǔn)聚合函數(shù)作為窗口函數(shù)配合order by使用,可以實(shí)現(xiàn)累積計(jì)算。

例如:sum窗口函數(shù)配合order by,可以實(shí)現(xiàn)累積和。

具體代碼如下:

SELECT product_id,product_name,product_type,sale_price,SUM(sale_price) OVER (ORDER BY product_id)?AS?current_sum FROM?product;

執(zhí)行結(jié)果:

相應(yīng)的AVG窗口函數(shù)配合order by,可以實(shí)現(xiàn)累積平均,max可以實(shí)現(xiàn)累積最大值,min可以實(shí)現(xiàn)累積最小值,count則可以實(shí)現(xiàn)累積計(jì)數(shù)。注意,只有計(jì)算類的窗口函數(shù)可以實(shí)現(xiàn)累積計(jì)算

這里提出一個(gè)問題,為什么增加了order by就可以實(shí)現(xiàn)累積計(jì)算呢?讀者可以停頓思考一下!

答案馬上揭曉:標(biāo)準(zhǔn)聚合函數(shù)作為窗口函數(shù)使用的時(shí)候,在指明order by的情況下,如果沒有Window子句,則Window子句默認(rèn)為:RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(上邊界不限制,下邊界到當(dāng)前行)。

移動計(jì)算

移動計(jì)算是在分區(qū)和排序的基礎(chǔ)上,對計(jì)算范圍進(jìn)一步做出限定。

例如:按照產(chǎn)品ID排序,將最近3條的銷售價(jià)格進(jìn)行匯總平均。

具體代碼如下:

SELECT?product_id,product_name,sale_price,AVG(sale_price) over?(?ORDER?BY?product_id?rows?2?preceding?)?AS?moving_avg FROM product;

rows 2 preceding的意思就是“截止到之前2行”。也就是將作為匯總對象的記錄限定為如下的最靠近的3行

執(zhí)行結(jié)果如下:

使用關(guān)鍵字FOLLOWING(“之后”)替換PRECEDING,就可以指定截止到之后~行

取任一字段值

取值的窗口函數(shù)有:first_value/last_value、lag/lead,其中first_value和lag在開篇的例子中已經(jīng)使用到了,這里就不舉例說明了。只細(xì)化說明下他們的語法。

first_value(字段名)-取出分區(qū)中的第一條記錄的任意一個(gè)字段的值,可以排序也可以不排序,此處也可以進(jìn)一步指明Window子句。

lag(字段名,N,默認(rèn)值)-取出當(dāng)前行之上的第N條記錄的任意一個(gè)字段的值,這里的N和默認(rèn)值都是可選的,默認(rèn)N為1,默認(rèn)值為null。

排序

排序?qū)?yīng)的四個(gè)窗口函數(shù)為:rank、dense_rank、row_number、ntitle

rank:計(jì)算排序時(shí),如果存在相同位次的記錄,則會跳過之后的位次。

e.g. 有三條記錄排在第1位時(shí):1位、1位、1位、4位......

dense_rank:計(jì)算排序時(shí),即使存在相同位次的記錄,也不會跳過之后的位次。

e.g. 有三條記錄排在第1位時(shí):1位、1位、1位、2位......

row_number:賦予唯一的連續(xù)位次。

e.g. 有三條記錄排在第1位時(shí):1位、2位、3位、4位...

ntitle:用于將分組數(shù)據(jù)按照順序切分成n片,返回當(dāng)前切片值

e.g. 對于一組數(shù)字(1,2,3,4,5,6),ntile(2)切片后為(1,1,1,2,2,2)

1)統(tǒng)計(jì)所有產(chǎn)品的售價(jià)排名

具體代碼如下:

SELECT?product_name,product_type,sale_price,RANK?()?OVER?(ORDER?BY?sale_price?)?AS?ranking FROM product;

執(zhí)行結(jié)果如下:

2)統(tǒng)計(jì)各產(chǎn)品類型下各產(chǎn)品的售價(jià)排名

具體代碼如下:

SELECT?product_name,product_type,sale_price,RANK?()?OVER?(PARTITION?BY?product_type?ORDER?BY?sale_price?)?AS?ranking FROM product;

執(zhí)行結(jié)果如下:

對比一下dense_rank、row_number、ntile

具體代碼如下:

SELECT product_name,product_type,sale_price,RANK () OVER (ORDER BY sale_price) AS ranking,DENSE_RANK () OVER (ORDER BY sale_price) AS dense_ranking,ROW_NUMBER () OVER (ORDER BY sale_price) AS row_num,ntile(3) OVER (ORDER BY sale_price) as nt1,ntile(30) OVER (ORDER BY sale_price) as nt2 --切片大于總記錄數(shù) FROM?product;

執(zhí)行結(jié)果如下:

從結(jié)果可以發(fā)現(xiàn),當(dāng)ntile(30)中的切片大于了總記錄數(shù)時(shí),切片的值為記錄的序號

序列

序列中的兩個(gè)窗口函數(shù)cume_dist和percent_rank,通過實(shí)例來看看它們是怎么使用的。

1)統(tǒng)計(jì)小于等于當(dāng)前售價(jià)的產(chǎn)品數(shù),所占總產(chǎn)品數(shù)的比例

具體代碼如下:

SELECT product_type,product_name,sale_price, CUME_DIST() OVER(ORDER BY sale_price) AS rn1, CUME_DIST() OVER (PARTITION BY product_type ORDER BY sale_price )?AS?rn2? FROM?product;

執(zhí)行結(jié)果如下:

rn1: 沒有partition,所有數(shù)據(jù)均為1組,總行數(shù)為8,

? ? ?第一行:小于等于100的行數(shù)為1,因此,1/8=0.125

? ? ?第二行:小于等于500的行數(shù)為3,因此,3/8=0.375

rn2: 按照產(chǎn)品類型分組,product_type=廚房用品的行數(shù)為4,

? ? ?第三行:小于等于500的行數(shù)為1,因此,1/4=0.25

2)統(tǒng)計(jì)每個(gè)產(chǎn)品的百分比排序

當(dāng)前行的RANK值-1/分組內(nèi)總行數(shù)-1

具體代碼如下:

SELECT product_type,product_name,sale_price, percent_rank() OVER (ORDER BY sale_price) AS rn1, percent_rank() OVER (PARTITION BY product_type ORDER BY sale_price )??AS?rn2? FROM?product;

執(zhí)行結(jié)果如下:


rn1: 沒有partition,所有數(shù)據(jù)均為1組,總行數(shù)為8,

第一行:排序?yàn)?,因此,(1-1)/(8-1)= 0

第二行:排序?yàn)?,因此,(2-1)/(8-1)= 0.14

rn2: 按照產(chǎn)品類型分組,product_type=廚房用品的行數(shù)為4,

第三行:排序?yàn)?,因此,(1-1)/(4-1)= 0

第四行:排序?yàn)?,因此,(2-1)/(4-1)= 0.33

總結(jié)

以上介紹了Hive中窗口函數(shù)的幾乎所有的使用場景,每種函數(shù)的用法也配合代碼進(jìn)行講解,相信大家看了本文后,在實(shí)際數(shù)據(jù)工作中對于窗口函數(shù)的使用肯定會得心應(yīng)手。

總結(jié)

以上是生活随笔為你收集整理的Hive窗口函数进阶指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。