複雜報表、多階段 ETL、Session 級快取、避免鎖競爭——全域暫存表 (Global Temporary Table, GTT) 為這些場景提供了「資料隨 Session/Transaction 生滅、定義永久保留」的獨特機制。本文系統闡述 GTT 兩種模式、DDL 特性、動態創建模式、統計資訊管理、效能優勢與常見誤區。
-- 1. Transaction-Specific (預設): ON COMMIT DELETE ROWS
CREATE GLOBAL TEMPORARY TABLE gtt_txn (
id NUMBER,
process_id VARCHAR2(50),
payload CLOB
) ON COMMIT DELETE ROWS;
-- 2. Session-Specific: ON COMMIT PRESERVE ROWS
CREATE GLOBAL TEMPORARY TABLE gtt_sess (
session_id NUMBER,
step_name VARCHAR2(100),
result_json VARCHAR2(4000)
) ON COMMIT PRESERVE ROWS;
| 特性 | ON COMMIT DELETE ROWS (交易級) | ON COMMIT PRESERVE ROWS (會話級) |
|---|---|---|
| 資料可見性 | 僅當前交易可見 | 整個 Session 可見 |
| 資料清除時機 | COMMIT / ROLLBACK 瞬間截斷 | DISCONNECT Session 結束時截斷 |
| 適用場景 | 單一交易多步驟中繼結果、避免中間表鎖 | 跨交易、多步驟報表、Session 級快取、購物車 |
| 鎖行為 | 無鎖競爭 (私有資料) | 無鎖競爭 (私有資料) |
| 統計資訊 | 12c+ 自動 Session 級統計 | 12c+ 自動 Session 級統計 |
| 索引/約束 | 支援 (定義時建立),同樣 Session 私有 | 支援 |
| 外鍵參考 | ❌ 不可被永久表參考 | ❌ 不可被永久表參考 |
| 分區/LOB | ✅ 支援 (11g+) | ✅ 支援 |
場景:報表參數動態決定欄位、多租戶隔離、避免永久物件膨脹。
CREATE OR REPLACE PROCEDURE create_dyn_gtt (
p_table_name IN VARCHAR2,
p_cols IN SYS.ODCIVARCHAR2LIST, -- 欄位定義清單
p_preserve IN BOOLEAN DEFAULT FALSE -- TRUE=Session, FALSE=Transaction
) AS
v_sql VARCHAR2(4000);
v_cnt NUMBER;
BEGIN
-- 檢查是否已存在 (同 Session 內不可重複創建同名 GTT)
SELECT COUNT(*) INTO v_cnt FROM user_tables WHERE table_name = UPPER(p_table_name);
IF v_cnt = 0 THEN
v_sql := 'CREATE GLOBAL TEMPORARY TABLE ' || p_table_name || ' (';
FOR i IN 1..p_cols.COUNT LOOP
v_sql := v_sql || p_cols(i) || ',';
END LOOP;
v_sql := RTRIM(v_sql, ',') || ') ON COMMIT ' || CASE WHEN p_preserve THEN 'PRESERVE ROWS' ELSE 'DELETE ROWS' END;
EXECUTE IMMEDIATE v_sql;
END IF;
EXCEPTION
WHEN OTHERS THEN
-- ORA-00955: name already used by existing object (跨 Session 並發創建)
IF SQLCODE != -955 THEN RAISE; END IF;
END;
/
關鍵點:
USER_TABLES。PRIVATE TEMPORARY TABLE (ORA$PTT_*),定義也在 Session 級私有,更適合極度動態場景。| 版本 | 統計機制 | DBA 操作 |
|---|---|---|
| 11g 及之前 | 共享統計 (字典級),所有 Session 共用一組 | DBMS_STATS.GATHER_TABLE_STATS 收集後鎖定 (LOCK_TABLE_STATS) 防自動任務覆蓋 |
| 12c+ | Session 級私有統計 (Global Temporary Table Statistics) | 自動收集、自動維護、Session 隔離,無需人工干預 |
11g 避坑:若不同 Session 資料量差異巨大 (Session A 10 行, Session B 100 萬行),共享統計會導致其中一方計劃極差。解法:鎖定統計 + 動態採樣 (OPTIMIZER_DYNAMIC_SAMPLING=4) 或 Hint DYNAMIC_SAMPLING。
TRUNCATE (隱性於 Commit/Disconnect) 瞬間重置 HWM,後續全掃描極快。| 陷阱 | 癥狀 | 根因 | 解法 |
|---|---|---|---|
| 連接池復用導致資料殘留 | Session 級 GTT 資料洩漏給下一個業務請求 | 連接池不斷連,Session 不結束,ON COMMIT PRESERVE ROWS 不清理 | 1) 用 ON COMMIT DELETE ROWS 2) 業務結束時顯式 DELETE FROM gtt 3) 連接池驗證查詢執行清理 |
| 統計資訊導致計劃翻車 | 同一 SQL,Session A 走索引快,Session B 走全掃慢 | 11g 共享統計不反映 Session 實際資料量 | 升級 12c+;或鎖定統計 + 動態採樣;或 Hint 指定存取路徑 |
| 大量 GTT 併發導致 Temp 表空間爆滿 | ORA-01652: unable to extend temp segment | 並發 Session 各自佔用 Temp 空間,總和超限 | 監控 Temp 使用率、增加 Temp 檔案、優化 SQL 減少中間結果集、考慮分批提交 |
| 跨資料庫連結 使用 GTT | ORA-02066: missing or invalid NETWORK LINK | GTT 定義不可跨 DB Link 參考 | 遠端也建同名 GTT,或改用永久表 + 定期清理 |
| 匯出/遷移遺漏 GTT 定義 | 目標庫缺少 GTT,應用報錯 | EXP/IMP 預設不導出 GTT 資料 (資料本身不導),但定義需 ROWS=N 導出 | Data Pump INCLUDE=TABLE:\"LIKE 'GTT_%'\" 導出定義 |
-- 1. GTT 儲存並行 Worker 產生的中間結果
CREATE GLOBAL TEMPORARY TABLE gtt_parallel_stage (
worker_id NUMBER,
chunk_id NUMBER,
agg_result NUMBER
) ON COMMIT PRESERVE ROWS;
-- 2. 並行管線函數寫入 GTT
CREATE OR REPLACE FUNCTION process_chunk(p_chunk_id NUMBER) RETURN SYS_REFCURSOR PIPELINED PARALLEL_ENABLE (PARTITION p_chunk_id BY ANY) IS
BEGIN
-- 複雜計算...
INSERT INTO gtt_parallel_stage VALUES (DBMS_DEBUG_JDWP.CURRENT_SESSION_ID, p_chunk_id, v_result);
PIPE ROW(v_result);
END;
-- 3. 主查詢匯總 GTT
SELECT SUM(agg_result) FROM gtt_parallel_stage;
此模式將並行計算中間結果物質化於 GTT,避免多次重算,且無 Redo/鎖開銷。
GTT 是 Oracle 解決「臨時中間結果物質化」的原生利器:定義永久、資料私有、零 Redo、零鎖、HWM 秒重置。掌握兩種模式 (Transaction/Session)、統計資訊演進 (12c+ Session 級)、動態創建限制、Temp 空間容量規劃、連接池清理策略,即可在複雜報表、ETL、高併發業務中釋放永久表的鎖與 Redo 壓力,實現極致效能。