【Oracle】PRAGMA AUTONOMOUS_TRANSACTION完全ガイド|独立トランザクションで監査ログ・エラーログを確実に残す方法まで解説

「メインの処理がエラーでロールバックされても、エラーログだけは確実に残したい」「DML トリガーの中で別テーブルへの INSERT をコミットしたい」という場面で役立つのが PRAGMA AUTONOMOUS_TRANSACTION(自律型トランザクション)です。

通常のサブプログラム呼び出しはメインセッションのトランザクションを共有しますが、PRAGMA AUTONOMOUS_TRANSACTION を宣言すると、そのサブプログラムはメインセッションとは完全に独立した別のトランザクションとして動作します。

この記事でわかること

  • PRAGMA AUTONOMOUS_TRANSACTION の仕組みと通常のサブプログラム呼び出しとの違い
  • 宣言方法と COMMIT/ROLLBACK の必須ルール(ORA-06519)
  • エラーログテーブルへの書き込みパターン(EXCEPTION ハンドラでの活用)
  • DML トリガー内での活用(ミュータルテーブルの回避ではない点に注意)
  • PRAGMA AUTONOMOUS_TRANSACTION が使えないケースと注意事項
スポンサーリンク

PRAGMA AUTONOMOUS_TRANSACTION とは

Oracle のトランザクション制御において、通常のプロシージャ・ファンクション呼び出しは呼び出し元(メインセッション)のトランザクションに属します。PRAGMA AUTONOMOUS_TRANSACTION を宣言したサブプログラムは、呼び出し元のトランザクションを一時停止して独自のトランザクションを開始します。

項目 通常のサブプログラム PRAGMA AUTONOMOUS_TRANSACTION
トランザクションの帰属 呼び出し元のトランザクションを共有 独立した別トランザクション
COMMIT の影響 呼び出し元の変更もすべてコミットされる 自サブプログラム内の変更のみコミット
呼び出し元のROLLBACK後 サブプログラムの変更も消える 先にコミット済みなら消えない
主な用途 一般的なビジネスロジック 監査ログ・エラーログ・トリガー内の別テーブル更新
終了時の必須操作 なし(呼び出し元が管理) COMMIT または ROLLBACK が必須(省略すると ORA-06519)
独立トランザクションの動作イメージ
メインセッション: [BEGIN TX] → [INSERT A] → [CALL log_proc()] → [ROLLBACK]
独立TX(log_proc内): [INSERT LOG] → [COMMIT]
結果: INSERT A は消えるが、INSERT LOG は残る

PRAGMA AUTONOMOUS_TRANSACTION の宣言方法

PRAGMA AUTONOMOUS_TRANSACTION はプロシージャ・ファンクション・パッケージボディの宣言部(IS の後)に記述します。無名ブロックには宣言できません。

PRAGMA AUTONOMOUS_TRANSACTION の基本的な宣言
-- プロシージャの宣言部に記述する(IS の直後)
CREATE OR REPLACE PROCEDURE write_audit_log(
    p_table_name  IN VARCHAR2,
    p_action      IN VARCHAR2,
    p_user_name   IN VARCHAR2,
    p_record_id   IN NUMBER
) AS
    PRAGMA AUTONOMOUS_TRANSACTION;   -- ← 独立トランザクションとして動作させる
BEGIN
    INSERT INTO audit_log(
        log_id, table_name, action, changed_by, record_id, changed_at
    ) VALUES (
        audit_log_seq.NEXTVAL, p_table_name, p_action, p_user_name, p_record_id, SYSTIMESTAMP
    );

    COMMIT;   -- 独立トランザクション内で必ず COMMIT する
              -- 省略すると呼び出し元に戻った時点で ORA-06519 が発生する
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;   -- エラー時は ROLLBACK する(ORA-06519 防止)
        -- ログ書き込み失敗は握りつぶす(ログ失敗で本処理を妨げない)
END write_audit_log;
/
ORA-06519: active autonomous transaction detected and rolled back
PRAGMA AUTONOMOUS_TRANSACTION を宣言したサブプログラムが、COMMIT または ROLLBACK をせずに終了すると ORA-06519 が発生します。必ず全パス(正常系・例外系)でトランザクションを終了させてください。

エラーログテーブルへの書き込みパターン

最も代表的な使用例はエラーログの書き込みです。メインの処理がロールバックされても、エラーログだけはデータベースに残したい場面です。

