Oracle 8.1.6引入分析函數徹底改變了複雜報表的編寫方式:原本需要自連接、子查詢、臨時表、甚至存儲過程才能實現的邏輯,如今一條OVER()子句即可優雅解決。本文系統梳理6大類核心分析函數,配合真實業務數據集實戰演示。
BILL_MONTH AREA_CODE NET_TYPE LOCAL_FARE
---------- --------- -------- ----------
200405 5761 G 7393344.04
200405 5761 J 5667089.85
200405 5762 G 6315075.96
200405 5762 J 6328716.15
... (共36行,跨3個月、5個區域、2種網絡類型)
生成「月-區域-網絡」三維度的全彙總報表:明細行 + 月小計 + 區域小計 + 網絡小計 + 總計。
SELECT bill_month, area_code, net_type, SUM(local_fare)
FROM t GROUP BY bill_month, area_code, net_type
UNION ALL
SELECT bill_month, area_code, NULL, SUM(local_fare)
FROM t GROUP BY bill_month, area_code
UNION ALL ... -- 共7個GROUP BY
SELECT
bill_month, area_code, net_type,
SUM(local_fare) AS fare_sum,
GROUPING_ID(bill_month, area_code, net_type) AS grp_id -- 識別彙總層級
FROM t
GROUP BY ROLLUP (bill_month, area_code, net_type)
ORDER BY bill_month, area_code, net_type;
ROLLUP:層次化彙總(月→區域→網絡→總計),N+1行;CUBE:全排列彙總(所有維度組合),2^N行;GROUPING SETS:自定義彙總組合,靈活控制輸出行數;GROUPING(col) / GROUPING_ID(...):區分「業務NULL」與「彙總NULL」。各月各區域下,按收入排名Top 3的網絡類型;處理並列排名業務語義差異。
SELECT
bill_month, area_code, net_type, local_fare,
RANK() OVER (PARTITION BY bill_month, area_code ORDER BY local_fare DESC) AS rnk,
DENSE_RANK() OVER (PARTITION BY bill_month, area_code ORDER BY local_fare DESC) AS drnk,
ROW_NUMBER() OVER (PARTITION BY bill_month, area_code ORDER BY local_fare DESC) AS rn
FROM t;
| 函數 | 並列值處理 | 排名序列 | 典型場景 |
|---|---|---|---|
| RANK | 跳過名次(1,1,3) | 不連續 | 競賽排名、考試名次 |
| DENSE_RANK | 不跳過(1,1,2) | 連續 | 薪資等級、客戶分級 |
| ROW_NUMBER | 強制唯一(1,2,3) | 連續唯一 | 分頁、去重取第一行 |
計算各區域各網絡類型的「環比增長率」:(本月 - 上月) / 上月。
SELECT
bill_month, area_code, net_type, local_fare,
LAG(local_fare) OVER (PARTITION BY area_code, net_type ORDER BY bill_month) AS prev_fare,
ROUND((local_fare - LAG(local_fare) OVER (...)) /
NULLIF(LAG(local_fare) OVER (...), 0) * 100, 2) AS mom_pct
FROM t
ORDER BY area_code, net_type, bill_month;
LAG(local_fare, 1) OVER (...) AS prev_1m, -- 上月
LAG(local_fare, 3) OVER (...) AS prev_3m, -- 上季
LAG(local_fare, 12) OVER (...) AS prev_12m -- 同比
計算各區域「3個月移動平均收入」,平滑季節性波動。
SELECT
bill_month, area_code, net_type, local_fare,
AVG(local_fare) OVER (
PARTITION BY area_code, net_type
ORDER BY bill_month
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW -- 含當前行共3個月
) AS mov_avg_3m,
SUM(local_fare) OVER (
PARTITION BY area_code, net_type
ORDER BY bill_month
RANGE BETWEEN INTERVAL '2' MONTH PRECEDING AND CURRENT ROW -- 語義窗口
) AS mov_sum_3m_range
FROM t;
ROWS:物理行數窗口,確定性強,性能優;RANGE:邏輯值範圍窗口,支持日期/數值間隔語義,但要求ORDER BY列唯一,否則可能產生非確定性結果。計算各網絡類型在區域月收入中的佔比。
SELECT
bill_month, area_code, net_type, local_fare,
ROUND(RATIO_TO_REPORT(local_fare) OVER (PARTITION BY bill_month, area_code) * 100, 2) AS pct_of_area
FROM t;
local_fare / SUM(local_fare) OVER (PARTITION BY bill_month, area_code)
查詢各區域「最早月份」和「最新月份」的收入值(而非彙總)。
SELECT
area_code,
MIN(bill_month) KEEP (DENSE_RANK FIRST ORDER BY bill_month) AS first_month,
MAX(local_fare) KEEP (DENSE_RANK FIRST ORDER BY bill_month) AS first_fare,
MIN(bill_month) KEEP (DENSE_RANK LAST ORDER BY bill_month) AS last_month,
MAX(local_fare) KEEP (DENSE_RANK LAST ORDER BY bill_month) AS last_fare
FROM t
GROUP BY area_code;
PARTITION BY列應包含表分區鍵,實現分區級並行;ORDER BY列與索引順序一致,優化器可跳過SORT操作(WINDOW NOSORT);ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING等價於全分區排序,慎用。分析函數將「行間計算、多維彙總、佔比排名」等複雜邏輯封裝為聲明式SQL,不僅代碼量減少80%,更通過優化器深度整合實現了極致性能。建議建立分析函數模式庫,將常見報表模式(如YoY、MoM、移動平均、Top N、佔比)標準化為可復用SQL模板。