窗口函數針對指定的行集合(分組)執行聚合運算。不一樣之處在於,窗口函數可以爲每一個分組返回多個值,而聚合函數只能返回單一值。聚合運算的對象實際上是一組行記錄,咱們稱之爲「窗口」(所以纔有了術語「窗口函數」)。在Oracle中成爲分析函數。sql
若是要計算整個公司的員工總數,傳統作法是執行count()*:函數
select count(*) from emp;
可是有時候咱們可能須要從非聚合數據行或者從不一樣緯度的聚合數據行裏訪問這一類聚合運算結果。spa
select ename, deptno, count(*) over() as cnt from emp order by 2;
關鍵字 OVER 代表 COUNT 函數會做爲窗口函數來調用,而不是一次普通的聚合函數調用。3d
這裏咱們爲前一節的查詢語句加上一個 WHERE 子句,以過濾掉 DEPTNO 等於 20和 30 的員工。code
select ename, deptno, count(*) over() as cnt from emp where deptno = 10 order by 2;
該示例代表 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;
因爲使用了 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;
相似於 GROUP BY 子句,PARTITION BY 子句會把全部的 Null 納入同一個分區或者分組。
考慮以下查詢:
select coalesce(comm,-1) as comm, count(*)over(partition by comm) as cnt from emp
若是不用count()而是用count(comm)*則會有以下結果:
select coalesce(comm,-1) as comm, count(comm)over(partition by comm) as cnt from emp
聚合函數會忽略掉 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;
結果均爲:
上述查詢中出現的 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;
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;
《SQL經典實例》 附錄A