エラーログテーブルを作成する
CREATE TABLE error_log (
    log_id       NUMBER         GENERATED ALWAYS AS IDENTITY,
    proc_name    VARCHAR2(100)  NOT NULL,
    error_code   NUMBER,
    error_msg    VARCHAR2(4000),
    error_stack  VARCHAR2(4000),
    created_at   TIMESTAMP      DEFAULT SYSTIMESTAMP,
    CONSTRAINT error_log_pk PRIMARY KEY (log_id)
);
エラーログ書き込みプロシージャ(PRAGMA AUTONOMOUS_TRANSACTION)
CREATE OR REPLACE PROCEDURE log_error(
    p_proc_name  IN VARCHAR2,
    p_error_code IN NUMBER   DEFAULT SQLCODE,
    p_error_msg  IN VARCHAR2 DEFAULT SQLERRM
) AS
    PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
    INSERT INTO error_log(proc_name, error_code, error_msg, error_stack)
    VALUES (
        p_proc_name,
        p_error_code,
        p_error_msg,
        SUBSTR(DBMS_UTILITY.FORMAT_ERROR_STACK
               || CHR(10) || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, 1, 4000)
    );
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        NULL;   -- ログ失敗は無視(本処理への影響を避ける)
END log_error;
/
エラーログを呼び出すメインプロシージャ
CREATE OR REPLACE PROCEDURE process_order(p_order_id IN NUMBER) AS
BEGIN
    -- メインの業務処理
    UPDATE orders SET status = 'PROCESSING' WHERE order_id = p_order_id;
    -- ... 他の処理 ...
    COMMIT;

EXCEPTION
    WHEN NO_DATA_FOUND THEN
        -- エラーログを書き込む(メインのROLLBACKとは独立してコミットされる)
        log_error(
            p_proc_name  => 'process_order',
            p_error_code => SQLCODE,
            p_error_msg  => 'order_id=' || p_order_id || ' が見つかりません'
        );
        ROLLBACK;   -- メインのトランザクションはロールバック
        RAISE;      -- 例外を再 RAISE する

    WHEN OTHERS THEN
        log_error(
            p_proc_name  => 'process_order',
            p_error_code => SQLCODE,
            p_error_msg  => SQLERRM
        );
        ROLLBACK;
        RAISE;
END process_order;
/

-- 実行例
BEGIN
    process_order(99999);   -- 存在しない order_id → ROLLBACK されるが error_log には記録される
EXCEPTION
    WHEN OTHERS THEN NULL;
END;
/

-- メインのロールバック後もエラーログは残っている
SELECT log_id, proc_name, error_code, error_msg, created_at
FROM error_log ORDER BY log_id DESC FETCH FIRST 5 ROWS ONLY;

DML トリガー内での PRAGMA AUTONOMOUS_TRANSACTION

DML トリガーの中でも独立トランザクションを使えます。たとえば「データ変更のたびに別の監査テーブルへ記録する」という要件に対応できます。

変更履歴を記録する DML トリガー
-- 変更履歴テーブル
CREATE TABLE employees_change_log (
    change_id    NUMBER GENERATED ALWAYS AS IDENTITY,
    emp_id       NUMBER,
    action_type  VARCHAR2(10),   -- INSERT / UPDATE / DELETE
    old_salary   NUMBER,
    new_salary   NUMBER,
    changed_by   VARCHAR2(100),
    changed_at   TIMESTAMP DEFAULT SYSTIMESTAMP,
    CONSTRAINT emp_change_log_pk PRIMARY KEY (change_id)
);

-- 独立トランザクションを使った変更履歴トリガー
CREATE OR REPLACE TRIGGER trg_employees_audit
    AFTER INSERT OR UPDATE OR DELETE ON employees
    FOR EACH ROW
DECLARE
    PRAGMA AUTONOMOUS_TRANSACTION;
    v_action VARCHAR2(10);
BEGIN
    IF INSERTING THEN
        v_action := 'INSERT';
    ELSIF UPDATING THEN
        v_action := 'UPDATE';
    ELSE
        v_action := 'DELETE';
    END IF;

    INSERT INTO employees_change_log(emp_id, action_type, old_salary, new_salary, changed_by)
    VALUES (
        COALESCE(:NEW.employee_id, :OLD.employee_id),
        v_action,
        :OLD.salary,
        :NEW.salary,
        SYS_CONTEXT('USERENV', 'SESSION_USER')
    );

    COMMIT;   -- トリガー内でも COMMIT が必要
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
END trg_employees_audit;
/
ミュータルテーブルエラー(ORA-04091)の回避には使えない
PRAGMA AUTONOMOUS_TRANSACTION はトリガー発火元テーブルへの参照・変更(ORA-04091)を解決しません。独立トランザクションから元テーブルへ SELECT しても、メインセッションの未コミット変更は見えないため整合性が崩れます。ORA-04091 の回避には コンパウンドトリガーを検討してください。

