成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SQL窗口函數是什么?漲見識了!

開發 后端
本文首先介紹窗口函數的定義及基本語法,之后將介紹在 DBMS 和大數據系統中是如何實現高效計算窗口函數的,包括窗口函數的優化、執行以及并行執行。

[[393570]]

 窗口函數(Window Function) 是 SQL2003 標準中定義的一項新特性,并在 SQL2011、SQL2016 中又加以完善,添加了若干處拓展。窗口函數不同于我們熟悉的普通函數和聚合函數,它為每行數據進行一次計算:輸入多行(一個窗口)、返回一個值。在報表等分析型查詢中,窗口函數能優雅地表達某些需求,發揮不可替代的作用。

本文首先介紹窗口函數的定義及基本語法,之后將介紹在 DBMS 和大數據系統中是如何實現高效計算窗口函數的,包括窗口函數的優化、執行以及并行執行。

什么是窗口函數?

窗口函數出現在 SELECT 子句的表達式列表中,它最顯著的特點就是 OVER 關鍵字。語法定義如下: 

  1. window_function (expression) OVER (  
  2.    [ PARTITION BY part_list ]  
  3.    [ ORDER BY order_list ]  
  4.    [ { ROWS | RANGE } BETWEEN frame_start AND frame_end ] ) 

其中包括以下可選項:

  •  PARTITION BY 表示將數據先按 part_list 進行分區
  •  ORDER BY 表示將各個分區內的數據按 order_list 進行排序

Figure 1. 窗口函數的基本概念

最后一項表示 Frame 的定義,即:當前窗口包含哪些數據?

  •  ROWS 選擇前后幾行,例如 ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING 表示往前 3 行到往后 3 行,一共 7 行數據(或小于 7 行,如果碰到了邊界)
  •  RANGE 選擇數據范圍,例如 RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING 表示所有值在 [c−3,c+3][c−3,c+3] 這個范圍內的行,cc 為當前行的值

Figure 2. Rows 窗口和 Range 窗口

邏輯語義上說,一個窗口函數的計算“過程”如下:

  •  按窗口定義,將所有輸入數據分區、再排序(如果需要的話)
  •  對每一行數據,計算它的 Frame 范圍
  •  將 Frame 內的行集合輸入窗口函數,計算結果填入當前行

舉個例子: 

  1. SELECT dealer_id, emp_name, sales,  
  2.        ROW_NUMBER() OVER (PARTITION BY dealer_id ORDER BY sales) AS rank,  
  3.        AVG(sales) OVER (PARTITION BY dealer_id) AS avgsales   
  4. FROM sales 

上述查詢中,rank 列表示在當前經銷商下,該雇員的銷售排名;avgsales 表示當前經銷商下所有雇員的平均銷售額。查詢結果如下: 

  1. +------------+-----------------+--------+------+---------------+  
  2. | dealer_id  | emp_name        | sales  | rank | avgsales      |  
  3. +------------+-----------------+--------+------+---------------+  
  4. | 1          | Raphael Hull    | 8227   | 1    | 14356         |  
  5. | 1          | Jack Salazar    | 9710   | 2    | 14356         |  
  6. | 1          | Ferris Brown    | 19745  | 3    | 14356         |  
  7. | 1          | Noel Meyer      | 19745  | 4    | 14356         |  
  8. | 2          | Haviva Montoya  | 9308   | 1    | 13924         |  
  9. | 2          | Beverly Lang    | 16233  | 2    | 13924         |  
  10. | 2          | Kameko French   | 16233  | 3    | 13924         |  
  11. | 3          | May Stout       | 9308   | 1    | 12368         |  
  12. | 3          | Abel Kim        | 12369  | 2    | 12368         |  
  13. | 3          | Ursa George     | 15427  | 3    | 12368         |  
  14. +------------+-----------------+--------+------+---------------+ 

注:語法中每個部分都是可選的:

  •  如果不指定 PARTITION BY,則不對數據進行分區;換句話說,所有數據看作同一個分區
  •  如果不指定 ORDER BY,則不對各分區做排序,通常用于那些順序無關的窗口函數,例如 SUM()
  •  如果不指定 Frame 子句,則默認采用以下的 Frame 定義:
    •   若不指定 ORDER BY,默認使用分區內所有行 RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    •   若指定了 ORDER BY,默認使用分區內第一行到當前值 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

