詳細說明在sql中窗口函數是什么,為什么需要窗口函數,有普通的聚合函數了那窗口函數的意義在哪,窗口函數的執行邏輯是什么,over中的字句是如何使用和理解的(是不是句句戳到你的痛點,哼哼~)
1.什么是窗口函數?
窗口函數(Window Functions)是SQL中的一種功能強大的工具,用于對查詢結果集中的每一行進行計算,而無需對數據進行分組(GROUP BY)。與聚合函數不同,窗口函數不會將行匯總到一條記錄中,而是保留原始行,并在此基礎上添加計算結果。窗口函數常用于排名、累積和、滑動窗口計算等。
2.為什么需要窗口函數(為什么普通的聚合函數不行)
2.1四點原因
按行計算而不改變行的結構:
?? ???? ?窗口函數能夠在不改變行結構的情況下對數據進行計算。與聚合函數(如SUM、AVG等)不同,窗口函數不會將多行數據聚合成一行,而是對每一行進行計算并返回結果,同時保留原始行。這在許多分析場景中非常有用,例如累積和、排名計算等。
支持復雜的分析計算:
?? ???? ?窗口函數可以進行復雜的分析計算,包括排名、累積和、滑動平均等。這些計算在財務分析、時間序列分析和數據挖掘中非常常見。例如,使用窗口函數可以計算每個銷售員的銷售排名、某產品在不同時間段的累積銷售量等。
靈活的分區和排序機制:
?? ???? ?窗口函數支持按分區和排序進行計算,提供了極大的靈活性。可以根據需要按特定列進行分區(PARTITION BY),并在每個分區內按某列排序(ORDER BY)。這使得可以在多個維度上進行復雜的數據分析。例如,可以按客戶ID和時間進行分區和排序,從而分析每個客戶在不同時間段的行為變化。
保持數據的上下文關系:
?? ???? ?窗口函數能夠保持數據的上下文關系。在進行累積計算或滑動窗口計算時,窗口函數能夠在當前行的基礎上考慮前后多行的數據。這在時間序列分析中尤為重要,例如計算滾動平均值、滾動總和等。
提高查詢的可讀性和維護性:
?? ???? ?使用窗口函數可以使SQL查詢更加簡潔和可讀。相比于嵌套的子查詢或復雜的JOIN操作,窗口函數提供了一種更直觀的方式來表達復雜的計算邏輯。這不僅提高了查詢的可讀性,還降低了維護的難度。
2.2舉幾個例子
例子1:計算每篇文章在每個時間點的累積觀看人數
SELECT artical_id,dt,SUM(diff) OVER (PARTITION BY artical_id ORDER BY dt ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_viewers
FROM combined
ORDER BY artical_id, dt;
在這個例子當中,窗口函數可以很方便的選擇包括當前行在內的其以前的所有行
例2:計算每個銷售員的銷售額排名
SELECT salesperson,sales,RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS sales_rank
FROM sales_data;
在這個例子當中,使用窗口函數做一個“開窗”,類似單獨拖出一個小窗口,在這個小窗口中選定一個標準對銷售員進行排序,在這個小窗口中,每一行數據都得到了自己的排名,然后帶著這個排名回到主查詢中;這個過程中,主查詢既沒有使用group by分組,也沒有order by來產生排名,也沒有使用子查詢,但是每一個銷售員卻得到了自己地區中的排名,這就是剛剛提到的對每一行進行計算并返回結果,同時保留原始行。
3.窗口函數的執行邏輯是什么(over怎么用)
只有了解了窗口函數的執行邏輯才能在復雜的查詢中正確運用窗口函數
3.1?生成基礎結果集
首先,SQL引擎會執行查詢語句中的FROM子句、WHERE子句、GROUP BY子句和HAVING子句,生成基礎結果集。這個結果集包含了所有符合條件的數據。所以窗口函數會在拿到所在主查詢的基礎結果集后才生效!
3.2 分區(Partitioning)
在執行窗口函數時,OVER子句中的PARTITION BY子句會將基礎結果集劃分為多個分區。每個分區獨立處理,窗口函數將在每個分區內單獨執行。如果沒有指定PARTITION BY,則視為一個整體分區。注意這個分區是在窗口內單獨做的,并沒有對主查詢中的結果或者說每一行產生影響。
3.3 排序(Ordering)
在每個分區內,根據OVER子句中的ORDER BY子句進行排序。排序定義了窗口函數的計算順序,確保計算按指定的順序進行。注意這個排序也是在窗口內單獨做的,并沒有對主查詢中的結果或者說每一行產生影響。
3.4 定義窗口幀(Frame)
窗口幀定義了窗口函數計算的行范圍。窗口幀由ROWS或RANGE子句指定。
從2、3中,不難看出,這有一個易混淆的點(也是本人混淆過的點),那就是over中的分區和排序 本質上 是在定義窗口函數的計算方式,和主查詢中的group by和order by(如果有的話)并無關系!!!!!
3.5 舉個例子
這個例子是在一個uid-用戶ID, artical_id-文章ID, in_time-進入時間, out_time-離開時間組成的表的基礎上,統計每篇文章同一時刻最大在看人數
SELECTartical_id,MAX(instant_viewer_cnt) max_uv
FROM (SELECTartical_id,dt,SUM(diff) OVER(PARTITION BY artical_id ORDER BY dt, diff DESC) instant_viewer_cntFROM (SELECT artical_id, in_time dt, 1 diffFROM tb_user_logWHERE artical_id != 0UNION ALLSELECT artical_id, out_time dt, -1 diffFROM tb_user_logWHERE artical_id != 0) t1 group byartical_id,dt
) t2
GROUP BY 1
ORDER BY 2 DESC
這里t1表是通過編碼操作和union操作生成的瞬時動作表,我們為了從t1的瞬時動作得到每個時刻的狀態表t2,所以需要窗口函數來做聚合,窗口函數中的order by只是定義sum的計算方式,如果要想主查詢中也是按照artical_id和dt的順序來呈現結果那么,是需要顯式地在真正的order by中寫出來的