パッケージでの PRAGMA AUTONOMOUS_TRANSACTION

パッケージ内のプロシージャ・ファンクションにも宣言できます。宣言はパッケージボディ側の定義部に記述します(パッケージ仕様部には書かない)。

パッケージボディでの PRAGMA AUTONOMOUS_TRANSACTION
-- パッケージ仕様部(PRAGMA は書かない)
CREATE OR REPLACE PACKAGE audit_pkg AS
    PROCEDURE log_dml(
        p_table_name IN VARCHAR2,
        p_action     IN VARCHAR2,
        p_row_count  IN NUMBER DEFAULT NULL
    );
END audit_pkg;
/

-- パッケージボディ(ここに PRAGMA を書く)
CREATE OR REPLACE PACKAGE BODY audit_pkg AS
    PROCEDURE log_dml(
        p_table_name IN VARCHAR2,
        p_action     IN VARCHAR2,
        p_row_count  IN NUMBER DEFAULT NULL
    ) AS
        PRAGMA AUTONOMOUS_TRANSACTION;   -- ボディ側の宣言部に記述
    BEGIN
        INSERT INTO audit_log(table_name, action, row_count, logged_by, logged_at)
        VALUES (p_table_name, p_action, p_row_count,
                SYS_CONTEXT('USERENV', 'SESSION_USER'), SYSTIMESTAMP);
        COMMIT;
    EXCEPTION
        WHEN OTHERS THEN ROLLBACK;
    END log_dml;
END audit_pkg;
/

PRAGMA AUTONOMOUS_TRANSACTION の注意事項

独立トランザクションからメインの未コミット変更は見えない
-- メインセッション
BEGIN
    INSERT INTO test_table(id, val) VALUES (1, 'test');
    -- ← まだ COMMIT していない

    -- 独立トランザクションから見ると、上の INSERT はまだ見えない
    check_proc();   -- PRAGMA AUTONOMOUS_TRANSACTION で宣言されたプロシージャ
    -- check_proc 内で SELECT * FROM test_table WHERE id=1 → 0件になる

    COMMIT;
END;
/
-- 独立トランザクションは「コミット済みのデータのみ」を参照する
-- メインセッションの未コミット変更は独立トランザクションから見えない
-- これによりデータ不整合が起きないよう設計する必要がある
自律型トランザクションのネスト(最大 4 レベル)
-- Oracle では自律型トランザクションを最大 4 レベルまでネストできる
-- 5 レベル目以降は ORA-06519 ではなく ORA-04026 が発生する可能性がある
-- セッションあたりの独立トランザクション数の上限は SESSIONS パラメータに依存する

-- パラメータ確認
SELECT name, value FROM V$PARAMETER
WHERE name IN ('sessions', 'transactions');
-- TRANSACTIONS = SESSIONS × 1.1 が目安
PRAGMA AUTONOMOUS_TRANSACTION を使うべき場面・避けるべき場面
推奨される場面:監査ログ・エラーログ・バッチ進捗テーブルへの書き込み(メインのロールバックで消えてはいけない記録)
避けるべき場面:業務データの更新(メインTXと独立するため整合性が崩れやすい)、ミュータルテーブルエラーの回避策(根本解決にならない)、呼び出し頻度が非常に高いループ内(セッションリソースを余分に消費する)

まとめ

  • PRAGMA AUTONOMOUS_TRANSACTION:プロシージャ・ファンクション・トリガーの宣言部に記述する。メインセッションとは独立したトランザクションで動作する
  • COMMIT/ROLLBACK は必須:サブプログラム終了時に未コミットのまま戻ると ORA-06519 が発生する。例外ハンドラでも必ず ROLLBACK を呼ぶ
  • エラーログパターン:EXCEPTION ハンドラで log_error() を呼び、メインのロールバック後もログを残す定番イディオム
  • 独立トランザクションからは未コミット変更が見えない:設計時にこの特性を意識し、整合性を崩さないようにする
  • ミュータルテーブルエラーの回避には使えない:ORA-04091 の解決策ではないため、コンパウンドトリガーを検討する

例外処理の詳細は 例外処理完全ガイドを参照してください。トランザクション管理の基礎は トランザクション完全ガイドも参照してください。