set hive.cli.print.header=true;
HIVE的窗口函數,對於每一條數據通過窗口滑動,對在窗口內的數據進行聚合等操作。
假設窗口大小爲2,那麼對每一條數據就以大小爲2的窗口滑動。
第一條數據的窗口:
然後滑動窗口到第二條數據:
對每二條、第三條、第四條數據進行窗口滑動,進行相應的聚合等操作,將結果放入對應行的cum_money列。
OVER()函數是窗口函數。一般與聚合函數/lag/lead/ntile/row_number/rank/dense_rank等函數搭配使用。
這裏通過示例加以說明:
假設table_sale表的數據如下:
orderdate | sale_money | clientName |
---|---|---|
2020-01-01 | 2.0 | a |
2020-01-02 | 1.0 | b |
2020-01-01 | 3.0 | b |
2020-01-02 | 5.0 | a |
1 不加條件的OVER函數
問題:統計客戶總消費金額
select *,sum(sale_money) over() as 'cum_money' from table_sale;
執行結果如下:
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | 11.0 |
2020-01-02 | 1.0 | b | 11.0 |
2020-01-01 | 3.0 | b | 11.0 |
2020-01-02 | 5.0 | a | 11.0 |
而HIVE是如何實現累計的呢?
對於OVER()函數而言,它會爲每一條數據進行開窗操作,不加參數時每一條數據的窗口大小是相同的。每一個窗口內部都會執行sum([窗口大小的數據集合])函數。
這裏沒有對OVER()函數加任何條件,從而每一條數據的開窗大小都爲4。進行sum()操作如下:
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | sum([2.0,1.0,3.0,5.0]) |
2020-01-02 | 1.0 | b | sum([2.0,1.0,3.0,5.0]) |
2020-01-01 | 3.0 | b | sum([2.0,1.0,3.0,5.0]) |
2020-01-02 | 5.0 | a | sum([2.0,1.0,3.0,5.0]) |
2 加排序條件的OVER函數
問題:統計客戶累計消費情況
select *,sum(sale_money) over(order by orderdate) as 'cum_money' from table_sale;
裏面對OVER函數加了條件,首先會對數據進行排序操作,此時加了排序條件每一條數據的窗口大小是不同的。
對於第一條數據而言,它的窗口大小爲1(因爲它前面沒有數據),爲第一條數據開窗後,就開始執行相應的聚合函數,然後賦予到第一行中。
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | sum([2.0]) |
2020-01-01 | 3.0 | b | |
2020-01-02 | 1.0 | b | |
2020-01-02 | 5.0 | a |
對於第二條數據,因爲它的前面已經有了a客戶的數據,再加上自身的數據,從而當前窗口大小爲2,然後就開啓大小爲2的窗口,進行聚合操作,即sum([2.0,,3.0])
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | sum([2.0]) |
2020-01-01 | 3.0 | b | sum([2.0,3.0]) |
2020-01-02 | 1.0 | b | |
2020-01-02 | 5.0 | a |
對於第三條數據,則開啓大小爲3的窗口,第四條條數據則開啓大小爲4的窗口:
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | sum([2.0]) |
2020-01-01 | 3.0 | b | sum([2.0,3.0]) |
2020-01-02 | 1.0 | b | sum([2.0,3.0,1.0]) |
2020-01-02 | 5.0 | a | sum([2.0,3.0,1.0,5.0]) |
從而完成了累計操作。
3 加分區條件的OVER函數
問題:統計每一個客戶總消費情況
select *,sum(sale_money) over(partition by clientName) as 'cum_money' from table_sale;
執行sum(sale_money) over(parition by name)部分時,首先對數據集進行分區(分組)操作,此時每一條數據集的窗口大小爲所在分區的大小。如對a客戶分區,a客戶有2條數據集,從而窗口大小爲2。從而對於sum()函數執行情況如下:
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | sum([2.0,5.0]) |
2020-01-02 | 5.0 | a | sum([2.0,5.0]) |
2020-01-01 | 3.0 | b | sum([3.0,1.0]) |
2020-01-02 | 1.0 | b | sum([3.0,1.0]) |
4 加分區排序條件的OVER函數
問題:統計每一個客戶累計消費情況
select *,
sum(sale_money) over(partition by clientName order by orderdate) as 'cum_money'
from table_sale;
首先OVER函數對數據執行分區操作,然後對數據進行排序操作。而加了排序操作使得分區內部的每一條數據的窗口大小是不同的。對於sum()函數執行情況如下:
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | sum([2.0]) |
2020-01-02 | 5.0 | a | sum([2.0,5.0]) |
2020-01-01 | 3.0 | b | sum([3.0]) |
2020-01-02 | 1.0 | b | sum([3.0,1.0]) |
5 加窗口大小條件的OVER函數
這裏先對OVER函數內部的若干參數進行說明:
- CURRENT ROW:當前行
- n PRECEDING:往前n行數據
- n FOLLOWING:往後n行數據
- UNBOUNDED:起點,UNBOUNDED PRECEDING 表示從前面的起點, UNBOUNDED FOLLOWING表示到後面的終點
問題:統計相鄰兩條數據的消費總額
select *,
sum(sale_money) over(rows between 1 preceding and current row) as 'cum_money'
from table_sale;
該問題的答案忽略了邊界問題,請忽略。
1 preceding : 指的是前一行;current row : 當前行,從而限定了窗口大小爲2。此時sum()函數執行如下:
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | 0 |
2020-01-02 | 5.0 | a | 2.0 |
2020-01-01 | 3.0 | b | 0 |
2020-01-02 | 1.0 | b | 3.0 |
6 LAG/LEAD函數的使用
- LAG(col,n):往前第n行數據
- LEAD(col,n):往後第n行數據
LAG和LEAD()要與OVER()函數一起使用。
問題:查看客戶前一天消費金額。
select *,
lag(sale_money,1,0) over(partition by clientName order by orderdate) as 'yesterdayMoney'
from table_sale;
-- 也可以寫成這樣的
select *,
lag(sale_money,1,0) over(distirbution by clientName sort by orderdate) as 'yesterdayMoney'
from table_sale;
lag(sale_money,1,0):取窗口內的前1天sale_money數據,若無數據,則用0替代。
查詢結果爲:
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | 0 |
2020-01-02 | 5.0 | a | 2.0 |
2020-01-01 | 3.0 | b | 0 |
2020-01-02 | 1.0 | b | 3.0 |
對於LEAD函數也是一樣的理解,如:
問題:查看客戶下一天消費金額。
select *,
lead(sale_money,1,0) over(partition by clientName order by orderdate) as 'nextMoney'
from table_sale;
lead(sale_money,1,0):取窗口內的下1天sale_money數據,若無數據,則用0替代。
orderdate | sale_money | clientName | cum_money |
---|---|---|---|
2020-01-01 | 2.0 | a | 5.0 |
2020-01-02 | 5.0 | a | 0 |
2020-01-01 | 3.0 | b | 1.0 |
2020-01-02 | 1.0 | b | 0 |
就這麼多了,希望對大家有幫助。
若有錯誤,還請指正。
參考:
1 嗶哩嗶哩的尚硅谷視頻:https://www.bilibili.com/video/BV1z4411y7C2?p=44
2 《Hive編程指南》