グローバル一時表(GTT: Global Temporary Table)は、テーブル定義は全セッションで共有しながら、格納されたデータは各セッションにのみ見えるというOracle 独自のオブジェクトです。バッチ処理の中間データ・複雑な集計の一時保持・セッションをまたがない作業領域として広く使われます。
この記事でわかること
- GTT の2種類:ON COMMIT DELETE ROWS(トランザクション)と ON COMMIT PRESERVE ROWS(セッション)
- セッション間でデータが見えない仕組みと実務上の注意点
- GTT のインデックス作成とセッションローカルな統計情報の設定
- 通常テーブルより undo/redo 生成が少ないパフォーマンス特性
- WITH句・サブクエリ・ワークテーブルとの使い分け
- DDL(TRUNCATE・DROP)の挙動
- バッチ処理・ETL でよく使う実務パターン
グローバル一時表(GTT)とは
GTT は CREATE GLOBAL TEMPORARY TABLE で作成する特殊なテーブルで、以下の特性を持ちます。
- 定義は全セッションで共有:テーブル定義は永続的に保存される(DROP するまで消えない)
- データはセッション(またはトランザクション)ごとに分離:自分のセッションが INSERT したデータは他セッションから見えない
- 自動削除:セッション終了時(または COMMIT 時)に自動でデータが削除される
- undo/redo 生成が少ない:一時セグメントに書き込むため、通常テーブルよりロールバック情報が少ない
「グローバル」という名前はテーブル定義がグローバル(全員共有)という意味であり、データがグローバルに見えるという意味ではありません。データの可視範囲はあくまでセッションローカルです。
2種類の GTT:ON COMMIT DELETE ROWS と ON COMMIT PRESERVE ROWS
| オプション | データの生存スコープ | COMMIT 時の動作 | セッション終了時 |
|---|---|---|---|
| ON COMMIT DELETE ROWS(デフォルト) | トランザクション単位 | 全データが自動削除される | データ削除(未コミット分も) |
| ON COMMIT PRESERVE ROWS | セッション単位 | データは保持される | 全データが自動削除される |
ON COMMIT DELETE ROWS:トランザクションスコープの GTT
-- ON COMMIT DELETE ROWS: COMMIT または ROLLBACK でデータが削除される
-- ストアドプロシージャ内の一時計算結果を保持し、プロシージャ終了と同時に消したい場合に使う
CREATE GLOBAL TEMPORARY TABLE gtt_calc_work (
row_id NUMBER,
base_val NUMBER,
result_val NUMBER
) ON COMMIT DELETE ROWS; -- デフォルト。省略可
-- 使い方:
BEGIN
-- トランザクション内でデータを INSERT
INSERT INTO gtt_calc_work VALUES (1, 100, NULL);
INSERT INTO gtt_calc_work VALUES (2, 200, NULL);
-- 計算処理
UPDATE gtt_calc_work SET result_val = base_val * 1.1;
-- 最終結果を別テーブルに反映
INSERT INTO final_results SELECT result_val FROM gtt_calc_work;
COMMIT; -- ← ここで gtt_calc_work のデータが自動削除される
-- COMMIT 後、gtt_calc_work は空
SELECT COUNT(*) FROM gtt_calc_work; -- 0件
END;
/
ON COMMIT PRESERVE ROWS:セッションスコープの GTT
-- ON COMMIT PRESERVE ROWS: セッションが続く限りデータが保持される
-- 複数のトランザクションにまたがる中間データを蓄積したい場合に使う
CREATE GLOBAL TEMPORARY TABLE gtt_session_work (
step_no NUMBER,
step_name VARCHAR2(100),
processed NUMBER DEFAULT 0
) ON COMMIT PRESERVE ROWS;
-- セッション内で複数の COMMIT をまたいでデータが残る
INSERT INTO gtt_session_work VALUES (1, 'データ取込', 0);
COMMIT; -- ← データは削除されない
INSERT INTO gtt_session_work VALUES (2, '変換処理', 0);
COMMIT; -- ← データは削除されない
SELECT COUNT(*) FROM gtt_session_work; -- 2件(保持されている)
-- セッションを切断した瞬間にデータが全削除される(次回接続時は0件)
セッション間のデータ分離
GTT に INSERT したデータは自分のセッションからしか見えません。同じ GTT に別セッションが INSERT しても、お互いのデータは見えないため、セッションを意識したロック競合が発生しません。
セッション間の分離の確認
-- セッション A が INSERT -- セッション A: INSERT INTO gtt_session_work VALUES (99, 'セッションAのデータ', 0); SELECT COUNT(*) FROM gtt_session_work; -- 1件(セッションAには見える) -- セッション B(同時接続): SELECT COUNT(*) FROM gtt_session_work; -- 0件(セッションBには見えない) -- → GTT は行レベルのロックも発生しない(データが独立しているため) -- 実務上の利点: -- バッチ処理を複数セッションで並列実行しても、それぞれが独立した作業領域を持てる
GTT へのロック競合は発生しない
通常のテーブルでは UPDATE 時に行ロックが発生し、他セッションが待機することがあります。GTT はデータが各セッション専用のため、複数のバッチジョブが同じ GTT 名を使っても互いに待機せず並列実行できます。高負荷なバッチを並列化する際のワークテーブルとして特に有効です。
通常のテーブルでは UPDATE 時に行ロックが発生し、他セッションが待機することがあります。GTT はデータが各セッション専用のため、複数のバッチジョブが同じ GTT 名を使っても互いに待機せず並列実行できます。高負荷なバッチを並列化する際のワークテーブルとして特に有効です。
GTT のインデックスと統計情報
GTT にインデックスを作成する
-- GTT にも通常のテーブルと同様にインデックスを作成できる -- インデックスの定義は共有されるが、インデックスエントリ(データ)はセッション分離される CREATE INDEX idx_gtt_work_row ON gtt_session_work (step_no); CREATE INDEX idx_gtt_work_step ON gtt_session_work (step_name, processed); -- インデックスはデータと同様に COMMIT または セッション終了で自動削除される -- → 毎回 CREATE INDEX は不要。一度作ればテーブル定義と共に永続化される
GTT の統計情報をセッション単位で設定する
-- GTT はデータがセッションごとに変わるため、GATHER_TABLE_STATS が困難
-- DBMS_STATS.SET_TABLE_STATS で行数・ブロック数を手動設定するのが実務的
BEGIN
DBMS_STATS.SET_TABLE_STATS(
ownname => USER,
tabname => 'GTT_SESSION_WORK',
numrows => 10000, -- 予想される行数
numblks => 100 -- 予想ブロック数
);
END;
/
-- → オプティマイザが GTT の行数を正しく見積もれるようになる
-- ※ Oracle 12c 以降では SESSION スコープの統計(セッションごとに自動収集)も利用可能
-- Oracle 12c 以降: GTT の統計をセッション単位で自動収集する設定
-- (GLOBAL_TEMP_TABLE_STATS を SESSION に設定すると、セッション内のデータに基づいて統計が更新される)
BEGIN
DBMS_STATS.SET_TABLE_PREFS(
ownname => USER,
tabname => 'GTT_SESSION_WORK',
pname => 'GLOBAL_TEMP_TABLE_STATS',
pval => 'SESSION' -- SESSION(デフォルト)または SHARED を指定
);
END;
/
パフォーマンス特性:undo/redo が少ない理由
GTT は通常テーブルと異なり、一時セグメント(Temp Segment)に書き込まれます。
| 特性 | 通常テーブル | GTT |
|---|---|---|
| 書き込み先 | undo 付きのデータブロック | 一時セグメント(undo 大幅削減) |
| redo ログ | 生成される | 大幅に削減される |
| undo 生成 | ROLLBACK 用に生成 | 最小限(構造変更のみ) |
| ROLLBACK コスト | undo から復元するため重い | 一時セグメントの破棄のみで軽い |
| COMMIT コスト | redo をフラッシュ | 削減される |
大量データを一時的に処理する場合、通常テーブルに書いて最後に DELETE するよりも GTT を使う方がundo 表領域の消費と redo ログの生成を大幅に抑えられます。
GTT の DDL(TRUNCATE・ALTER・DROP)
GTT に対する DDL の動作
-- TRUNCATE: 自セッションのデータのみ削除(他セッションのデータは影響なし) TRUNCATE TABLE gtt_session_work; -- DROP TABLE: テーブル定義ごと削除(全セッションのデータが失われる) -- 他セッションが使用中でも DROP は実行できる -- → 実行中のバッチがある場合は DROP しないよう注意 DROP TABLE gtt_session_work; -- ALTER TABLE: 列追加などのスキーマ変更は通常テーブルと同様に可能 -- ただし他セッションがデータを持っている状態での ALTER は影響を与える可能性がある ALTER TABLE gtt_calc_work ADD (calc_status VARCHAR2(20) DEFAULT 'PENDING');
GTT・WITH句・ワークテーブルの使い分け
| 方法 | 用途・特徴 | 推奨するケース |
|---|---|---|
| WITH句(CTE) | SQL 内の中間結果。クエリが終わると消える | 1つのクエリ内で使いまわす中間計算 |
| GTT(ON COMMIT DELETE ROWS) | トランザクション内で複数の DML に使いまわす | ストアドプロシージャで複数の UPDATE にまたがる中間データ |
| GTT(ON COMMIT PRESERVE ROWS) | セッション全体で蓄積・参照する | 複数のバッチステップをまたぐ大量データの一時保持 |
| 通常テーブル(ワークテーブル) | 複数セッション間でデータを共有する必要がある | バッチ間の引き継ぎデータ・エラー明細の永続保存 |
実務パターン:バッチ処理中間データの一時保持
ETL バッチで GTT を使う実務パターン
-- ステップ1:受注データ受け取り用 GTT(ON COMMIT PRESERVE ROWS でバッチ全体で保持)
CREATE GLOBAL TEMPORARY TABLE gtt_incoming_orders (
order_id NUMBER,
customer_id NUMBER,
amount NUMBER,
status VARCHAR2(20),
err_msg VARCHAR2(500)
) ON COMMIT PRESERVE ROWS;
-- ステップ2:バッチプロシージャ内での利用
CREATE OR REPLACE PROCEDURE batch_process_orders AS
v_err_count NUMBER := 0;
BEGIN
-- 外部表からデータを GTT に取り込む
INSERT INTO gtt_incoming_orders (order_id, customer_id, amount, status)
SELECT order_id, customer_id, amount, 'PENDING'
FROM ext_orders_staging; -- 外部ファイルから読み込んだデータ
COMMIT;
-- バリデーション:金額が負のものをエラーに
UPDATE gtt_incoming_orders
SET status = 'ERROR',
err_msg = '金額が0以下です'
WHERE amount <= 0;
COMMIT;
-- 正常データのみ本テーブルに登録
INSERT INTO orders (order_id, customer_id, amount)
SELECT order_id, customer_id, amount
FROM gtt_incoming_orders
WHERE status = 'PENDING';
-- エラー件数確認
SELECT COUNT(*) INTO v_err_count
FROM gtt_incoming_orders WHERE status = 'ERROR';
COMMIT;
IF v_err_count > 0 THEN
-- エラー明細を永続テーブルにコピー(GTT はセッション終了で消えるため)
INSERT INTO order_errors
SELECT order_id, err_msg, SYSDATE FROM gtt_incoming_orders WHERE status = 'ERROR';
COMMIT;
END IF;
DBMS_OUTPUT.PUT_LINE('処理完了: エラー' || v_err_count || '件');
-- セッション終了時に gtt_incoming_orders は自動削除される
END;
/
よくある誤解と注意点
- 「テンポラリ表領域が必要」は誤り:GTT のデータはデフォルトのテンポラリ表領域に格納されるため、専用の CREATE TEMPORARY TABLESPACE は不要です。通常の TEMP 表領域が使われます
- テーブル定義は永続的:GTT は定義を DROP するまで残ります。データだけが自動削除されます
- フラッシュバック・クエリは使えない:GTT は undo に書き込まれないため、AS OF TIMESTAMP での過去参照はできません
- 分散トランザクション(DBLINK)では制限あり:GTT をリモートの DB リンク経由で参照することはできません
- エラー明細は別テーブルに保存:GTT のデータはセッション終了で消えます。エラー情報など後で確認が必要なデータは永続テーブルに COMMIT しておく必要があります
まとめ
- ON COMMIT DELETE ROWS:COMMIT でデータ削除。ストアドプロシージャ内の一時計算に最適
- ON COMMIT PRESERVE ROWS:セッション終了でデータ削除。バッチ全体の中間データ保持に最適
- セッション分離:自セッションのデータのみ見える。並列バッチでロック競合なし
- パフォーマンス:undo/redo 生成が少なく、大量データの一時処理で効果的
- 統計情報:DBMS_STATS.SET_TABLE_STATS で行数を手動設定して実行計画を安定させる
- エラーデータは永続テーブルへ:GTT のデータはセッション終了で消えるため、確認が必要なデータは永続化する
GTT の中間データを集計・整形するにはサブクエリ完全ガイドのインラインビューや WITH句(CTE)も組み合わせると効果的です。大量データ処理での undo 表領域の肥大化が問題になる場合はTEMP表領域の肥大化の原因とクリーンアップ方法も参照してください。

