分析函數核心語法
analytic_function([arguments]) OVER (
[PARTITION BY partition_expr [, ...]]
[ORDER BY order_expr [ASC|DESC] [NULLS FIRST|LAST] [, ...]]
[WINDOWING_CLAUSE]
)
四大排序型函數對比
| 函數 | 行號分配規則 | 並列處理 | 典型場景 |
| ROW_NUMBER() | 1,2,3,4... 連續唯一 | 任意打斷並列,保證唯一 | 分頁、去重保留首行、Top-N |
| RANK() | 1,2,2,4... 跳躍式 | 並列同名次,下一名次跳過 | 競賽排名、成績名次 |
| DENSE_RANK() | 1,2,2,3... 連續密集 | 並列同名次,下一名次不跳過 | 連續等級評定、薪資檔次 |
| ROWNUM | 偽列,結果集順序號 | 無排序保證,WHERE ROWNUM<=N 陷阱 | 簡單限制行數 (需配合子查詢排序) |
實戰範例:員工薪資排名
SELECT
deptno, ename, sal,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal DESC) AS rn,
RANK() OVER (PARTITION BY deptno ORDER BY sal DESC) AS rk,
DENSE_RANK() OVER (PARTITION BY deptno ORDER BY sal DESC) AS drk
FROM emp
ORDER BY deptno, sal DESC;
結果對比
| DEPTNO | ENAME | SAL | RN | RK | DRK |
| 10 | KING | 5000 | 1 | 1 | 1 |
| 10 | CLARK | 2450 | 2 | 2 | 2 |
| 10 | MILLER | 1300 | 3 | 3 | 3 |
| 20 | SCOTT | 3000 | 1 | 1 | 1 |
| 20 | FORD | 3000 | 2 | 1 | 1 |
| 20 | ADAMS | 1100 | 3 | 3 | 2 |
| 20 | SMITH | 800 | 4 | 4 | 3 |
| 20 | JONES | 2975 | 5 | 5 | 4 |
偏移型函數:LAG / LEAD
語法與參數
LAG(expr [, offset [, default]]) OVER (PARTITION BY ... ORDER BY ...)
LEAD(expr [, offset [, default]]) OVER (PARTITION BY ... ORDER BY ...)
- expr:要訪問的列/表達式
- offset:偏移行數 (默認 1),正數向前/向後
- default:超出分區邊界時返回值 (默認 NULL)
實戰:環比增長率計算
SELECT
deptno, ename, hiredate, sal,
LAG(sal) OVER (PARTITION BY deptno ORDER BY hiredate) AS prev_sal,
LEAD(sal) OVER (PARTITION BY deptno ORDER BY hiredate) AS next_sal,
ROUND((sal - LAG(sal) OVER (PARTITION BY deptno ORDER BY hiredate))
/ LAG(sal) OVER (PARTITION BY deptno ORDER BY hiredate) * 100, 2) AS growth_pct
FROM emp;
窗口框架子句
-- RANGE/ROWS BETWEEN start AND end
SUM(sal) OVER (
PARTITION BY deptno
ORDER BY hiredate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -- 累計
) AS cumulative_sal
AVG(sal) OVER (
PARTITION BY deptno
ORDER BY hiredate
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING -- 移動平均
) AS moving_avg
經典應用模式
1. 高效分頁查詢 (12c+ OFFSET/FETCH 替代方案)
SELECT * FROM (
SELECT e.*, ROW_NUMBER() OVER (ORDER BY empno) rn
FROM emp e
) WHERE rn BETWEEN 21 AND 30; -- 第3頁,每頁10行
2. 去重保留最新/最舊記錄
-- 保留每個 deptno 最新 hiredate 的記錄
DELETE FROM emp
WHERE rowid IN (
SELECT rowid FROM (
SELECT rowid,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY hiredate DESC) rn
FROM emp
) WHERE rn > 1
);
3. Top-N 每組查詢
-- 每個部門薪資前 3 名
SELECT * FROM (
SELECT e.*,
RANK() OVER (PARTITION BY deptno ORDER BY sal DESC) rk
FROM emp e
) WHERE rk <= 3;
4. 會話化/分組島問題
-- 識別連續登錄天數分組
SELECT
user_id, login_dt,
login_dt - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_dt) AS grp_flag
FROM user_logins
-- 相同 grp_flag 即為連續登錄組
5. 累計/移動統計
SELECT
order_dt, amount,
SUM(amount) OVER (ORDER BY order_dt ROWS UNBOUNDED PRECEDING) AS running_total,
AVG(amount) OVER (ORDER BY order_dt ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma_7day
FROM daily_sales;
性能優化關鍵點
- 索引支持:
PARTITION BY + ORDER BY 列創建複合索引可避免排序
- 減少分區數:大量微小分區增加窗口管理開銷
- 避免不必要的窗口框架:默認
RANGE UNBOUNDED PRECEDING 可能觸發磁盤排序,ROWS 通常更快
- 物化視圖預計算:高頻報表預先計算分析函數結果
版本支持矩陣
| 函數 | 引入版本 | 備註 |
| ROW_NUMBER/RANK/DENSE_RANK | 8i | 核心分析函數 |
| LAG/LEAD | 8i | 偏移訪問 |
| FIRST_VALUE/LAST_VALUE | 9i | 窗口邊界值 |
| NTILE | 9i | 分桶 |
| RATIO_TO_REPORT | 9i | 比率計算 |
| LISTAGG | 11gR2 | 字符串聚合 |
| APPROX_* | 12c+ | 近似計算函數 |