oracle 之分析函式 over (partition by ...order by ...)
一:分析函式over
Oracle從8.1.6開始提供分析函式,分析函式用於計算基於組的某種聚合值,它和聚合函式的不同之處是對於每個組返回多行,而聚合函式對於每個組只返回一行。
1、分析函式和聚合函式的不同之處:
分析函式和聚合函式很多是同名的,意思也一樣,只是聚合函式用group by分組,每個分組返回一個統計值,而分析函式採用partition by分組,並且每組每行都可以返回一個統計值。簡單的說就是聚合函式返回統計結果,分析函式返回明細加統計結果。
(一)、分析函式語法:
FUNCTION_NAME(<argument>,<argument>...)
OVER
(<Partition-Clause><Order-by-Clause><Windowing Clause>)
例:(在oracle示例庫中演示,使用者scott)
select ename,sum(sal) over (partition by deptno order by ename) new_alias from emp;
a、sum就是函式名(FUNCTION_NAME)
b、(sal)是分析函式的引數,每個函式有0~3個引數,引數可以是表示式,例如:sum(sal+comm)
c、over 是一個關鍵字,用於標識分析函式,否則查詢分析器不能區別sum()聚集函式和sum()分析函式
d、partition by deptno (按相應的值(deptno)進行分組統計)是可選的分割槽子句,如果不存在任何分割槽子句,則全部的結果集可看作一個單一的大區
e、order by ename 是可選的order by 子句,有些函式需要它,有些則不需要.依靠已排序資料的那些函式。
即:分析函式帶有一個開窗函式over(),包含三個分析子句:
分組(partition by)
排序(order by)
視窗(rows)
示例1:
SELECT empno,ename,job,deptno, ----查詢基礎欄位 COUNT(*) over(PARTITION BY deptno) cnt_dept_man, --- 查詢部門人員數量 (等同於用部門deptno進行分組查詢) COUNT(*) over (PARTITION BY deptno ORDER BY empno) AS sum_dept_add, --- 查詢出的部門人員數依次為前一行的求和數加上當前行的行數(若未sum則會是逐行累加的資料) COUNT(*) over(PARTITION BY job) cnt_job_man , ---查詢崗位的的人員數量 (等同於用崗位job進行分組查詢) COUNT(*) over (PARTITION BY job ORDER BY empno) AS sum_job_add ---查詢出崗位人員(依次為前一行的求和數加上當前行的行數(若未sum則會是逐行累加的資料) FROM emp;
(二)、FUNCTION子句
ORACLE提供了N多個分析函式,按功能分5類
Oracle分析函式——函式列表
------------------------------------------------------------------------------------------------
SUM :該函式計算組中表達式的累積和
MIN :在一個組中的資料視窗中查詢表示式的最小值
MAX :在一個組中的資料視窗中查詢表示式的最大值
AVG :用於計算一個組和資料視窗內表示式的平均值。
COUNT :對一組內發生的事情進行累積計數
-------------------------------------------------------------------------------------------------
RANK :根據ORDER BY子句中表達式的值,從查詢返回的每一行,計算它們與其它行的相對位置
DENSE_RANK :根據ORDER BY子句中表達式的值,從查詢返回的每一行,計算它們與其它行的相對位置
FIRST :從DENSE_RANK返回的集合中取出排在最前面的一個值的行
LAST :從DENSE_RANK返回的集合中取出排在最後面的一個值的行
FIRST_VALUE :返回組中資料視窗的第一個值
LAST_VALUE :返回組中資料視窗的最後一個值。
LAG :可以訪問結果集中的其它行而不用進行自連線
LEAD :LEAD與LAG相反,LEAD可以訪問組中當前行之後的行
ROW_NUMBER :返回有序組中一行的偏移量,從而可用於按特定標準排序的行號
-------------------------------------------------------------------------------------------------
STDDEV :計算當前行關於組的標準偏離
STDDEV_POP :該函式計算總體標準偏離,並返回總體變數的平方根
STDDEV_SAMP :該函式計算累積樣本標準偏離,並返回總體變數的平方根
VAR_POP :該函式返回非空集合的總體變數(忽略null)
VAR_SAMP :該函式返回非空集合的樣本變數(忽略null)
VARIANCE :如果表示式中行數為1,則返回0,如果表示式中行數大於1,則返回VAR_SAMP
COVAR_POP :返回一對錶達式的總體協方差
COVAR_SAMP :返回一對錶達式的樣本協方差
CORR :返回一對錶達式的相關係數
-------------------------------------------------------------------------------------------------
CUME_DIST :計算一行在組中的相對位置
NTILE :將一個組分為"表示式"的散列表示
PERCENT_RANK :和CUME_DIST(累積分配)函式類似
PERCENTILE_DISC :返回一個與輸入的分佈百分比值相對應的資料值
PERCENTILE_CONT :返回一個與輸入的分佈百分比值相對應的資料值
RATIO_TO_REPORT :該函式計算expression/(sum(expression))的值,它給出相對於總數的百分比
REGR_ (Linear Regression) Functions :這些線性迴歸函式適合最小二乘法迴歸線,有9個不同的迴歸函式可使用
-------------------------------------------------------------------------------------------------
CUBE :按照OLAP的CUBE方式進行資料統計,即各個維度均需統計
ROLLUP :
-------------------------------------------------------------------------------------------------
示例2:查詢每個部門工資最高的員工資訊
1、(count,rank,dense_rank,row_number)排名函式的使用及注意事項
在使用排名函式的時候需要注意以下三點:
(1、排名函式必須有 OVER 子句。
(2、排名函式必須有包含 ORDER BY 的 OVER 子句。
(3、分組內從1開始排序。
-- 一般的寫法: SELECT E.ENAME, E.JOB, E.SAL MAXSAL , E.DEPTNO FROM SCOTT.EMP E, (SELECT E.DEPTNO, MAX(E.SAL) SAL FROM SCOTT.EMP E GROUP BY E.DEPTNO) ME WHERE E.DEPTNO = ME.DEPTNO AND E.SAL = ME.SAL; -- 分析函式OVER (使用count函式用order by將相應資料分組,獲取分組編號) SELECT ENAME,JOB,MAXSAL,DEPTNO FROM (SELECT ENAME,JOB,MAX(SAL) OVER (PARTITION BY DEPTNO) AS MAXSAL,DEPTNO, COUNT(*) OVER(PARTITION BY DEPTNO ORDER BY SAL DESC) AS NUM FROM EMP) WHERE NUM = 1; --析函式OVER (使用rank函式用order by將相應資料分組,獲取分組編號) SELECT E.ENAME,E.JOB,E.SAL,E.DEPTNO FROM (SELECT ENAME,JOB,SAL,RANK() OVER(PARTITION BY DEPTNO ORDER BY SAL DESC) RANK ,DEPTNO FROM EMP) E WHERE E.RANK = 1 AND NOT deptno IS NULL; --分析函式OVER (使用dense_rank函式用order by將相應資料分組,獲取分組編號) SELECT E.ENAME,E.JOB,E.SAL,E.DEPTNO FROM (SELECT ENAME,JOB,SAL,dense_RANK() OVER(PARTITION BY DEPTNO ORDER BY SAL DESC) RANK ,DEPTNO FROM EMP) E WHERE E.RANK = 1 AND NOT deptno IS NULL; --分析函式OVER (使用row_number函式用order by將相應資料分組,獲取分組編號) SELECT E.ENAME,E.JOB,E.SAL,E.DEPTNO FROM (SELECT ENAME,JOB,SAL,row_number() OVER(PARTITION BY DEPTNO ORDER BY SAL DESC) RANK ,DEPTNO FROM EMP) E WHERE E.RANK = 1 AND NOT deptno IS NULL;
注意事項:
一般寫法與分析函式的主要區別在於:使用分析函式可以提升sql的執行效率,一般寫法是通過兩個或多個表關聯來進行查詢(存在笛卡爾積),而用分析函式則所有的查詢都在一個表中實現,大大提升了sql的查詢效率(主要體現於自身表的關聯查詢)。
row_number的用途非常廣泛,排序最好用它,它會為查詢出來的每一行記錄生成一個序號,依次排序且不會重複,注意使用row_number函式時必須要用over子句選擇對某一列進行排序才能生成序號。
rank函式用於返回結果集的分割槽內每行的排名,行的排名是相關行之前的排名數加一。簡單來說rank函式就是對查詢出來的記錄進行排名,與row_number函式不同的是,rank函式考慮到了over子句中排序欄位值相同的情況,如果使用rank函式來生成序號,over子句中排序欄位值相同的序號是一樣的,後面欄位值不相同的序號將跳過相同的排名號排下一個,也就是相關行之前的排名數加一,可以理解為根據當前的記錄數生成序號,後面的記錄依此類推。
dense_rank函式的功能與rank函式類似,dense_rank函式在生成序號時是連續的,而rank函式生成的序號有可能不連續。dense_rank函數出現相同排名時,將不跳過相同排名號,rank值緊接上一次的rank值。在各個分組內,rank()是跳躍排序,有兩個第一名時接下來就是第三名,dense_rank()是連續排序,有兩個第一名時仍然跟著第二名。
count函式用於返回結果集的分割槽內每行的排名,行的排名是相關行之前的排名數加一,count()是跳躍排序,有兩個第一名時兩個第一名的序號都為2,就沒有第一名,有兩個第二名,接下來就是第三名,dense_rank()是連續排序,有兩個第一名時仍然跟著第二名。
示例3、查詢員工資訊的同時,查詢員工工資與所在部門最低、最高工資的差額
2、(min、max)取最值函式的使用及注意事項
--一般寫法
SELECT E.ENAME, E.JOB,E.SAL,E.DEPTNO,
ME.MIN_SAL MIN_SAL,
ME.MAX_SAL MAX_SAL, E.SAL - ME.MIN_SAL DIFF_MIN_SAL, ME.MAX_SAL - E.SAL DIFF_MAX_SAL FROM SCOTT.EMP E, (SELECT E.DEPTNO, MIN(E.SAL) MIN_SAL, MAX(E.SAL) MAX_SAL FROM SCOTT.EMP E GROUP BY E.DEPTNO) ME WHERE E.DEPTNO = ME.DEPTNO ORDER BY E.DEPTNO, E.SAL; --使用分析函式: SELECT E.ENAME, E.JOB,E.SAL,E.DEPTNO,
MIN(E.SAL) OVER(PARTITION BY E.DEPTNO) MIN_SAL, MAX(E.SAL) OVER(PARTITION BY E.DEPTNO) MAX_SAL, NVL(E.SAL - MIN(E.SAL) OVER(PARTITION BY E.DEPTNO), 0) DIFF_MIN_SAL, NVL(MAX(E.SAL) OVER(PARTITION BY E.DEPTNO) - E.SAL, 0) DIFF_MAX_SAL FROM EMP E; /*注:這裡沒有排序條件,若加上order by 排序條件, MAX() OVER(PARTITION BY .. ORDER BY .. DESC) 排序規則只能為desc,否則不起作用,將查詢到目前為止排序值最高欄位的對應值 MIN() OVER(PARTITION BY .. ORDER BY .. ASC ) 排序規則只能為asc,否則不起作用,將查詢到目前為止排序值最低的欄位的對應值, 如下:*/ SELECT E.ENAME, E.JOB,E.SAL,E.DEPTNO, MIN(E.SAL) OVER(PARTITION BY E.DEPTNO) MIN_SAL01, MAX(E.SAL) OVER(PARTITION BY E.DEPTNO) MAX_SAL01, MIN(E.SAL) OVER(PARTITION BY E.DEPTNO ORDER BY E.SAL) MIN_SAL02, MAX(E.SAL) OVER(PARTITION BY E.DEPTNO ORDER BY E.SAL) MAX_SAL02, --不起作用 MIN(E.SAL) OVER(PARTITION BY E.DEPTNO ORDER BY E.SAL DESC) MIN_SAL03, --不起作用 MAX(E.SAL) OVER(PARTITION BY E.DEPTNO ORDER BY E.SAL DESC) MAX_SAL03, MIN(E.SAL) OVER(PARTITION BY E.DEPTNO ORDER BY E.SAL ASC) MIN_SAL04, MAX(E.SAL) OVER(PARTITION BY E.DEPTNO ORDER BY E.SAL ASC) MAX_SAL04, --不起作用 NVL(E.SAL - MIN(E.SAL) OVER(PARTITION BY E.DEPTNO), 0) DIFF_MIN_SAL, NVL(MAX(E.SAL) OVER(PARTITION BY E.DEPTNO) - E.SAL, 0) DIFF_MAX_SAL FROM EMP E;