最后,窗口函數可以分為以下 3 類:

  •  聚合(Aggregate):AVG(), COUNT(), MIN(), MAX(), SUM()...
  •  取值(Value):FIRST_VALUE(), LAST_VALUE(), LEAD(), LAG()...
  •  排序(Ranking):RANK(), DENSE_RANK(), ROW_NUMBER(), NTILE()...

受限于篇幅,本文不去探討各個窗口函數的含義。關注公眾號Java技術棧,在后臺回復:面試,可以獲取我整理的 MySQL 系列面試題和答案,非常齊全。

注:Frame 定義并非所有窗口函數都適用,比如 ROW_NUMBER()、RANK()、LEAD() 等。這些函數總是應用于整個分區,而非當前 Frame。

窗口函數 VS. 聚合函數

從聚合這個意義上出發,似乎窗口函數和 Group By 聚合函數都能做到同樣的事情。但是,它們之間的相似點也僅限于此了!這其中的關鍵區別在于:窗口函數僅僅只會將結果附加到當前的結果上,它不會對已有的行或列做任何修改。而 Group By 的做法完全不同:對于各個 Group 它僅僅會保留一行聚合結果。

有的讀者可能會問,加了窗口函數之后返回結果的順序明顯發生了變化,這不算一種修改嗎?因為 SQL 及關系代數都是以 multi-set 為基礎定義的,結果集本身并沒有順序可言,ORDER BY 僅僅是最終呈現結果的順序。

另一方面,從邏輯語義上說,SELECT 語句的各個部分可以看作是按以下順序“執行”的:

Figure 3. SQL 各部分的邏輯執行順序

注意到窗口函數的求值僅僅位于 ORDER BY 之前,而位于 SQL 的絕大部分之后。這也和窗口函數只附加、不修改的語義是呼應的——結果集在此時已經確定好了,再依此計算窗口函數。別再 select * 了,送你 12 個查詢技巧,推薦看下。

窗口函數的執行

窗口函數經典的執行方式分為排序和函數求值這 2 步。

Figure 4. 一個窗口函數的執行過程,通常分為排序和求值 2 步

窗口定義中的 PARTITION BY 和 ORDER BY 都很容易通過排序完成。例如,對于窗口 PARTITION BY a, b ORDER BY c, d,我們可以對輸入數據按 (a,b,c,d)(a,b,c,d) 或 (b,a,c,d)(b,a,c,d) 做排序,之后數據就排列成 Figure 1 中那樣了。

接下來考慮:如何處理 Frame?

  •  對于整個分區的 Frame(例如 RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),只要對整個分區計算一次即可,沒什么好說的;
  •  對于逐漸增長的 Frame(例如 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),可以用 Aggregator 維護累加的狀態,這也很容易實現;
  •  對于滑動的 Frame(例如 ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING)相對困難一些。一種經典的做法是要求 Aggregator 不僅支持增加還支持刪除(Removable),這可能比你想的要更復雜,例如考慮下 MAX() 的實現。

窗口函數的優化

對于窗口函數,優化器能做的優化有限。這里為了行文的完整性,仍然做一個簡要的說明。

通常,我們首先會把窗口函數從 Project 中抽取出來,成為一個獨立的算子稱之為 Window。

Figure 5. 窗口函數的優化過程

有時候,一個 SELECT 語句中包含多個窗口函數,它們的窗口定義(OVER 子句)可能相同、也可能不同。顯然,對于相同的窗口,完全沒必要再做一次分區和排序,我們可以將它們合并成一個 Window 算子。

對于不同的窗口,最樸素地,我們可以將其全部分成不同的 Window,如上圖所示。實際執行時,每個 Window 都需要先做一次排序,代價不小。

那是否可能利用一次排序計算多個窗口函數呢?某些情況下,這是可能的。例如本文例子中的 2 個窗口函數: 

  1. ... ROW_NUMBER() OVER (PARTITION BY dealer_id ORDER BY sales) AS rank,  
  2.     AVG(sales) OVER (PARTITION BY dealer_id) AS avgsales ... 

