hive窗口分析函數使用詳解系列二之分組排序窗口函數

1.綜述

我們討論面試中各大廠的SQL算法面試題,往往核心考點就在於窗口函數,所以掌握好了窗口函數,面對SQL算法面試往往事半功倍。

已更新第一類聚合函數類,點擊這裏閱讀 hive窗口函數聚合函數類

本節介紹Hive聚合函數中的第二類聚合函數:分組排序窗口函數。

這些函數的用法不僅僅適用於Hive,對於很多數數據庫來說同樣也適用,比如SparkSQL,FlinkSQL以及Mysql8,Oracle,MSSQL等傳統的關係型數據庫。

如果論使用的廣泛性,hive窗口函數中我們使用最廣泛的就是排序類窗口函數,我們通常一提起窗口函數,想到的就是這類排序類窗口函數,它在我們進行數據去重中扮演了至關重要的角色。

1.1 Hive窗口函數分類

Hive提供的窗口函數可以分爲一下幾類

  • 聚合函數類
count() over();
sum() over();
max() over();
min() over();
avg() over();
  • 分組排序類
row_number() over();
rank() over();
dense_rank() over();
percent_rank() over();
cume_dist() over();
ntile() over();
  • 求偏移量類
lead() over();
lag() over();
first_value() over();

1.2 分析函數語法

分析函數 over(partition by 列名 order by 列名 rows between 開始位置 and 結束位置)

具體解析

over()括號內爲空時,是直接進行計算。

其中partition by 列名 是按指定列進行分組,進而進行計算。

最後的order by 列名 是按照指定列進行排序,進而進行計算。

1.3 基礎數據準備

create table if not exists temp.user_info (
  `id` bigint comment '用戶id',
  `client` string comment '客戶端',
  `gender` int comment '性別,0女1男',
  `constellation` string comment '星座',
  `age` int comment '年齡',
  `pv` bigint comment '訪問量',
  `chat_num` bigint comment '聊天次數'
) comment '用戶信息測試臨時表' 

數據預覽

id client gender constellation age pv chat_num
1 ios 0 處女座 29 174 3
2 ios 1 雙魚座 26 263 2
3 android 1 雙魚座 35 232 39
4 ios 1 水瓶座 32 57 3
5 ios 1 射手座 33 67 6
6 ios 1 雙子座 36 81 5
7 ios 1 獅子座 29 68 4
8 ios 1 獅子座 28 19 3
9 ios 0 射手座 32 479 2
10 ios 1 白羊座 26 255 36

2.各分組排序類函數的使用

2.1 row_number

  • 功能

按照排序的順序輸出窗口中的數據的行號信息,不會出現排名相同的情況,且排名是連續的。即使是值相同,排名也會按照其排序順序遞增。

  • 示例

按照星座分組,統計出pv由高到低的排名。

select id,client,gender,row_number() over(partition by constellation order by pv desc) as rank_id
from temp.user_info where id <= 10

數據結果:

id client gender constellation pv rank_id
6 ios 1 雙子座 81 1
2 ios 1 雙魚座 263 1
3 android 1 雙魚座 232 2
1 ios 0 處女座 174 1
9 ios 0 射手座 479 1
5 ios 1 射手座 67 2
4 ios 1 水瓶座 57 1
7 ios 1 獅子座 68 1
8 ios 1 獅子座 19 2
10 ios 1 白羊座 255 1

可以很清晰的看到按星座分組,每個星座內部的PV排名的id。例如,射手座用戶id9排名第一,用戶id5排名第二。

  • 拓展使用:
  1. 更深一步的需求是篩出每個星座最高pv的用戶,(或者說按星座去重,只取最高訪問pv的用戶)

  2. 再更進一步還可以計算諸如最大在線天數等SQL問題。

  3. 業務中常用用法爲通過指定主鍵進行數據去重。

2.2 rank

  • 功能

按照指定列進行排名,如果值相同,則排名並列,下一個排名會出現跳躍,即排名是不連續的。例如有前2個值一樣,那麼前2行並列第一,第3行的排名則爲3。

  • 示例

按使用客戶端分組,統計年齡由高到低的排名。

select id,client,age,rank() over(partition by client order by age desc) as rank_id
from temp.user_info where id <= 10

數據結果:

id client age rank_id
3 android 35 1
6 ios 36 1
5 ios 33 2
9 ios 32 3
4 ios 32 3
1 ios 29 5
7 ios 29 5
8 ios 28 7
2 ios 26 8
10 ios 26 8

可以看到上述信息中,通過安卓和iOS客戶端的年齡大小排名。其中32歲兩個並列第三,沒有第四名,直接開始第五名。相同值進行並列。rank_id的排名是不連續的。

  • 拓展使用:

常用於指定排名場景

2.3 dense_rank

  • 功能

該函數可以和rank()對照使用,按照指定列排序的順序輸出窗口中的數據的排名,如果值相同時,排名並列,下一個排名是連續遞增的,不會出現跳躍情況。即如果前2行的值相同,則前2行並列第1,第3行的排名則爲第2。

可以理解爲一個爲疏鬆排名(rank),一個爲密集排名(dense_rank)

  • 示例

按使用客戶端分組,統計年齡由高到低的排名。

select id,client,age,dense_rank() over(partition by client order by age desc) as rank_id
from temp.user_info where id <= 10

數據結果:對比上文

