Hive常用開窗函數總結


 

本文鏈接: https://blog.csdn.net/Abysscarry/article/details/81408265

背景:
平常我們使用 hive或者 mysql時,一般聚合函數用的比較多。但對於某些偏分析的需求,group by可能很費力,子查詢很多,這個時候就需要使用窗口分析函數了~
注:hiveoracle提供開窗函數,mysql8之前版本不提供但Oracle發布的 MySQL 8.0版本支持窗口函數(over)和公用表表達式(with)這兩個重要的功能!


版本Hive 1.1.0 + cdh5.13.0


一、介紹

分析函數用於計算基於組的某種聚合值,它和聚合函數的不同之處是:對於每個組返回多行,而聚合函數對於每個組只返回一行

開窗函數指定了分析函數工作的數據窗口大小,這個數據窗口大小可能會隨着行的變化而變化!到底什么是數據窗口?后面舉例會詳細講到!

1. 基礎結構:

分析函數(如:sum(),max(),row_number()...) + 窗口子句(over函數)

2. over函數寫法:
  over(partition by cookieid order by createtime) 先根據cookieid字段分區,相同的cookieid分為一區,每個分區內根據createtime字段排序(默認升序)
  
注:不加 partition by 的話則把整個數據集當作一個分區,不加 order by的話會對某些函數統計結果產生影響,如sum()

3. 測試數據:

測試表test1只有三個字段 cookieidcreatetimepv

4. 窗口含義:

SELECT cookieid,createtime,pv,
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime) AS pv1, -- 默認為從起點到當前行
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS pv2, --從起點到當前行,結果同pv1 
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS pv3,   --當前行+往前3行
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND 1 FOLLOWING) AS pv4,    --當前行+往前3行+往后1行
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS pv5   ---當前行+往后所有行  
FROM test1;

結果:

cookieid    createtime    pv    pv1  pv2    pv3  pv4  pv5
a            2017-12-01    3     3      3         3      3    3
b            2017-12-00    3     3      3         3      3    3
cookie1        2017-12-10    1     1      1         1      6    26
cookie1        2017-12-11    5     6      6         6      13   25
cookie1        2017-12-12    7     13  13         13   16   20
cookie1        2017-12-13    3     16  16         16   18   13
cookie1        2017-12-14    2     18  18         17   21   10
cookie1        2017-12-15    4     22  22         16   20   8
cookie1        2017-12-16    4     26  26         13   13   4
cookie2        2017-12-12    7     7      7         7      13   14
cookie2        2017-12-16    6     13  13         13   14   7
cookie2        2017-12-24    1     14  14         14   14   1
cookie3        2017-12-22    5     5      5         5       5   5

注:這些窗口的划分都是在分區內部!超過分區大小就無效了

相信大家看了后就會明白,如果不指定ROWS BETWEEN,默認統計窗口為從起點到當前行;如果不指定ORDER BY,則將分組內所有值累加;

關鍵是理解 ROWS BETWEEN 含義,也叫做window子句
PRECEDING:往前
FOLLOWING往后
CURRENT ROW:當前行
UNBOUNDED無邊界,UNBOUNDED PRECEDING 表示從最前面的起點開始, UNBOUNDED FOLLOWING:表示到最后面的終點
–其他AVG,MIN,MAX,和SUM用法一樣


二、SUM 函數

select cookieid,createtime,pv,
sum(pv) over(PARTITION BY cookieid ORDER BY createtime) as pv1 
FROM test1

 

 

首先 PARTITION BY cookieid,根據cookieid分區,各分區之間默認根據字典順序排序ORDER BY createtime,指定的是分區內部的排序,默認為升序

我們可以清晰地看到,窗口函數和聚合函數的不同,sum()函數可以根據每一行的窗口返回各自行對應的值,有多少行記錄就有多少個sum值,而group by只能計算每一組的sum,每組只有一個值!

其中sum()計算的是分區內排序后一個個疊加的值,和order by有關

如果不加 order by會咋樣:

