OVER() 子句為分析函數指定:分區 (PARTITION BY)、排序 (ORDER BY)、窗口框架 (ROWS/RANGE BETWEEN)。它不改變結果集行數,而是為每行附加基於窗口的計算結果。
analytic_function([arguments]) OVER (
[PARTITION BY partition_expr [, ...]]
[ORDER BY order_expr [ASC|DESC] [NULLS FIRST|LAST] [, ...]]
[windowing_clause]
)
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ---------- --------- ----- -------- ----- ----- ------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
SELECT empno, ename, deptno, sal,
SUM(sal) OVER (PARTITION BY deptno) AS dept_sal_total
FROM scott.emp;
結果:每行顯示所屬部門薪資總和 (10:8750, 20:10875, 30:9400),不減少行數,對比 GROUP BY 僅輸出 3 行。
SELECT empno, ename, deptno, sal,
SUM(sal) OVER (PARTITION BY deptno ORDER BY sal) AS dept_running_total
FROM scott.emp;
結果:部門內按薪資遞增累加 (10: 1300→3750→8750)。ORDER BY 隱含窗口框架:RANGE UNBOUNDED PRECEDING AND CURRENT ROW (從分區起始到當前行)。
SELECT empno, sal, job,
RANK() OVER (PARTITION BY job ORDER BY sal DESC) AS job_sal_rank
FROM scott.emp;
結果:同職位內薪資降冪排名。並列同名次,跳過後續名次 (如兩筆 3000 同第 1,下一筆第 3)。
對比函數:
DENSE_RANK():並列同名次,不跳過 (1,1,2,3...)ROW_NUMBER():嚴格唯一遞增 (1,2,3,4...)| 框架子句 | 語義 | 典型用途 |
|---|---|---|
| ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW | 分區起始到當前行 (實體行數) | 累加總和、年初至今 |
| ROWS BETWEEN 2 PRECEDING AND CURRENT ROW | 前 2 行到當前行 | 3 日移動平均 |
| ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING | 整個分區 | 等同無 ORDER BY 的 PARTITION BY |
| RANGE BETWEEN ... (需 ORDER BY 數值/日期) | 基於值範圍而非行數 | 時間窗口:前 30 天、同月 |
SELECT empno, ename, hiredate, sal,
AVG(sal) OVER (
ORDER BY hiredate
RANGE BETWEEN INTERVAL '30' DAY PRECEDING AND CURRENT ROW
) AS mov_avg_30d
FROM scott.emp;
| 類別 | 函數 | 用途 |
|---|---|---|
| 彙總 | SUM, AVG, COUNT, MIN, MAX | 分組/累加/移動統計 |
| STDDEV, VARIANCE | 統計分布 | |
| 排序 | RANK, DENSE_RANK, ROW_NUMBER | Top-N、去重、分頁 |
| NTILE(n) | 分桶 (四分位數等) | |
| 值比較 | LAG, LEAD | 前後行比較、環比 |
| FIRST_VALUE, LAST_VALUE | 分區首/尾值 | |
| 分布 | PERCENT_RANK, CUME_DIST | 相對位置、百分位 |
| PERCENTILE_CONT, PERCENTILE_DISC | 中位數、分位數 |
SELECT * FROM (
SELECT empno, ename, deptno, sal,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal DESC) AS rn
FROM emp
) WHERE rn <= 3;
SELECT * FROM (
SELECT empno, ename, deptno, sal,
ROW_NUMBER() OVER (PARTITION BY empno ORDER BY hiredate DESC) AS rn
FROM emp
) WHERE rn = 1;
SELECT deptno, hiredate, sal,
LAG(sal) OVER (PARTITION BY deptno ORDER BY hiredate) AS prev_sal,
sal - LAG(sal) OVER (PARTITION BY deptno ORDER BY hiredate) AS diff
FROM emp;
SELECT deptno,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY sal) OVER (PARTITION BY deptno) AS median_sal
FROM emp;
PARTITION BY 縮小排序範圍,減少記憶體/臨時表空間壓力DBMS_RANDOM)ORDER BY 可能與窗口排序衝突,導致結果難以預期EXPLAIN PLAN 觀察 WINDOW SORT / WINDOW NOSORT 操作OVER() 將「群組計算」從「減少行數的 GROUP BY」解放為「保留行數的逐行附加」,是 SQL 從集合思維進階到窗口思維的關鍵橋樑。掌握 PARTITION BY、ORDER BY、窗口框架三要素,即可解決 90% 的報表統計、排名、環比、去重、分頁等進階需求。