id client age rank_id
3 android 35 1
6 ios 36 1
5 ios 33 2
9 ios 32 3
4 ios 32 3
1 ios 29 4
7 ios 29 4
8 ios 28 5
2 ios 26 6
10 ios 26 6

可以看到,排名沒有出現跳躍,是連續的,相同排名並列其名次。例如ios的32歲兩個並列第三老。

  • 拓展使用

和rank相反,我希望出現不中斷的排名,這樣的使用場景。

2.4 percent_rank

  • 功能

見名知意,按百分比進行排名。

與 percent 函數類似,percent_rank (分佈函數)函數的窗口 order by 子句所指定列中的值的返回值,是介於 0 和 1 之間的小數形式表示。

  • 計算方法

(rank - 1) / (n - 1)

rank爲上述rank()函數的排名,n 爲當前窗口的總數。

  • 示例

按使用客戶端分組,統計年齡由高到低的百分比排名(percent_rank)。

另一種問法:某某用戶的年齡(或者其他指標)在ios客戶端排名前百分之多少?

select id,client,age,percent_rank() over(partition by client order by age desc) as rank_id
from temp.user_info where id <= 10

數據結果:

id client age rank_id
3 android 35 0.0
6 ios 36 0.0
5 ios 33 0.125
9 ios 32 0.25
4 ios 32 0.25
1 ios 29 0.5
7 ios 29 0.5
8 ios 28 0.75
2 ios 26 0.875
10 ios 26 0.875

可以看出,用戶9和4的年齡32歲排名ios客戶端前百分之二十五,其並列排名。

  • 拓展使用

該函數經常用於較大數據量的百分比佔比分析,也常用於探究數據分佈分析場景。

例如可以通過分佈函數踢除極值,進而求解均值,降低數據誤差。

2.5 cume_dist

  • 功能

如果按升序排列,則統計:小於等於當前值的行數所佔當前分區窗口總行數的比例。(number of rows ≤ current row)/(total number of rows)。

如果是降序排列,則統計:大於等於當前值的行數所佔當前分區窗口總行數的比例。

  • 示例

1.統計小於等於當前年齡的人數佔總人數的比例。

另一種問法:小於等於29歲的人佔總人數的比例。

select id,client,age,cume_dist() over(order by age desc) as rank_id
from temp.user_info where id <= 10
order by age

數據結果:

id client age rank_id
2 ios 26 0.2
10 ios 26 0.2
8 ios 28 0.3
1 ios 29 0.5
7 ios 29 0.5
4 ios 32 0.7
9 ios 32 0.7
5 ios 33 0.8
3 android 35 0.9
6 ios 36 1.0

可以看到小於等於29歲所佔人羣的總比例爲50%。

2.統計當前客戶端分區小於等於當前年齡的人數佔總人數的比例。

另一種問法:ios客戶端小於等於29歲的人佔總人數的比例。

select id,client,age,cume_dist() over(partition by client order by age) as rank_id
from temp.user_info where id <= 10
order by age

數據結果:

id client age rank_id
2 ios 26 0.2222222222222222
10 ios 26 0.2222222222222222
8 ios 28 0.3333333333333333
1 ios 29 0.5555555555555556
7 ios 29 0.5555555555555556
4 ios 32 0.7777777777777778
9 ios 32 0.7777777777777778
5 ios 33 0.8888888888888888
3 android 35 1.0
6 ios 36 1.0

可以看到ios客戶端小於等於29歲的人羣佔比爲55.6%左右。

  • 拓展使用

該函數是一個累積求比例的函數,常用於求解排名前百分之多少或者排名後百分之多少的問題。

2.6 ntile

  • 功能

分桶窗口函數,用於將按指定列分組的數據按照順序切分成N片,返回當前切片值。將一個有序的數據集劃分爲多個桶(bucket),併爲每行分配一個適當的桶數(切片值,第幾個切片,第幾個分區等概念)。它可用於將數據劃分爲相等的小切片,爲每一行分配該小切片的數字序號。

  • 注意

ntile不支持rows between,range between.

  • 示例

統計按照客戶端分組,按年齡排序,將每個窗口分成3片(桶),返回每片(桶)的的分片(桶)信息。

另一種問法:把ios客戶端的人羣按年齡正序分成三部分,返回任意一部分的值。

select id,client,age,cume_dist() over(partition by client order by age) as rank_id
from temp.user_info where id <= 10
order by age

數據結果:

id client age rank_id
3 android 35 1
10 ios 26 1
2 ios 26 1
8 ios 28 1
7 ios 29 2
1 ios 29 2
4 ios 32 2
9 ios 32 3
5 ios 33 3
6 ios 36 3

可以看到ios客戶端9人被分爲三部分,需要哪一部分,只要再限制rank_id 等於幾就行。

  • 拓展使用

該函數是一個分桶函數,可以按照指定的列把數據均勻的分成想要的幾部分數據。

例如,求解用戶活躍時長前百分之二十的羣體,如果包含0活躍時長用戶,用百分比排序就不好計算了,而用該函數可以很快計算出來。

以上,爲本次分享內容。

後續計劃會開啓一個新的系列內容:SQL每日一題系列,多來自各大廠的高頻面試題,有好的算法面試題也可以積極分享,互相交流。

感謝閱讀。

下一期:hive窗口分析函數使用詳解之三-求偏移量類窗口函數

按例,歡迎點擊此處關注我的個人公衆號,交流更多知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章