1. 程式人生 > >Hive開窗函式總結

Hive開窗函式總結

背景:
平常我們使用 hive或者 mysql時,一般聚合函式用的比較多。但對於某些偏分析的需求,group by可能很費力,子查詢很多,這個時候就需要使用視窗分析函數了~
注:hiveoracle提供開窗函式,mysql不提供

版本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, --當前行+往前3SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND 1 FOLLOWING) AS pv4, --當前行+往前3行+往後1SUM(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   --將所有資料分成4FROM 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 取分組內排序後,截止到當前行,最後一個值

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