雖然這 2 個窗口并非完全一致,但是 AVG(sales) 不關心分區內的順序,完全可以復用 ROW_NUMBER() 的窗口。

窗口函數的并行執行

現代 DBMS 大多支持并行執行。對于窗口函數,由于各個分區之間的計算完全不相關,我們可以很容易地將各個分區分派給不同的節點(線程),從而達到分區間并行。

但是,如果窗口函數只有一個全局分區(無 PARTITION BY 子句),或者分區數量很少、不足以充分并行時,怎么辦呢?上文中我們提到的 Removable Aggregator 的技術顯然無法繼續使用了,它依賴于單個 Aggregator 的內部狀態,很難有效地并行起來。

TUM 的這篇論文中提出使用線段樹(Segment Tree)實現高效的分區內并行。線段樹是一個 N 叉樹數據結構,每個節點包含當前節點下的部分聚合結果。

下圖是一個使用二叉線段樹計算 SUM() 的例子。例如下圖中第三行的 1212,表示葉節點 5+75+7 的聚合結果;而它上方的 2525 表示葉節點 5+7+3+105+7+3+10 的聚合結果。

[[393574]]

Figure 6. 使用線段樹計算給定范圍的總和

假設當前 Frame 是第 2 到第 8 行,即需要計算 7+3+10+...+47+3+10+...+4 區間之和。有了線段樹以后,我們可以直接利用 7+13+207+13+20 (圖中紅色字體)計算出聚合結果。

線段樹可以在 O(nlogn)O(nlog⁡n) 時間內構造,并能在 O(logn)O(log⁡n) 時間內查詢任意區間的聚合結果。更棒的是,不僅查詢可以多線程并發互不干擾,而且線段樹的構造過程也能被很好地并行起來。

最后,關注公眾號Java技術棧,在后臺回復:面試,可以獲取我整理的 MySQL 系列面試題和答案,非常齊全。 

 

責任編輯:龐桂玉 來源: Java技術棧
相關推薦

2020-06-07 16:07:57

Python開發工具

2024-07-26 10:47:10

2023-07-19 12:09:07

MySQL窗口函數

2021-03-07 16:41:06

URLSpring5

2020-08-20 07:38:51

Java字符串整形

2021-01-07 16:50:36

SQL數據庫函數

2021-01-06 10:33:15

SQL數據庫函數

2025-05-19 00:02:45

SQL窗口函數

2023-11-09 14:47:51

SQL工具數據庫

2022-11-18 07:34:12

Docker項目目錄

2024-09-14 09:41:17

2024-07-30 09:01:12

2022-04-28 11:12:15

芯片

2017-07-12 09:46:00

5G社會網絡

2021-11-30 14:06:37

排序算法代碼

2022-02-06 11:35:53

SQL數據函數

2024-03-05 15:28:38

SQL窗口函數分頁查詢

2019-11-11 14:51:19

Java數據結構Properties

2009-09-04 05:34:57

KVM性能紅帽KVM

2011-06-27 08:35:28

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久精品视频在线免费观看 | 日韩免费网站 | 一区二区三区在线 | 久久涩涩| 久久伊人影院 | 日韩一区欧美一区 | 天啪| av免费看在线 | 福利一区在线观看 | 先锋影音资源网站 | 欧美成人手机在线 | 一区欧美 | 黄色毛片网站在线观看 | 黄色av网站在线观看 | 国产精品免费一区二区三区四区 | 成人影院一区二区三区 | 成人午夜激情 | 91精品中文字幕一区二区三区 | 亚洲精品久久久蜜桃网站 | 国产aa| 在线欧美视频 | 亚洲+变态+欧美+另类+精品 | 国产精品久久久久久久久久久免费看 | 国产一区二区精 | 一二区成人影院电影网 | 亚洲免费一区 | 亚洲精品国产第一综合99久久 | 一区二区中文 | h片免费在线观看 | 日韩伦理一区二区 | 久久久国产一区二区三区 | 中文字幕一区二区三区精彩视频 | www.成人.com | 国产精品久久久久久妇女 | 亚洲成人一区二区三区 | 亚洲欧美一区二区三区国产精品 | 日本视频在线播放 | 81精品国产乱码久久久久久 | 成人午夜免费福利视频 | 激情三区 | 中文字幕精品一区二区三区精品 |