select cookieid,createtime,pv,
sum(pv) over(PARTITION BY cookieid) as pv1 
FROM test1

 

 


可以看到,如果沒有order by,不僅分區內沒有排序,sum()計算的pv也是整個分區的pv

注:max()函數無論有沒有order by 都是計算整個分區的最大值


三、NTILE 函數

NTILE(n),用於將分組數據按照順序切分成n片,返回當前切片值

注1:如果切片不均勻,默認增加第一個切片的分布
注2:NTILE不支持ROWS BETWEEN

SELECT cookieid,createtime,pv,
NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime) AS ntile1,    --分組內將數據分成2片
NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS ntile2,  --分組內將數據分成3片
NTILE(4) OVER(PARTITION BY cookieid ORDER BY createtime) AS ntile3   --將所有數據分成4片
FROM test1 

用法舉例:
統計一個cookie,pv數最多的前1/3的天:

SELECT cookieid,createtime,pv,
NTILE(3) OVER(PARTITION BY cookieid ORDER BY pv DESC) AS ntile 
FROM test1;

ntile = 1 的記錄,就是我們想要的結果!


四、ROW_NUMBER 函數

ROW_NUMBER() 從1開始,按照順序,生成分組內記錄的序列

ROW_NUMBER() 的應用場景非常多,比如獲取分組內排序第一的記錄、獲取一個session中的第一條refer等。

SELECT cookieid,createtime,pv,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn  
FROM test1;

 

 


五、RANK 和 DENSE_RANK 函數

RANK() 生成數據項在分組中的排名,排名相等會在名次中留下空位
DENSE_RANK() 生成數據項在分組中的排名,排名相等會在名次中不會留下空位

我們把 rankdense_rankrow_number三者對比,這樣比較清晰:

SELECT cookieid,createtime,pv,
RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rank1,
DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS d_rank2,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3 
FROM test1

 

 


六、CUME_DIST 函數

cume_dist 返回小於等於當前值的行數/分組內總行數

比如,我們可以統計小於等於當前薪水的人數,所占總人數的比例

SELECT cookieid,createtime,pv,
round(CUME_DIST() OVER(ORDER BY pv),2) AS cd1,
round(CUME_DIST() OVER(PARTITION BY cookieid ORDER BY pv),2) AS cd2  
FROM test1;

注:cd1沒有partition,所有數據均為1組!


七、PERCENT_RANK 函數

percent_rank 分組內當前行的RANK值-1/分組內總行數-1

注:一般不會用到該函數,可能在一些特殊算法的實現中可以用到吧

SELECT  cookieid,createtime,pv,
PERCENT_RANK() OVER(ORDER BY pv) AS rn1 
from test1


八、LAG 和 LEAD 函數

LAG(col,n,DEFAULT) 用於統計窗口內往上第n行值

第一個參數為列名,第二個參數為往上第n行(可選,默認為1),第三個參數為默認值(當往上第n行為NULL時候,取默認值,如不指定,則為NULL)

SELECT cookieid,createtime,pv,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAG(createtime,1,'1970-01-01') OVER(PARTITION BY cookieid ORDER BY createtime) AS lag1,
LAG(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS lag2 
FROM test1;

LEAD 函數則與 LAG 相反:
LEAD(col,n,DEFAULT)  用於統計窗口內往下第n行值

第一個參數為列名,第二個參數為往下第n行(可選,默認為1),第三個參數為默認值(當往下第n行為NULL時候,取默認值,如不指定,則為NULL)


九、FIRST_VALUE 和 LAST_VALUE 函數

FIRST_VALUE 取分組內排序后,截止到當前行,第一個值

SELECT cookieid,createtime,pv,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
FIRST_VALUE(pv) OVER(PARTITION BY cookieid ORDER BY createtime) AS first  
FROM test1;

 

 

LAST_VALUE 函數則相反:
LAST_VALUE 取分組內排序后,截止到當前行,最后一個值

這兩個函數還是經常用到的(往往和排序配合使用),比較實用!


參考文章:http://lxw1234.com/archives/category/hive


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM