SQL經典實例(附錄)窗口函數

窗口函數針對指定的行集合(分組)執行聚合運算。不一樣之處在於,窗口函數可以爲每一個分組返回多個值,而聚合函數只能返回單一值。聚合運算的對象實際上是一組行記錄,咱們稱之爲「窗口」(所以纔有了術語「窗口函數」)。在Oracle中成爲分析函數。sql

窗口操做

若是要計算整個公司的員工總數,傳統作法是執行count()*:函數

select count(*) from emp;

clipboard.png
可是有時候咱們可能須要從非聚合數據行或者從不一樣緯度的聚合數據行裏訪問這一類聚合運算結果。spa

select ename,
       deptno,
       count(*) over() as cnt
    from emp
order by 2;

clipboard.png

關鍵字 OVER 代表 COUNT 函數會做爲窗口函數來調用,而不是一次普通的聚合函數調用。3d

執行時機

這裏咱們爲前一節的查詢語句加上一個 WHERE 子句,以過濾掉 DEPTNO 等於 20和 30 的員工。code

select ename,
       deptno,
       count(*) over() as cnt
    from emp where deptno = 10
order by 2;

clipboard.png

該示例代表 WHERE 和 GROUP BY 這一類子句執行完以後,才輪到窗口函數執行。對象

分區

可使用 PARTITION BY 子句針對行數據進行分區(partition)或者分組(group),並根據其結果執行聚合運算。咱們在前面的示例中看到過,若是 OVER 關鍵字後面跟着一個空的圓括號,那麼窗口函數執行聚合運算時,會把該查詢結果集總體做爲一個分區來看待。所以,咱們不妨把 PARTITION BY 子句理解成「動態的 GROUP BY」,它不一樣於傳統的 GROUP BY,由於在最終的結果集中容許出現多種由 PARTITION BY 生成的分區。
考慮以下查詢語句:blog

select ename,
       deptno,
       count(*) over(partition by deptno) as cnt
    from emp 
order by 2;

clipboard.png

因爲使用了 PARTITION BY DEPTNO 子句,如今聚合函數 COUNT 會分別計算出每個部門的員工人數。排序

相較於傳統的 GROUP BY,PARTITION BY 子句的另外一個好處是,在同一個 SELECT 語句裏咱們能夠按照不一樣的列進行分區,並且不一樣的窗口函數調用之間互不影響。ip

以下所示的查詢,它會逐一列出全體員工,並返回每個人所屬的部門,所在部門的員工總數,每個人的職位,以及公司範圍內從事相同工做的員工總數。get

select ename,
       deptno,
       count(*) over(partition by deptno) as dept_cnt,
       job,
       count(*) over(partition by job) as job_cnt
    from emp 
order by 2;

clipboard.png

Null的影響

相似於 GROUP BY 子句,PARTITION BY 子句會把全部的 Null 納入同一個分區或者分組。
考慮以下查詢:

select coalesce(comm,-1) as comm,
    count(*)over(partition by comm) as cnt
from emp

clipboard.png
若是不用count()而是用count(comm)*則會有以下結果:

select coalesce(comm,-1) as comm,
    count(comm)over(partition by comm) as cnt
from emp

clipboard.png
聚合函數會忽略掉 NULL 值。

當使用 COUNT 函數時,咱們應該思考一下是否要把 Null 包括在內。使用COUNT(column) 會忽略 Null。若是但願把 NULL 值一併計入,則應該使用COUNT(*)。(此時咱們要計算的不是實際的列值,而是但願知道有多少行。)

排序

當在窗口函數的 OVER 子句中使用 ORDER BY 時,咱們其實是在決定兩件事:
(1) 分區內的行數據如何排序;
(2) 計算涉及哪些行數據。
咱們來看一下以下所示的查詢,該查詢計算出了 DEPTNO 等於 10 的員工的工資累計合計值。

select deptno,
       ename,
       hiredate, 
       sal,
       sum(sal) over(partition by deptno) as total1,
       sum(sal) over() as total2,
       sum(sal) over(order by hiredate) as running_total
    from emp
where deptno = 10;

這個查詢與下列查詢等價,使用range between...and顯式指定了order by hiredate默認行爲方式:

select deptno,
       ename,
       hiredate, 
       sal,
       sum(sal) over(partition by deptno) as total1,
       sum(sal) over() as total2,
       sum(sal) over(order by hiredate
                    range between unbounded preceding
                    and current row) as running_total
    from emp
where deptno = 10;

結果均爲:
clipboard.png
上述查詢中出現的 RANGE BETWEEN 子句在 ANSI 標準中被稱做 Framing 子句。Framing 子句能定義動態變化的「數據子窗口」,並將其融入聚合運算。
例如以下查詢語句:

select deptno,
       ename,
       hiredate, 
       sal,
       sum(sal) over(order by hiredate
                    range between unbounded preceding
                    and current row) as run_total1,
       sum(sal) over(order by hiredate
                    range between 1 preceding
                    and current row) as run_total2,
       sum(sal) over(order by hiredate
                    range between current row
                    and unbounded following) as run_total3,
       sum(sal) over(order by hiredate
                    range between current row
                    and 1 following) as run_total4
    from emp
where deptno = 10;

clipboard.png

select 
    ename,
    sal,
    min(sal)over(order by sal) min1,
    max(sal)over(order by sal) max1,
    min(sal)over(order by sal
                range between unbounded preceding
                and unbounded following) min2,
    max(sal)over(order by sal
                range between unbounded preceding
                and unbounded following) max2,
    min(sal)over(order by sal
                range between current row
                and current row) min3,
    max(sal)over(order by sal
                range between current row
                and current row) max3,
    max(sal)over(order by sal
                rows between 3 preceding
                and 3 following) max4
 from emp;

clipboard.png

《SQL經典實例》 附錄A
相關文章
相關標籤/搜索