hive窗口函数最全总结
準(zhǔn)備工作
一:窗口函數(shù)概況
1.1 窗口函數(shù)說(shuō)明
1.2 窗口范圍說(shuō)明
1.2.1 窗口范圍取值可選項(xiàng)
1.2.2 默認(rèn)窗口范圍含義
思考一:如何理解省略order by的情況,不能指定窗口范圍?
二:窗口函數(shù)分類(lèi)和特性
2.1 窗口函數(shù)分類(lèi)
2.2 窗口函數(shù)功能和常見(jiàn)應(yīng)用概括
2.2.1 功能
2.2.2 應(yīng)用
三:窗口函數(shù)應(yīng)用場(chǎng)景(基礎(chǔ)使用)
3.1 聚合型窗口函數(shù)
3.1.1 情境一:購(gòu)買(mǎi)物品的用戶(hù)及其總?cè)藬?shù)
3.1.2 情景二:用戶(hù)購(gòu)買(mǎi)物品明細(xì)及每周各種類(lèi)型用戶(hù)購(gòu)買(mǎi)總數(shù)量(補(bǔ)分析列)。
3.2 分析型(排序)窗口函數(shù)
3.2.1情景三:每種用戶(hù)類(lèi)型銷(xiāo)量排行第一的用戶(hù)及其明細(xì)(分組排名)
思考二:窗口排序遇到null
3.2.2 情景四:每種用戶(hù)類(lèi)型銷(xiāo)量排名前2的用戶(hù)明細(xì)(top n)
3.1.4 情景五:連續(xù)n天登陸
情景六:給定數(shù)字的頻率查詢(xún)中位數(shù)
3.3 取值型窗口函數(shù)
3.3.1 準(zhǔn)備工作
3.3.2 情景七:找到車(chē)輛上一次鎖車(chē)記錄
3.3.3 情景八:那么如何求某個(gè)省份圍欄、某小時(shí)、某輛車(chē)的最長(zhǎng)未被騎行時(shí)間。(多次窗口使用)
思考三:多次窗口使用能否改成一次窗口和group by結(jié)合得到大致數(shù)據(jù)?
思考四:多次窗口使用能否改成一次窗口和group by結(jié)合得到所有明細(xì)數(shù)據(jù)?
3.3.4 情景九:連續(xù)出現(xiàn)的數(shù)字
四:進(jìn)階使用
4.1 窗口函數(shù)與group by異同點(diǎn)
4.1.1 窗口函數(shù)與group by 區(qū)別
4.1.2 group by去重
4.2 collect_list + partition by的特殊用法
4.2.1 問(wèn)題提出
4.2.2 解決思路
4.2.3 實(shí)際解決
4.2.4 延伸到風(fēng)控業(yè)務(wù)的實(shí)際用法
4.3 cube&grouping set這塊
五:窗口函數(shù)底層運(yùn)行原理
六:其他注意事項(xiàng)
思考問(wèn)題:
思考一:如何理解省略order by的情況,不能指定窗口范圍?
思考二:窗口排序遇到null
準(zhǔn)備工作
已經(jīng)有測(cè)試表:order_detail.本文接下來(lái)所有測(cè)試樣例如果未特殊說(shuō)明均基于下面order_detail表。
| user_id (用戶(hù)ID) | order_date (訂單日期) | user_type (用戶(hù)類(lèi)型) | sales (銷(xiāo)量) | |
| 1 | qishili | 2022-07-03 | new | 5 |
| 2 | wangshi | 2022-07-01 | old | 2 |
| 3 | liiu | 2022-07-01 | new | 1 |
| 4 | lilisi | 2022-07-05 | new | 5 |
| 5 | lisi | 2022-07-02 | old | 1 |
| 6 | wutong | 2022-07-02 | new | 6 |
| 7 | qibaqiu | 2022-07-05 | new | 1 |
| 8 | liwei | 2022-07-02 | old | 3 |
| 9 | zhangsa | 2022-07-02 | new | 2 |
| 10 | wanger | 2022-07-02 | new | 3 |
一:窗口函數(shù)概況
1.1 窗口函數(shù)說(shuō)明
窗口函數(shù)簡(jiǎn)單的說(shuō)就是在執(zhí)行聚合函數(shù)的時(shí)候指定一個(gè)操作窗口,這個(gè)窗口由over來(lái)進(jìn)行控制。接下來(lái)重點(diǎn)介紹一下over()函數(shù)。
over():指定分析函數(shù)工作的數(shù)據(jù)窗口大小,這個(gè)大小可能會(huì)隨著行的變化而變化。
其基本語(yǔ)法如下:<分析函數(shù)> over ( partition by <用于分組的列名> order by <用于排序的列名> desc/asc rows between 開(kāi)始行 and 結(jié)束行 )
樣例1--窗口函數(shù)基本使用
select user_id,user_type,sales,count(user_id) over(partition by user_type order by sales desc rows between unbounded preceding and current row) as cnt from order_detail可以看到over()里面一共有3部分組成:分區(qū)、排序、指定窗口范圍,注意這三部分可以組合使用,也可以不使用。
partition by可以理解為分組。over(partition by 列)搭配分析函數(shù)時(shí)候(可以接多個(gè)列,表示按照這些列分組),分析函數(shù)是按照每一組的數(shù)據(jù)進(jìn)行分析計(jì)算的。
order by是在窗口內(nèi)進(jìn)行排序,desc/asc可以進(jìn)行選擇使用。
rows between 開(kāi)始位置 and 結(jié)束位置,指定數(shù)據(jù)范圍。 -- 例如常使用的窗口范圍是rows between unbounded preceding and current row.常用該窗口來(lái)累加計(jì)算。
1.2 窗口范圍說(shuō)明
1.2.1 窗口范圍取值可選項(xiàng)
1)范圍取值
【a:rows+參數(shù)來(lái)控制范圍】
-
默認(rèn)值(不寫(xiě)):這個(gè)其實(shí)是最常用的,下面會(huì)進(jìn)行不同情況下默認(rèn)值總結(jié)
-
preceding:往前;
-
following: 往后
-
current row:當(dāng)前行
-
unbounded :起點(diǎn)(一般結(jié)合preceding,following使用)
-
unbounded preceding: 表示該窗口最前面的行(起點(diǎn))
-
unbounded following:該窗口最后面的行(終點(diǎn))
樣例2--窗口范圍一些完整寫(xiě)法
rows between unbounded preceding and current row --(表示從窗口起點(diǎn)到當(dāng)前行) rows?between unbounded preceding and unbounded following--(表示從窗口起點(diǎn)到終點(diǎn)) rows between 2 preceding and 1 following --(表示往前2行到往后1行) rows between 2 preceding and 1 current row --(表示往前兩行到當(dāng)前行) rows between current row and unbounded following --(表示當(dāng)前行到終點(diǎn)) 【b.range between來(lái)控制范圍】range 表示的是值, 表示比這個(gè)值小n的行,比這個(gè)值大n的行即range between 是以當(dāng)前值為錨點(diǎn)進(jìn)行計(jì)算。
eg: sum(sales) range between 100 preceding and 200 following
【c.通過(guò)窗口函數(shù)來(lái)控制范圍】
-
lag(col, n, default_val): 往前第n行數(shù)據(jù),沒(méi)有數(shù)據(jù)的話(huà)用default_value代替
-
lead(col, n, default_val):往后第n行數(shù)據(jù),沒(méi)有數(shù)據(jù)的話(huà)用default_value代替
-
ntile(n):把有序分區(qū)中的行分發(fā)到指定數(shù)據(jù)的組中,各個(gè)組有編號(hào),編號(hào)從1開(kāi)始,對(duì)于每一行,NTILE返回此行所屬的組的編號(hào)。注意:n必須為int類(lèi)型.
2)為了解釋rows between unbounded preceding and current row和rows?between unbounded preceding and unbounded following,我們舉例說(shuō)明:
樣例3--rows between and取值范圍區(qū)別
select user_id,user_type,sales,count(user_id) over(partition by user_type order by sales desc rows between unbounded preceding and current row) as current_cnt, -- 樣例1中的代碼: 窗口取到當(dāng)前行count(user_id) over(partition by user_type order by sales desc rows between unbounded preceding and unbounded following) as unbounded_following_cnt -- 窗口取到所有行 from order_detail樣例3測(cè)試結(jié)果(如下表),可以看出:
-
當(dāng)指定范圍是從頭到當(dāng)前行,分析函數(shù)得到的這個(gè)局部范圍(動(dòng)態(tài)變化的)的分析值,例如current_cnt,每一行記錄得到的都是一個(gè)變化的值(同一個(gè)分組內(nèi)不同行值都是不同的)。(再具體一點(diǎn)是到每個(gè)組中的某一行記錄)
-
當(dāng)指定范圍是開(kāi)頭到末尾行,分析函數(shù)得到的是全局范圍(固定的)的分析值,例如unbounded_following_cnt,同一個(gè)組每一行記錄都是一個(gè)相等值(指同一個(gè)分組內(nèi)哈,不同分組值還是不同的)。
| user_id | order_date | user_type | sales | current_cnt | unbounded_following_cnt | |
| 1 | wutong | 2022-07-01 | new | 6 | 1 | 7 |
| 2 | lilisi | 2022-07-02 | new | 5 | 3 | 7 |
| 3 | qishili | 2022-07-02 | new | 5 | 3 | 7 |
| 4 | wanger | 2022-07-01 | new | 3 | 4 | 7 |
| 5 | zhangsa | 2022-07-03 | new | 2 | 5 | 7 |
| 6 | qibaqiu | 2022-07-06 | new | 1 | 7 | 7 |
| 7 | liiu | 2022-07-05 | new | 1 | 7 | 7 |
| 8 | liwei | 2022-07-05 | old | 3 | 1 | 3 |
| 9 | wangshi | 2022-07-02 | old | 2 | 2 | 3 |
| 10 | lisi | 2022-07-01 | old | 1 | 3 | 3 |
1.2.2 默認(rèn)窗口范圍含義
1)情況一:當(dāng)over()里面有partition by和order by的時(shí)候,但是缺少rows between and這部分,即不寫(xiě)這塊,也就是我們常說(shuō)的默認(rèn)值,其分析函數(shù)對(duì)應(yīng)的范圍是同一組從開(kāi)頭到當(dāng)前值。
上面的默認(rèn)窗口范圍和樣例2中的窗口范圍區(qū)別:
樣例4--既有partition by和order by情況下默認(rèn)值窗口范圍和常見(jiàn)窗口范圍比較
select user_id,user_type,sales,sum(sales) over(partition by user_type order by sales desc rows between unbounded preceding and current row) as current_cnt, -- 樣例1中的代碼: 窗口取到各組當(dāng)前行(當(dāng)前這行的記錄)sum(sales) over(partition by user_type order by sales desc) as default_cnt, -- 默認(rèn)也是窗口取到各組當(dāng)前行,但是更準(zhǔn)確的說(shuō)應(yīng)該是取到各組當(dāng)前值,因?yàn)閛rder by之后,-- 當(dāng)排序的字段相同的時(shí)候,-- 相同的字段會(huì)一起被計(jì)算出來(lái),所以說(shuō)是取到當(dāng)前“值/行”。會(huì)把值相同的進(jìn)行一起計(jì)算,-- 得到某一個(gè)結(jié)果。sum(sales) over(partition by user_type order by sales desc rows between unbounded preceding and unbounded following) as unbounded_following_cnt-- 窗口取到所有行 ,各個(gè)分組里面也取所有行,各個(gè)組里面只有同一個(gè)值 from order_detail樣例4測(cè)試結(jié)果(如下表),可以清楚看到:
-
rows between unbounded preceding and current row其分析計(jì)算出來(lái)的結(jié)果是針對(duì)開(kāi)頭到當(dāng)前行的范圍,所以同一組的各個(gè)記錄對(duì)應(yīng)的分析值也不一樣;
-
而默認(rèn)不寫(xiě)窗口范圍的,其分析是針對(duì)開(kāi)頭到當(dāng)前值的范圍,因?yàn)楫?dāng)前值可以對(duì)應(yīng)多條記錄,需要把這幾天記錄一起去分析得到一個(gè)明確的結(jié)果,然后這個(gè)值所對(duì)應(yīng)的這幾條記錄最終都是這同一個(gè)明確的結(jié)果。
-
而窗口范圍明確寫(xiě)開(kāi)頭到結(jié)尾,那么同一個(gè)組內(nèi)所有記錄都是同一個(gè)結(jié)果,分析的是同一個(gè)組的所有數(shù)據(jù)。
| user_id | user_type | sales | current_cnt | default_cnt | unbounded_following_cnt | |
| 1 | wutong | new | 6 | 1 | 1 | 7 |
| 2 | lilisi | new | 5 | 2 | 3 | 7 |
| 3 | qishili | new | 5 | 3 | 3 | 7 |
| 4 | wanger | new | 3 | 4 | 4 | 7 |
| 5 | zhangsa | new | 2 | 5 | 5 | 7 |
| 6 | liiu | new | 1 | 6 | 7 | 7 |
| 7 | qibaqiu | new | 1 | 7 | 7 | 7 |
| 8 | liwei | old | 3 | 1 | 1 | 3 |
| 9 | wangshi | old | 2 | 2 | 2 | 3 |
| 10 | lisi | old | 1 | 3 | 3 | 3 |
2)情況二:當(dāng)over里面只有partiton by,沒(méi)有order by的時(shí)候,注意這個(gè)時(shí)候?qū)懙拇翱诜秶荒軐?xiě)樣例2的范圍,會(huì)直接報(bào)錯(cuò),只能按照默認(rèn)范圍來(lái)。默認(rèn)范圍是從分組內(nèi)開(kāi)頭到結(jié)尾的記錄即ows between unbounded preceding and unbounded following,雖然范圍是這個(gè),但是sql代碼不能寫(xiě)上ows between unbounded preceding and unbounded following,只能什么也不寫(xiě)。(因?yàn)闆](méi)有order by每次數(shù)據(jù)記錄返回來(lái)的順序可能不一致)。
樣例5--窗口里面只有partition by
-- 只partition by select user_id,user_type,sales,sum(sales) over(partition by user_type ) as default_cnt -- 默認(rèn)-- sum(sales) over(partition by user_type rows between unbounded -- preceding and unbounded following) as unbounded_following_cnt -- 直接報(bào)錯(cuò),不能寫(xiě)出窗口范圍。 from order_detail樣例5測(cè)試結(jié)果:可以看待窗口范圍是各個(gè)分組里面開(kāi)頭到結(jié)尾的數(shù)據(jù),各個(gè)組分析得到的結(jié)果一樣。
| 1 | zhangsa | new | 2 | 23 |
| 2 | wanger | new | 3 | 23 |
| 3 | liiu | new | 1 | 23 |
| 4 | qibaqiu | new | 1 | 23 |
| 5 | wutong | new | 6 | 23 |
| 6 | lilisi | new | 5 | 23 |
| 7 | qishili | new | 5 | 23 |
| 8 | lisi | old | 1 | 6 |
| 9 | wangshi | old | 2 | 6 |
| 10 | liwei | old | 3 | 6 |
3)情況三:當(dāng)over()里面只有order by的時(shí)候,這個(gè)時(shí)候窗口范圍的控制就和情況一中一樣,可以,只不過(guò)這個(gè)時(shí)候就只有一個(gè)分組了,全部數(shù)據(jù)放到一個(gè)分組里面!
常用的窗口范圍和默認(rèn)窗口范圍見(jiàn)情況一,這里不再贅述。
樣例6--窗口里面沒(méi)有partition by,有order by
select user_id,user_type,sales,sum(sales) over(order by sales desc rows between unbounded preceding and current row) as current_cnt, -- 樣例1中的代碼: 窗口取到當(dāng)前行sum(sales) over(order by sales desc) as default_cnt, -- 默認(rèn):取到當(dāng)前“行/值”sum(sales) over(order by sales desc rows between unbounded preceding and unbounded following) as unbounded_following_cnt -- 窗口取到所有行 from order_detail樣例6測(cè)試結(jié)果
| user_id | user_type | sales | current_cnt | default_cnt | unbounded_following_cnt | |
| 1 | wutong | new | 6 | 6 | 6 | 29 |
| 2 | lilisi | new | 5 | 11 | 16 | 29 |
| 3 | qishili | new | 5 | 16 | 16 | 29 |
| 4 | wanger | new | 3 | 19 | 22 | 29 |
| 5 | liwei | old | 3 | 22 | 22 | 29 |
| 6 | zhangsa | new | 2 | 24 | 26 | 29 |
| 7 | wangshi | old | 2 | 26 | 26 | 29 |
| 8 | lisi | old | 1 | 27 | 29 | 29 |
| 9 | liiu | new | 1 | 28 | 29 | 29 |
| 10 | qibaqiu | new | 1 | 29 | 29 | 29 |
4)情況四:當(dāng)窗口里面既沒(méi)有partition by,也沒(méi)有order by的時(shí)候
樣例7--既沒(méi)有partition by也沒(méi)有order by的時(shí)候
select user_id,user_type,sales,-- sum(sales) over(rows between unbounded preceding and current row) -- as current_cnt, -- 錯(cuò)誤寫(xiě)法 ,也就是說(shuō)這種情況下,我們也不能自己寫(xiě)窗口范圍。sum(sales) over() as sales_cnt -- 默認(rèn) 正確寫(xiě)法 范圍默認(rèn)為開(kāi)頭到結(jié)尾。-- sum(sales) over(rows between unbounded preceding and --unbounded following) as unbounded_following_cnt -- 錯(cuò)誤寫(xiě)法 from order_detail5)對(duì)上面5種情況進(jìn)行初步總結(jié):
-
如果over()里面沒(méi)有order by,那么我們就不能指定窗口范圍,只能使用默認(rèn)值(代碼中row between end這處什么也不寫(xiě)),此時(shí)的默認(rèn)值代表的范圍是各分組的開(kāi)頭到結(jié)尾數(shù)據(jù);
-
反之,如果over()里面🈶?order by,那么我們就可以指定窗口范圍,如果不指定窗口范圍(默認(rèn)值),此時(shí)默認(rèn)值的范圍代表分組里面的的開(kāi)頭到當(dāng)前值(當(dāng)前“行”)的數(shù)據(jù)范圍。
-
如果over里面沒(méi)有partition by,此時(shí)就只有一個(gè)分組,同上一樣。
思考一:如何理解省略order by的情況,不能指定窗口范圍?
在任何并行系統(tǒng)中,如果order by子句未生成唯一排序和總排序,則行的順序是不確定的,也就是說(shuō),如果order by 表達(dá)式生成重復(fù)的值(部分排序),則這些行的返回順序可能會(huì)有差別,也就是說(shuō)窗口函數(shù)可能返回意外或不一致的結(jié)果。
二:窗口函數(shù)分類(lèi)和特性
2.1 窗口函數(shù)分類(lèi)
1)按照窗口來(lái)分
可以分為靜態(tài)窗口函數(shù)、動(dòng)態(tài)窗口函數(shù)。其中靜態(tài)窗口函數(shù)主要是指排序函數(shù);而動(dòng)態(tài)窗口函數(shù)主要分為累計(jì)計(jì)算函數(shù)和偏分析函數(shù)。其中窗口數(shù)據(jù)集這塊對(duì)應(yīng)文章第一章的詳細(xì)描述。
窗口函數(shù)窗口函數(shù)名窗口數(shù)據(jù)集靜態(tài)窗口函數(shù)動(dòng)態(tài)窗口函數(shù)
排序函數(shù)row_number():當(dāng)前行在組內(nèi)的排序,eg:1,2,3,4,5
dense_rank()不間斷組內(nèi)排序,eg:1,1,2,3,3
rank():間斷的組內(nèi)排序,eg:1,1,3,4,5
累計(jì)計(jì)算函數(shù)偏移分析函數(shù)sum()count()avg()max()min()percent_rank():累計(jì)百分比cume_dist():累計(jì)分布值first_value():返回組內(nèi)第一個(gè)值last_value():返回組內(nèi)的最后一個(gè)值nth_value():返回組內(nèi)的第n行l(wèi)ag():從當(dāng)前行往上偏移第n行,默認(rèn)為nulllead():從當(dāng)前行開(kāi)始向下偏移第n行,默認(rèn)為nullntile():返回當(dāng)前行在組內(nèi)截止當(dāng)前行的第n行分組參數(shù):partition by排序參數(shù):order by窗口參數(shù):row between ...andover()
2)按照函數(shù)功能
可以將窗口函數(shù)分為:聚合型窗口函數(shù)、分析型窗口函數(shù)、取值型窗口函數(shù)。
窗口函數(shù)聚合型取值型分析型聚合函數(shù):sum() /max() /min() /avg() /count()排序函數(shù)和占比函數(shù):row_number() / dense_rank() / rank() / percent_rank() /cume_dist()?取值:lag() / lead() / first_value() / last_value()
3)舉例擴(kuò)展一下窗口函數(shù)中三個(gè)排序函數(shù)區(qū)別
樣例8--三個(gè)排序函數(shù)區(qū)別(每種用戶(hù)類(lèi)型銷(xiāo)量排名)
select user_id,user_type,sales,row_number() over(partition by user_type order by sales) as rn,dense_rank() over(partition by user_type order by sales) as rd,rank() over(partition by user_type order by sales) as rr from order_detail樣例8測(cè)試結(jié)果:
| user_id | user_type | sales | rn | rd | rr | |
| 1 | liiu | new | 1 | 1 | 1 | 1 |
| 2 | qibaqiu | new | 1 | 2 | 1 | 1 |
| 3 | zhangsa | new | 2 | 3 | 2 | 3 |
| 4 | wanger | new | 3 | 4 | 3 | 4 |
| 5 | lilisi | new | 5 | 5 | 4 | 5 |
| 6 | qishili | new | 5 | 6 | 4 | 5 |
| 7 | wutong | new | 6 | 7 | 5 | 7 |
| 8 | lisi | old | 1 | 1 | 1 | 1 |
| 9 | wangshi | old | 2 | 2 | 2 | 2 |
| 10 | liwei | old | 3 | 3 | 3 | 3 |
2.2 窗口函數(shù)功能和常見(jiàn)應(yīng)用概括
2.2.1 功能
1)窗口函數(shù)同時(shí)具有分組和排序的功能;
2)不減少原有表的行數(shù)。即窗口函數(shù)可以理解為在本行內(nèi)做運(yùn)算,每一行對(duì)應(yīng)一行的值。
3)可指定窗口數(shù)據(jù)范圍
2.2.2 應(yīng)用
1)分組排名問(wèn)題
2)top n問(wèn)題
3)連續(xù)登陸問(wèn)題
4)加一分析結(jié)果列
5)累計(jì)問(wèn)題
6)上一個(gè)下一個(gè)記錄問(wèn)題
三:窗口函數(shù)應(yīng)用場(chǎng)景(基礎(chǔ)使用)
3.1 聚合型窗口函數(shù)
sum,count()這些很常見(jiàn),其功能我們不再贅述
3.1.1 情境一:購(gòu)買(mǎi)物品的用戶(hù)及其總?cè)藬?shù)
樣例9--查詢(xún)2022年7月份購(gòu)買(mǎi)物品的用戶(hù)及其總?cè)藬?shù)
-- 正確方法:總?cè)藬?shù)是指7月份購(gòu)買(mǎi)物品的總?cè)藬?shù)。 select user_id,count(1) over() as 7_mounth_all_cnt from order_detail where substring(order_date,1,7) = '2022-07' -- 錯(cuò)誤理解;不是每個(gè)人購(gòu)買(mǎi)物品總?cè)藬?shù)。 select user_id,count(1) as 7_mounth_all_cnt from order_detail where substring(order_date,1,7) = '2022-07' group by user_id樣例9測(cè)試結(jié)果:
| user_id | 7_mounth_all_cnt | |
| 1 | qibaqiu | 10 |
| 2 | zhangsa | 10 |
| 3 | lisi | 10 |
| 4 | wanger | 10 |
| 5 | liiu | 10 |
| 6 | wangshi | 10 |
| 7 | liwei | 10 |
| 8 | wutong | 10 |
| 9 | lilisi | 10 |
| 10 | qishili | 10 |
3.1.2 情景二:用戶(hù)購(gòu)買(mǎi)物品明細(xì)及每周各種類(lèi)型用戶(hù)購(gòu)買(mǎi)總數(shù)量(補(bǔ)分析列)。
樣例10:用戶(hù)購(gòu)買(mǎi)物品明細(xì)以及每周各種類(lèi)型用戶(hù)購(gòu)買(mǎi)總數(shù)量
select user_id,user_type,sales,sum(sales) over(partition by week(order_date),user_type) as sales_s from order_detail樣例10測(cè)試結(jié)果如下,像這樣既要明細(xì)又需要聚合值的,顯然使用group by是做不到的,要使用窗口函數(shù)。
| user_id | user_type | sales | sales_s | |
| wanger | new | 3 | 20 | |
| 2 | qibaqiu | new | 1 | 20 |
| 3 | wutong | new | 6 | 20 |
| 4 | lilisi | new | 5 | 20 |
| 5 | qishili | new | 5 | 20 |
| 6 | lisi | old | 1 | 3 |
| 7 | wangshi | old | 2 | 3 |
| 8 | zhangsa | new | 2 | 3 |
| 9 | liiu | new | 1 | 3 |
| 10 | liwei | old | 3 | 3 |
3.2 分析型(排序)窗口函數(shù)
在第二章中已經(jīng)對(duì)常見(jiàn)排序函數(shù)進(jìn)行總結(jié)過(guò),可參考。
3.2.1情景三:每種用戶(hù)類(lèi)型銷(xiāo)量排行第一的用戶(hù)及其明細(xì)(分組排名)
可以參照第二章中的樣例7。
樣例11:每種用戶(hù)類(lèi)型銷(xiāo)量排行第一的用戶(hù)及其明細(xì)(分組排序)
-- 把窗口函數(shù)放在自查詢(xún)里面得到一個(gè)每條記錄的排名 select * from (select user_id,user_type,sales,row_number() over(partition by user_type order by sales desc) as rcfrom order_detail )t1 where t1.rc = 1樣例11測(cè)試結(jié)果:
| user_id | user_type | sales | rc | |
| 1 | wutong | new | 6 | 1 |
| 2 | liwei | old | 3 | 1 |
思考二:窗口排序遇到null
1)窗口中order by的 時(shí)候,使用desc降序排列的時(shí)候,null值排在首位;
使用asc升序的時(shí)候,null值排在末尾;
2)如何控制null的位置:
nulls last和 nulls first.
如下圖:此樣例建議等看完第四章中部分論述的背景,再回來(lái)看這個(gè)
rrow_number() over(partition by fence,hour(lock_time),bike_id order by time_gap desc nulls last) as rc
樣例12:排序的時(shí)候不需要先對(duì)null值進(jìn)行處理,直接使用nulls last會(huì)很省事!
select -- 找到分組中最大間隔fence,hourtime,bike_id,time_gap from ( -- 分組對(duì)時(shí)間間隔進(jìn)行排序selectfence,bike_id,hour(lock_time) as hourtime,time_gap,-- 發(fā)現(xiàn)time_gap存在很多null的情況,在參與order by排序的時(shí)候會(huì)影響排序結(jié)果,所以得到結(jié)果不準(zhǔn)確row_number() over(partition by fence,hour(lock_time),bike_id order by time_gap desc nulls last) as rcfrom( -- 分組找到本次記錄的上次記錄的鎖車(chē)時(shí)間和事件間隔select fence,bike_id,hour(lock_time),unlock_time,lock_time,lag(unlock_time,1,null) over(partition by fence,hour(lock_time),bike_id) as last_unlock_time,(unix_timestamp(unlock_time) - unix_timestamp(lag(lock_time,1,null) over(partition by fence,hour(lock_time),bike_id))) as time_gapfrom bike_hour_inc)t1 )t2 where t2.rc =1 order by fence,hourtime,bike_id ?3.2.2 情景四:每種用戶(hù)類(lèi)型銷(xiāo)量排名前2的用戶(hù)明細(xì)(top n)
可以參照第二章中的樣例7
樣例13:每種用戶(hù)類(lèi)型銷(xiāo)量排名前2的用戶(hù)明細(xì)
-- 自查詢(xún)使用窗口函數(shù)得到分組里面每條記錄排名,外查詢(xún)進(jìn)行排名的約束 select * from (select user_id,user_type,sales,row_number() over(partition by user_type order by sales desc) as rcfrom order_detail )t1 where t1.rc <=2樣例13測(cè)試結(jié)果:
| user_id | user_type | sales | rc | |
| 1 | wutong | new | 6 | 1 |
| 2 | lilisi | new | 5 | 2 |
| 3 | liwei | old | 3 | 1 |
| 4 | wangshi | old | 2 | 2 |
3.1.4 情景五:連續(xù)n天登陸
給定測(cè)試數(shù)據(jù)集
|
| 2022-06-02 |
| 7 | 2022-06-10 |
| 7 | 2022-06-03 |
| 7 | 2022-05-30 |
| 7 | 2022-05-31 |
| 7 | 2022-06-02 |
| 1 | 2022-06-07 |
| 7 | 2022-06-01 |
| 1 | 2022-05-30 |
sql邏輯
樣例14:連續(xù)n天登陸(去重、排序、間隔、分組、約束)
select login_id from ( selectlogin_id,login_date,-- row_number() over(partition by login_id order by login_date) as rc -- 對(duì)登陸日期進(jìn)行排名-- 找到登陸日期與時(shí)間排名之間的間隔date_sub(login_date,row_number() over(partition by login_id order by login_date) ) as time_gap from (-- 每個(gè)人一天可能不止登陸一次,需要去重select login_id,login_datefrom login_tablegroup by login_id,login_date)t1)t2group by t2.login_id,t2.time_gap -- 以用戶(hù)和時(shí)間間隔進(jìn)行分組having count(1) >= 5 -- 每個(gè)用戶(hù)分組和時(shí)間間隔分組里面數(shù)據(jù)記錄大于等于5的。測(cè)試結(jié)果login_id = 7;
情景六:給定數(shù)字的頻率查詢(xún)中位數(shù)
todo
3.3 取值型窗口函數(shù)
3.3.1 準(zhǔn)備工作
1)lag/lead() over()的使用
lag(col,n,default):用于統(tǒng)計(jì)窗口往上第n行值:
-
第一個(gè)參數(shù)為列名;
-
第二個(gè)參數(shù)為往上第n行(默認(rèn)為1);
-
第三個(gè)參數(shù)為默認(rèn)值(當(dāng)往上第n行為null的時(shí)候,取默認(rèn)值,如果不指定,則取null)。
同理:
lead(col,n,default):用于統(tǒng)計(jì)窗口往下第n行值:
-
第一個(gè)參數(shù)為列名;
-
第二個(gè)參數(shù)為往下第n行(默認(rèn)為1);
-
第三個(gè)參數(shù)為默認(rèn)值(當(dāng)往下第n行為null的時(shí)候,取默認(rèn)值,如果不指定,則取null)。
-
窗口partition by和group by
-
是否添加范圍
2)first_value
todo
思考:first_value與max()區(qū)別???
3.3.2 情景七:找到車(chē)輛上一次鎖車(chē)記錄
類(lèi)似情景表述:查看顧客上次的購(gòu)買(mǎi)時(shí)間
某車(chē)輛在某一小時(shí)內(nèi)某一圍欄中本次騎行的明細(xì)及對(duì)應(yīng)上次鎖車(chē)時(shí)間。新的測(cè)試數(shù)據(jù)集:
| fence | bike_id | unlock_time | lock_time | |
| 1 | 湖北 | 101 | 2022-09-01 07:03:23 | 2022-09-01 07:05:03 |
| 2 | 湖北 | 101 | 2022-09-01 07:12:53 | 2022-09-01 07:15:13 |
| 3 | 湖北 | 101 | 2022-09-01 07:32:11 | 2022-09-01 07:55:13 |
| 4 | 湖北 | 101 | 2022-09-01 07:56:11 | 2022-09-01 07:59:52 |
| 5 | 湖北 | 101 | 2022-09-01 09:12:10 | 2022-09-01 09:25:53 |
| 6 | 湖北 | 101 | 2022-09-01 09:42:09 | 2022-09-01 09:45:33 |
| 7 | 湖北 | 102 | 2022-09-01 07:02:21 | 2022-09-01 07:15:13 |
| 8 | 湖北 | 102 | 2022-09-01 07:47:21 | 2022-09-01 07:55:13 |
| 9 | 山東 | 104 | 2022-09-01 07:02:21 | 2022-09-01 07:15:13 |
| 10 | 山東 | 104 | 2022-09-01 07:44:21 | 2022-09-01 07:55:13 |
樣例15:某哈羅單車(chē)車(chē)輛在某一小時(shí)內(nèi)某一圍欄中本次騎行的明細(xì)及對(duì)應(yīng)上次鎖車(chē)時(shí)間?,沒(méi)有order by
select fence,bike_id,unlock_time, -- 開(kāi)鎖lock_time, -- 鎖車(chē)lag(lock_time,1,null) over(partition by fence,hour(unlock_time),bike_id) as last_lock_time from bike_hour_inc測(cè)試結(jié)果
| fence | bike_id | unlock_time | lock_time | last_lock_time (本次開(kāi)鎖時(shí)間對(duì)應(yīng)的上一次鎖車(chē)時(shí)間) | |
| 1 | 山東 | 104 | 2022-09-01 07:02:21 | 2022-09-01 07:15:13 | null |
| 2 | 山東 | 104 | 2022-09-01 07:44:21 | 2022-09-01 07:55:13 | 2022-09-01 07:15:13 |
| 3 | 湖北 | 101 | 2022-09-01 07:03:23 | 2022-09-01 07:05:03 | null |
| 4 | 湖北 | 101 | 2022-09-01 07:12:53 | 2022-09-01 07:15:13 | 2022-09-01 07:05:03 |
| 5 | 湖北 | 101 | 2022-09-01 07:32:11 | 2022-09-01 07:55:13 | 2022-09-01 07:15:13 |
| 6 | 湖北 | 101 | 2022-09-01 07:56:11 | 2022-09-01 07:59:52 | 2022-09-01 07:55:13 |
| 7 | 湖北 | 102 | 2022-09-01 07:02:21 | 2022-09-01 07:15:13 | null |
| 8 | 湖北 | 102 | 2022-09-01 07:47:21 | 2022-09-01 07:55:13 | 2022-09-01 07:15:13 |
| 9 | 湖北 | 101 | 2022-09-01 09:12:10 | 2022-09-01 09:25:53 | null |
| 10 | 湖北 | 101 | 2022-09-01 09:42:09 | 2022-09-01 09:45:33 | 2022-09-01 09:25:53 |
111
3.3.3 情景八:那么如何求某個(gè)省份、某小時(shí)、某拖拉機(jī)的最長(zhǎng)未被騎行時(shí)間。(多次窗口使用)
樣例16:各車(chē)輛在某一省份分小時(shí)段最大未騎行間隔(不完全對(duì)的寫(xiě)法,忽視了null排序情況)
-- 注意:最里層自查詢(xún)要在窗口里面使用partition by進(jìn)行分組,在外層查詢(xún)里面對(duì)時(shí)間間隔排序也需要分組 select -- 找到分組中最大間隔fence,hourtime,bike_id,time_gap from ( -- 分組對(duì)時(shí)間間隔進(jìn)行排序selectfence,bike_id,hour(lock_time) as hourtime,time_gap,-- 發(fā)現(xiàn)time_gap存在很多null的情況,在參與order by排序的時(shí)候會(huì)影響排序結(jié)果,所以得到結(jié)果不準(zhǔn)確row_number() over(partition by fence,hour(lock_time),bike_id order by time_gap desc) as rcfrom( -- 分組找到本次記錄的上次記錄的鎖車(chē)時(shí)間和事件間隔select fence,bike_id,hour(lock_time),unlock_time,lock_time,lag(unlock_time,1,null) over(partition by fence,hour(lock_time),bike_id) as last_unlock_time,(unix_timestamp(unlock_time) - unix_timestamp(lag(lock_time,1,null) over(partition by fence,hour(lock_time),bike_id order by unlock_time))) as time_gapfrom bike_hour_inc)t1 )t2 where t2.rc =1 order by fence,hourtime,bike_id測(cè)試結(jié)果:
| fence | hourtime | bike_id | time_gap | |
| 1 | 山東 | 7 | 104 | 1748 |
| 2 | 湖北 | 7 | 101 | 58 |
| 3 | 湖北 | 7 | 102 | 1928 |
| 4 | 湖北 | 9 | 101 | 976 |
糾正后,最終正確寫(xiě)法1:對(duì)null先進(jìn)行處理
樣例17:各拖拉機(jī)在某一省份分小時(shí)段最大未騎行間隔(兩次窗口函數(shù)完全正確寫(xiě)法)
select * from (select t2.fence,t2.bike_id,t2.every_hour,t2.unlock_time,t2.lock_time,t2.last_lock_time,t2.time_gap,t2.time_gap_not_null,row_number() over(partition by t2.fence,t2.every_hour,t2.bike_id order by time_gap_not_null desc) as rcfrom(select t1.fence,t1.bike_id,t1.every_hour,t1.unlock_time,t1.lock_time,t1.last_lock_time,t1.time_gap,case when t1.time_gap is not null then t1.time_gap else -1 end as time_gap_not_null-- nvl(t1.time_gap,999999) as time_gap_not_null2 -- 不行,數(shù)值類(lèi)型不一樣的感覺(jué)。from(select fence,bike_id,hour(lock_time) as every_hour,unlock_time,lock_time,-- 使用lag,窗口里面應(yīng)該需要加一個(gè)order by吧!lag(lock_time,1,null) over(partition by fence,hour(unlock_time),bike_id order by unlock_time) as last_lock_time,(unix_timestamp(unlock_time) - unix_timestamp(lag(lock_time,1,null) over(partition by fence,hour(lock_time),bike_id order by unlock_time))) as time_gapfrom bike_hour_inc)t1)t2 )t3 where t3.rc =1 order by t3.fence,t3.every_hour,t3.bike_id測(cè)試結(jié)果:
| fence | bike_id | hour | unlock_time | lock_time | last_lock_time | time_gap | time_gap_not_null | rc | |
| 1 | 山東 | 104 | 7 | 2022-09-01 07:44:21 | 2022-09-01 07:55:13 | 2022-09-01 07:15:13 | 1748 | 1748 | 1 |
| 2 | 湖北 | 101 | 7 | 2022-09-01 07:32:11 | 2022-09-01 07:55:13 | 2022-09-01 07:15:13 | 1018 | 1018 | 1 |
| 3 | 湖北 | 102 | 7 | 2022-09-01 07:47:21 | 2022-09-01 07:55:13 | 2022-09-01 07:15:13 | 1928 | 1928 | 1 |
| 4 | 湖北 | 101 | 9 | 2022-09-01 09:42:09 | 2022-09-01 09:45:33 | 2022-09-01 09:25:53 | 976 | 976 | 1 |
糾正后,正確寫(xiě)法(第二種辦法)
見(jiàn)3.4節(jié)樣例12。
注意事項(xiàng):
1)null在窗口中排序問(wèn)題:(回去看3.4節(jié)的論述)
2)nvl()字段類(lèi)型問(wèn)題
3)多重子查詢(xún)
4)兩次分組
思考三:多次窗口使用能否改成一次窗口和group by結(jié)合得到大致數(shù)據(jù)?
子查詢(xún)里面使用窗口,外查詢(xún)里面也使用窗口
外查詢(xún)?nèi)绻皇褂么翱诤瘮?shù)partition by分組,我們也可以使用group by后取max即可。
樣例18:各車(chē)輛在某一圍欄分小時(shí)段最大未騎行間隔(使用group by+一次窗口函數(shù)? )
-- 原始版 select t2.fence,t2.bike_id,t2.every_hour,max(t2.time_gap_not_null) as max_gapfrom(select t1.fence,t1.bike_id,t1.every_hour,t1.unlock_time,t1.lock_time,t1.last_lock_time,t1.time_gap,case when t1.time_gap is not null then t1.time_gap else -1 end as time_gap_not_null-- nvl(t1.time_gap,999999) as time_gap_not_null2 -- 不行,數(shù)值類(lèi)型不一樣的感覺(jué)。from(select fence,bike_id,hour(lock_time) as every_hour,unlock_time,lock_time,lag(lock_time,1,null) over(partition by fence,hour(unlock_time),bike_id) as last_lock_time,(unix_timestamp(unlock_time) - unix_timestamp(lag(lock_time,1,null) over(partition by fence,hour(lock_time),bike_id))) as time_gapfrom bike_hour_inc)t1)t2group by t2.fence,t2.bike_id,t2.every_hour order by t2.fence,t2.every_hour,t2.bike_id簡(jiǎn)潔版:因?yàn)橥獠樵?xún)使用的是group by后的max聚合,而null是不參與聚合這種的
樣例19:各拖拉機(jī)在某一省份分小時(shí)段最大未騎行間隔(使用group by+一次窗口函數(shù)? ?簡(jiǎn)介版)
SQL-- 因?yàn)橥獠樵?xún)使用的是group by后的max聚合,而null是不參與聚合這種的, -- 所以上述代碼還可以繼續(xù)簡(jiǎn)化,不需要對(duì)null進(jìn)行專(zhuān)門(mén)處理 -- 簡(jiǎn)潔版select t1.fence,t1.bike_id,t1.every_hour,max(t1.time_gap) as max_gapfrom(select fence,bike_id,hour(lock_time) as every_hour,unlock_time,lock_time,lag(lock_time,1,null) over(partition by fence,hour(unlock_time),bike_id) as last_lock_time,(unix_timestamp(unlock_time) - unix_timestamp(lag(lock_time,1,null) over(partition by fence,hour(lock_time),bike_id order by unlock_time))) as time_gapfrom bike_hour_inc)t1group by t1.fence,t1.bike_id,t1.every_hour order by t1.fence,t1.every_hour,t1.bike_id測(cè)試結(jié)果如下:可以看到除了一些明細(xì)數(shù)據(jù)沒(méi)有(后續(xù)有時(shí)間可以探討再進(jìn)行關(guān)聯(lián)得到所有明細(xì)數(shù)據(jù)),其余的結(jié)果是一致的!!!
| 1 | 山東 | 104 | 7 | 1748 |
| 2 | 湖北 | 101 | 7 | 1018 |
| 3 | 湖北 | 102 | 7 | 1928 |
| 4 | 湖北 | 101 | 9 | 976 |
綜上所述:如果我們只需要的是大致數(shù)據(jù),那么就可以使用上述方法
思考四:多次窗口使用能否改成一次窗口和group by結(jié)合得到所有明細(xì)數(shù)據(jù)?
樣例20:各拖拉機(jī)在某一省份分小時(shí)段最大未騎行間隔(group by+窗口 )測(cè)試是否可以得到明細(xì)數(shù)據(jù)
select t2.fence,t2.bike_id,t2.every_hour,t2.max_gap,t3.* from (select t1.fence,t1.bike_id,t1.every_hour,max(t1.time_gap) as max_gapfrom(select fence,bike_id,hour(lock_time) as every_hour,unlock_time,lock_time,lag(lock_time,1,null) over(partition by fence,hour(unlock_time),bike_id order by unlock_time) as last_lock_time,(unix_timestamp(unlock_time) - unix_timestamp(lag(lock_time,1,null) over(partition by fence,hour(lock_time),bike_id))) as time_gapfrom bike_hour_inc)t1group by t1.fence,t1.bike_id,t1.every_hour )t2 join bike_hour_inc t3 on t2.fence = t3.fence and t2.bike_id = t3.bike_idand t2.every_hour = hour(unlock_time) order by t2.fence,t2.every_hour,t2.bike_id測(cè)試結(jié)果:我們發(fā)現(xiàn), group by得到大致數(shù)據(jù)記錄然后再去關(guān)聯(lián),發(fā)現(xiàn)數(shù)據(jù)記錄變多了,這樣說(shuō)明如果group by的不是主鍵,那么就會(huì)數(shù)據(jù)量變多,得不到我們需要的記錄,當(dāng)僅僅group by的是主鍵,才能用這種group by分組再進(jìn)行關(guān)聯(lián)得到所需的最終結(jié)果!
| fence | bike_id | every_hour | max_gap | fence | bike_id | unlock_time | lock_time | |
| 1 | 山東 | 104 | 7 | 1748 | 山東 | 104 | 2022-09-01 07:44:21 | 2022-09-01 07:55:13 |
| 2 | 山東 | 104 | 7 | 1748 | 山東 | 104 | 2022-09-01 07:02:21 | 2022-09-01 07:15:13 |
| 3 | 湖北 | 101 | 7 | 1018 | 湖北 | 101 | 2022-09-01 07:32:11 | 2022-09-01 07:55:13 |
| 4 | 湖北 | 101 | 7 | 1018 | 湖北 | 101 | 2022-09-01 07:12:53 | 2022-09-01 07:15:13 |
| 5 | 湖北 | 101 | 7 | 1018 | 湖北 | 101 | 2022-09-01 07:03:23 | 2022-09-01 07:05:03 |
| 6 | 湖北 | 101 | 7 | 1018 | 湖北 | 101 | 2022-09-01 07:56:11 | 2022-09-01 07:59:52 |
| 7 | 湖北 | 102 | 7 | 1928 | 湖北 | 102 | 2022-09-01 07:02:21 | 2022-09-01 07:15:13 |
| 8 | 湖北 | 102 | 7 | 1928 | 湖北 | 102 | 2022-09-01 07:47:21 | 2022-09-01 07:55:13 |
| 9 | 湖北 | 101 | 9 | 976 | 湖北 | 101 | 2022-09-01 09:12:10 | 2022-09-01 09:25:53 |
| 10 | 湖北 | 101 | 9 | 976 | 湖北 | 101 | 2022-09-01 09:42:09 | 2022-09-01 09:45:33 |
3.3.4 情景九:連續(xù)出現(xiàn)的數(shù)字
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
sql邏輯:
樣例21:連續(xù)三次出現(xiàn)的數(shù)字
select distinct num as consecutivenums from (select num,lead(num,1,null) over(order by id) as n1,lead(num,2,null) over(order by id) as n2from shuzi )t1 where t1.num = t1.n1 and t1.n1 = t1.n2測(cè)試結(jié)果就是:1。
四:進(jìn)階使用
4.1 窗口函數(shù)與group by異同點(diǎn)
4.1.1 窗口函數(shù)與group by 區(qū)別
窗口函數(shù)分組匯總會(huì)改變返回?cái)?shù)據(jù)的行數(shù),group by 默認(rèn)返回每組中的第一行;而窗口函數(shù)不會(huì)減少返回?cái)?shù)據(jù)行數(shù),只可能是在原始數(shù)據(jù)表新加上一列,來(lái)存放窗口分析得到的值。
樣例8--group by返回?cái)?shù)據(jù)行數(shù)
select user_type, count(1) as cnt from order_detail group by user_type樣例8測(cè)試結(jié)果:可以看到group by只得到兩條數(shù)據(jù)(一共兩個(gè)分組),而窗口函數(shù)作用后,原來(lái)是幾條數(shù)據(jù),現(xiàn)在還是幾條數(shù)據(jù)。
| user_type | cnt | |
| 1 | new | 7 |
| 2 | old | 3 |
4.1.2 group by去重
group by可以完成去重效果,但是窗口函數(shù)不能。
答案:這是不行的,因?yàn)槭褂么翱诤瘮?shù)并沒(méi)有減少數(shù)據(jù)量。那么什么時(shí)候用窗口的時(shí)候不能使用group by呢。參考下面一節(jié)的總結(jié)。
4.2 collect_list + partition by的特殊用法
參考: sparksql中collect_list+partitionby的特殊用法 - 百度文庫(kù)
4.2.1 問(wèn)題提出
hive或者spark中collect_list一般都是用來(lái)做分組后的合并,也就是說(shuō)經(jīng)常和partition by連用,本節(jié)特定講一下collect_list + partition by的用法。
問(wèn)題:如何求一個(gè)用戶(hù)的連續(xù)續(xù)課次數(shù)和續(xù)課金額?
背景:有嚴(yán)格規(guī)定的兩個(gè)課程期次序列:s1,s2,s3,s4和p1,p2,p3,我們規(guī)定滿(mǎn)足如下條件才算是一個(gè)用戶(hù)的續(xù)課;
a:當(dāng)課程屬于同一課程序列的時(shí)候,必須遵循嚴(yán)格的期次順序,且不得有重復(fù)。如s1,s3,s4,s2,站在s2的角 度看,續(xù)了3次(s3,s4,s2);站在s3的角度看,續(xù)課了1次(s4);
b:當(dāng)課程屬于不同課程序列的時(shí)候,只要后面跟的是不同的課程序列都算一次,重復(fù)值不算,如. s2,p2,p1,s2,站在s2的角度看,續(xù)費(fèi)了2次(p2,p1);站在p2的角度看,續(xù)費(fèi)了1次(s2)
4.2.2 解決思路
1)第一種思路:如果不考慮減少數(shù)據(jù)量,那么對(duì)于用戶(hù)參加的每一次的課程而言,附加一列生成一個(gè)這個(gè)用戶(hù)的全量有序的購(gòu)課記錄就好,只是要記錄一下對(duì)應(yīng)的這門(mén)課在整個(gè)序列中的位置。
2)第二種思路:最好的方式肯定是對(duì)于每一個(gè)用戶(hù)而言,附加一個(gè)該用戶(hù)在這門(mén)課之后有序的選課記錄,這里也有兩種方式。
a:自身關(guān)聯(lián)這張表,找出每一個(gè)用戶(hù)買(mǎi)了這門(mén)課之后的所有購(gòu)課記錄和購(gòu)課金額;
b:如果我們不想再關(guān)聯(lián)一次,那么我們就要考慮一下如何使用collect_list + partition by進(jìn)行處理。
4.2.3 實(shí)際解決
給定數(shù)據(jù)
| user_id | term_id | pay_time |
| 1 | s1 | 1 |
| 1 | s2 | 2 |
| 1 | s3 | 3 |
| 1 | s4 | 4 |
代碼邏輯:
select user_type, count(1) as cnt from order_detail group by user_type測(cè)試結(jié)果:
| user_id | term_id | pay_time | category_list |
| 1 | s4 | 4 | ["s4,"] |
| 1 | s3 | 3 | ["s4,","s3,"] |
| 1 | s2 | 2 | ["s4,","s3,","s2,"] |
| 1 | s1 | 1 | ["s4,","s3,","s2,","s1,"] |
4.3 cube&grouping set這塊
?todo 高級(jí)函數(shù)
GROUPING SETS、GROUPING__ID、CUBE、ROLLUP這幾個(gè)分析函數(shù)通常用于OLAP中,不能累加,而且需要根據(jù)不同維度上鉆和下鉆的指標(biāo)統(tǒng)計(jì),比如,分小時(shí)、天、月的UV數(shù)
參考:Hive窗口函數(shù)保姆級(jí)教程
-
grouping sets是一種將多個(gè)group by邏輯寫(xiě)在一個(gè)sql語(yǔ)句中的便利寫(xiě)法。等價(jià)于將不同維度的group by結(jié)果集進(jìn)行union all。
-
cube:根據(jù)group by的維度的所有組合進(jìn)行聚合。
-
rollup的使用:是cube的子集,以最左側(cè)的維度為主,從該維度進(jìn)行層級(jí)聚合。
五:窗口函數(shù)底層運(yùn)行原理
?todo
六:其他注意事項(xiàng)
參考:
一文讀懂SQL窗口函數(shù) - 墨天輪
SQL高級(jí)功能窗口函數(shù)及使用場(chǎng)景 - 知乎
SQL窗口函數(shù)不懂?五大應(yīng)用場(chǎng)景讓你一步到位_數(shù)據(jù)小斑馬的博客-CSDN博客
HIVE 窗口及分析函數(shù) 應(yīng)用場(chǎng)景_Data_IT_Farmer的博客-CSDN博客
Hive之窗口函數(shù)(一文搞懂)_For_dongyang的博客-CSDN博客_hive 窗口函數(shù)
Hive常用函數(shù)大全(二)(窗口函數(shù)、分析函數(shù)、增強(qiáng)group)_李國(guó)冬的博客-CSDN博客_常用窗口函數(shù)
總結(jié)
以上是生活随笔為你收集整理的hive窗口函数最全总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 在vue中安装axios库
- 下一篇: java使用hdf.jar_在HDFVi