【Oracle】トリガー情報を取得するSQL文|USER/ALL/DBA_TRIGGERS・作成・有効化・実務パターンまで完全解説

Oracleのトリガー(Trigger)は、テーブルやビューに対する操作(INSERT / UPDATE / DELETE)が発生したとき、自動的に実行されるPL/SQLプログラムです。監査ログの記録、データ整合性の保証、自動採番など、実務で幅広く使われます。

この記事では、トリガー情報の確認方法を中心に、3つのデータディクショナリビューの使い分けから、作成・有効化/無効化・削除・実務パターン・エラー対処まで、現場で必要な知識を体系的に解説します。

この記事でわかること

  • USER_TRIGGERS / ALL_TRIGGERS / DBA_TRIGGERS違いと使い分け
  • トリガーの一覧取得・詳細確認のSQL
  • USER_SOURCE によるソースコード取得
  • トリガーの有効/無効の確認ALTER TRIGGER ENABLE/DISABLE
  • CREATE TRIGGER の基本構文(BEFORE/AFTER、行レベル/文レベル)
  • :NEW / :OLD 疑似レコードの使い方
  • INSTEAD OF トリガー(ビュー用)の仕組み
  • DROP TRIGGER による削除方法
  • 他RDBMS比較(MySQL、PostgreSQL、SQL Server)
  • よくあるエラーと対処法(ORA-04091 等)
  • 実務パターン(監査ログ、自動採番、論理削除フラグ)
スポンサーリンク

トリガーとは(基本概念)

トリガーは、特定のイベント(INSERT / UPDATE / DELETE)がテーブルやビューに対して発生したとき、Oracleが自動的に起動するPL/SQLプログラムです。アプリケーション側でロジックを書かなくても、データベースレベルで処理を強制できるため、データの整合性保証監査に適しています。

トリガーの動作イメージ
-- ユーザーがINSERTを実行
INSERT INTO employees (emp_id, emp_name) VALUES (100, '山田太郎');

-- ↓ Oracleが自動的にトリガーを起動
-- ↓ 例:監査テーブルに操作ログを自動記録
-- ↓ 例:自動採番カラムに値を自動セット

トリガーの主な用途は以下のとおりです。

用途 説明
監査ログ 誰が・いつ・何を変更したかを自動記録 更新前後の値を監査テーブルに INSERT
データ整合性 CHECK制約では表現できない複雑な検証 他テーブルを参照する整合性チェック
自動採番 シーケンスから主キーを自動セット INSERT前にシーケンスのNEXTVALを代入
派生カラム更新 他のカラムから計算値を自動設定 更新日時の自動セット
ビューの更新 更新不可ビューに対するDMLを実現 INSTEAD OFトリガーで基底テーブルを更新

トリガー情報を確認する3つのビュー

Oracleには、トリガー情報を確認するための3つのデータディクショナリビューが用意されています。権限レベルに応じて使い分けます。

ビュー名 表示範囲 必要権限 主な使用場面
USER_TRIGGERS 自分が所有するトリガーのみ 不要(自スキーマ) 開発者が自分のトリガーを確認
ALL_TRIGGERS アクセス可能な全トリガー 不要 他スキーマのトリガーも含めて確認
DBA_TRIGGERS データベース上の全トリガー SELECT ANY DICTIONARY DBAの管理作業・棚卸し

ポイント

この3ビューの命名規則は、USER_TABLES / ALL_TABLES / DBA_TABLES と同じパターンです。Oracle のデータディクショナリビューは USER_ → ALL_ → DBA_ の順で参照範囲が広がります。

USER_TRIGGERS — 自分のトリガー一覧

SQL
-- 自分が所有するトリガーの一覧を取得
SELECT
    trigger_name,       -- トリガー名
    trigger_type,       -- BEFORE EACH ROW / AFTER STATEMENT 等
    triggering_event,   -- INSERT / UPDATE / DELETE
    table_name,         -- 対象テーブル名
    status              -- ENABLED / DISABLED
FROM
    user_triggers
ORDER BY
    table_name, trigger_name;

実行結果(例)

TRIGGER_NAME          TRIGGER_TYPE         TRIGGERING_EVENT  TABLE_NAME   STATUS
--------------------- -------------------- ----------------- ------------ --------
TRG_EMP_AUDIT         AFTER EACH ROW       INSERT OR UPDATE  EMPLOYEES    ENABLED
TRG_EMP_SEQ           BEFORE EACH ROW      INSERT            EMPLOYEES    ENABLED
TRG_ORDER_CHECK       BEFORE EACH ROW      INSERT            ORDERS       DISABLED

USER_TRIGGERS は特別な権限なしで参照できるため、開発者が最もよく使うビューです。OWNER カラムは存在せず、すべて自スキーマのトリガーが返ります。

ALL_TRIGGERS — アクセス可能な全トリガー

SQL
-- アクセス可能な全トリガー(他スキーマ含む)
SELECT
    owner,              -- 所有者
    trigger_name,
    trigger_type,
    triggering_event,
    table_owner,        -- 対象テーブルの所有者
    table_name,
    status
FROM
    all_triggers
WHERE
    owner = 'HR'      -- スキーマ名で絞り込み
ORDER BY
    owner, table_name, trigger_name;

ALL_TRIGGERS には OWNER カラムがあり、他ユーザーが所有するトリガーも表示されます。複数スキーマにまたがるシステムで、関連トリガーを横断的に確認する場合に便利です。

DBA_TRIGGERS — 全ユーザーの全トリガー(DBA向け)

SQL
-- データベース上のすべてのトリガー(DBA権限が必要)
SELECT
    owner,
    trigger_name,
    trigger_type,
    triggering_event,
    table_name,
    status
FROM
    dba_triggers
WHERE
    owner NOT IN ('SYS', 'SYSTEM')  -- システムトリガーを除外
ORDER BY
    owner, table_name, trigger_name;

注意:DBA_TRIGGERS には SELECT ANY DICTIONARY または SELECT_CATALOG_ROLE が必要です。権限がないと ORA-00942: table or view does not exist が発生します。

特定テーブルのトリガーを確認する

実務では「このテーブルにどんなトリガーが付いているか」を確認するケースが最も多いです。WHERE table_name = 'テーブル名' で絞り込みます。

SQL
-- 特定テーブルのトリガー一覧(詳細情報付き)
SELECT
    trigger_name,
    trigger_type,
    triggering_event,
    status,
    description        -- トリガーの宣言部(CREATE TRIGGER ...)
FROM
    user_triggers
WHERE
    table_name = 'EMPLOYEES'
ORDER BY
    trigger_name;

注意:テーブル名はデータディクショナリ内では大文字で格納されています。WHERE table_name = 'employees' ではヒットしないため、'EMPLOYEES' と大文字で指定するか UPPER() を使ってください。

トリガーのソースコード取得(USER_SOURCE)

トリガーの実装内容(PL/SQLのソースコード)は USER_SOURCE(または ALL_SOURCE / DBA_SOURCE)から取得できます。

SQL
-- トリガーのソースコードを行番号順に取得
SELECT
    line,       -- 行番号
    text        -- ソースコード
FROM
    user_source
WHERE
    name = 'TRG_EMP_AUDIT'
    AND type = 'TRIGGER'
ORDER BY
    line;

実行結果(例)

LINE  TEXT
----- --------------------------------------------------
    1 TRG_EMP_AUDIT
    2 AFTER INSERT OR UPDATE ON employees
    3 FOR EACH ROW
    4 BEGIN
    5   INSERT INTO emp_audit_log (
    6     action_date, action_type, emp_id, old_name, new_name
    7   ) VALUES (
    8     SYSDATE, 'UPDATE', :NEW.emp_id, :OLD.emp_name, :NEW.emp_name
    9   );
   10 END;

また、USER_TRIGGERSTRIGGER_BODY カラムでもソースを取得できますが、LONG型のためSQLで直接扱いにくい場合があります。USER_SOURCE を使う方法がお勧めです。

SQL(DBMS_METADATAを使う方法)
-- DDL全体を取得(CREATE TRIGGER 文を再構成)
SELECT
    DBMS_METADATA.GET_DDL('TRIGGER', 'TRG_EMP_AUDIT') AS ddl_text
FROM
    dual;

DBMS_METADATA.GET_DDL を使うと、CREATE TRIGGER 文を完全な形で取得できます。別環境への移行やバックアップ用のスクリプト生成に便利です。

トリガーの有効/無効を確認・変更する

ステータスの確認

USER_TRIGGERSSTATUS カラムで、トリガーが ENABLED(有効)DISABLED(無効) かを確認できます。

SQL
-- 無効化されているトリガーを一覧表示
SELECT
    trigger_name,
    table_name,
    status
FROM
    user_triggers
WHERE
    status = 'DISABLED'
ORDER BY
    table_name;

ALTER TRIGGER による有効化/無効化

大量データのロード時やメンテナンス時に、一時的にトリガーを無効化するケースがあります。

SQL
-- 個別のトリガーを無効化
ALTER TRIGGER trg_emp_audit DISABLE;

-- 個別のトリガーを有効化
ALTER TRIGGER trg_emp_audit ENABLE;

-- テーブルの全トリガーを一括無効化
ALTER TABLE employees DISABLE ALL TRIGGERS;

-- テーブルの全トリガーを一括有効化
ALTER TABLE employees ENABLE ALL TRIGGERS;

注意:トリガーを無効化したまま戻し忘れると、監査ログが記録されない・整合性チェックが効かないなどの問題が起きます。必ず作業後に再有効化してください。

CREATE TRIGGER の基本構文

トリガーの作成には CREATE TRIGGER 文を使います。基本構文は以下のとおりです。

基本構文
CREATE [OR REPLACE] TRIGGER トリガー名
    {BEFORE | AFTER}                -- 実行タイミング
    {INSERT | UPDATE [OF 列名] | DELETE}  -- 対象イベント
    [OR {INSERT | UPDATE | DELETE}]  -- 複数イベント指定可
    ON テーブル名
    [FOR EACH ROW]              -- 行レベルトリガー
    [WHEN (条件)]               -- 起動条件(オプション)
[DECLARE]
    -- 変数宣言(オプション)
BEGIN
    -- PL/SQL処理
END;

BEFORE / AFTER の違い

タイミング 実行時点 :NEW の変更 主な用途
BEFORE DML実行 可能(値を変更できる) 自動採番、デフォルト値セット、入力値チェック
AFTER DML実行 不可(参照のみ) 監査ログ記録、他テーブルへの反映

BEFOREトリガーの例(自動採番)

SQL
CREATE OR REPLACE TRIGGER trg_emp_seq
    BEFORE INSERT ON employees
    FOR EACH ROW
BEGIN
    -- シーケンスから主キーを自動採番
    IF :NEW.emp_id IS NULL THEN
        :NEW.emp_id := emp_seq.NEXTVAL;
    END IF;
END;

AFTERトリガーの例(監査ログ)

SQL
CREATE OR REPLACE TRIGGER trg_emp_audit
    AFTER INSERT OR UPDATE OR DELETE ON employees
    FOR EACH ROW
DECLARE
    v_action VARCHAR2(10);
BEGIN
    CASE
        WHEN INSERTING THEN v_action := 'INSERT';
        WHEN UPDATING  THEN v_action := 'UPDATE';
        WHEN DELETING  THEN v_action := 'DELETE';
    END CASE;

    INSERT INTO emp_audit_log (
        audit_date, action_type, emp_id,
        old_emp_name, new_emp_name, changed_by
    ) VALUES (
        SYSDATE, v_action, NVL(:NEW.emp_id, :OLD.emp_id),
        :OLD.emp_name, :NEW.emp_name, USER
    );
END;

行レベルトリガー vs 文レベルトリガー

トリガーには行レベル(FOR EACH ROW)文レベル(FOR EACH ROW なし)の2種類があります。

種類 実行回数 :NEW / :OLD 主な用途
行レベル
FOR EACH ROW
影響を受ける行ごとに1回 使用可能 各行の値チェック、監査ログ、自動採番
文レベル
(FOR EACH ROW なし)
DML文ごとに1回だけ 使用不可 処理完了通知、集計テーブルの更新
文レベルトリガーの例
-- DML文の実行後に1回だけ起動
CREATE OR REPLACE TRIGGER trg_emp_statement
    AFTER INSERT ON employees
    -- FOR EACH ROW を省略 → 文レベル
BEGIN
    -- 例:処理完了ログをテーブルに記録
    INSERT INTO operation_log (log_date, message)
    VALUES (SYSDATE, 'EMPLOYEESテーブルにINSERTが実行されました');
END;

実行順序の整理

1つのDML文に対して、トリガーは以下の順序で起動します。
BEFORE文レベル → (行ごとに) BEFORE行レベル → DML実行 → AFTER行レベル → AFTER文レベル

:NEW / :OLD 疑似レコード

行レベルトリガーでは、:NEW:OLD という疑似レコードを使って、変更前後の行データにアクセスできます。

イベント :OLD :NEW
INSERT NULL(挿入前はデータなし) 挿入される値
UPDATE 更新の値 更新の値
DELETE 削除される値 NULL(削除後はデータなし)
:NEW / :OLD の使用例
CREATE OR REPLACE TRIGGER trg_salary_check
    BEFORE UPDATE OF salary ON employees
    FOR EACH ROW
BEGIN
    -- 給与の減額を禁止する
    IF :NEW.salary < :OLD.salary THEN
        RAISE_APPLICATION_ERROR(
            -20001,
            '給与の減額はできません(旧: ' || :OLD.salary ||
            ' → 新: ' || :NEW.salary || ')'
        );
    END IF;
END;

ポイント:WHEN 句内では : を付けずに NEW.salary / OLD.salary と記述します。BEGIN ... END 内では :NEW / :OLD とコロンを付けるのがルールです。

INSTEAD OF トリガー(ビュー用)

INSTEAD OF トリガーは、ビューに対する INSERT / UPDATE / DELETE を横取りし、代わりに基底テーブルへの操作を実行するための特殊なトリガーです。複数テーブルを結合したビューなど、通常は更新できないビューに対して DML を可能にします。

SQL
-- 結合ビューの作成
CREATE OR REPLACE VIEW v_emp_dept AS
SELECT
    e.emp_id, e.emp_name, e.salary,
    d.dept_id, d.dept_name
FROM
    employees e
    JOIN departments d ON e.dept_id = d.dept_id;

-- INSTEAD OFトリガーでビューへのINSERTを実現
CREATE OR REPLACE TRIGGER trg_v_emp_dept_insert
    INSTEAD OF INSERT ON v_emp_dept
    FOR EACH ROW
BEGIN
    -- 基底テーブル(employees)にINSERT
    INSERT INTO employees (emp_id, emp_name, salary, dept_id)
    VALUES (:NEW.emp_id, :NEW.emp_name, :NEW.salary, :NEW.dept_id);
END;

INSTEAD OFトリガーの特徴

  • ビューにのみ使用可(テーブルには使えない)
  • 常に行レベル(FOR EACH ROW)で動作
  • BEFORE / AFTER ではなくINSTEAD OFを指定
  • ビューへのDMLを「横取り」して、基底テーブルを直接操作する

トリガーの削除(DROP TRIGGER)

不要になったトリガーは DROP TRIGGER で削除できます。

SQL
-- トリガーの削除
DROP TRIGGER trg_emp_audit;

-- 他スキーマのトリガーを削除(DROP ANY TRIGGER 権限が必要)
DROP TRIGGER hr.trg_emp_audit;

注意:テーブルを DROP TABLE すると、そのテーブルに紐づくトリガーも自動的に削除されます。一方、トリガーを DROP してもテーブル本体には影響しません。

削除ではなく一時的に停止したい場合は、前述の ALTER TRIGGER ... DISABLE を使います。削除すると定義自体が失われるため、再作成が必要になります。

他RDBMSとの比較

トリガーの仕様はRDBMSごとに異なります。Oracle経験者が他のDBを扱う場合や、逆にMySQLからOracle移行する場合の参考にしてください。

機能 Oracle MySQL PostgreSQL SQL Server
BEFORE / AFTER 両方 両方 両方 AFTERのみ(INSTEAD OFあり)
INSTEAD OF ビューのみ 非対応 ビューのみ(ルールも可) テーブル・ビュー両方
行レベル / 文レベル 両方 行レベルのみ 両方 文レベルのみ
変更前後の参照 :NEW / :OLD NEW / OLD NEW / OLD inserted / deleted テーブル
言語 PL/SQL SQL(制限あり) PL/pgSQL(関数経由) T-SQL
1テーブルに複数 可能 イベント+タイミングごとに1つ 可能 可能

PostgreSQLのトリガー

PostgreSQLではトリガー本体を直接書けず、まずトリガー関数を作成し、それを CREATE TRIGGER で紐づける2段構えになります。Oracle とは構文が大きく異なるので注意してください。

よくあるエラーと対処法

エラー 原因 対処法
ORA-04091
mutating table
行レベルトリガー内でトリガーの対象テーブル自体を SELECT / DML した コンパウンドトリガー、パッケージ変数、文レベルトリガーで対応
ORA-04098
trigger is invalid and failed re-validation
トリガーが依存するオブジェクト(テーブル、シーケンスなど)が変更・削除された ALTER TRIGGER ... COMPILE で再コンパイル
ORA-04082
NEW or OLD references not allowed in table level triggers
文レベルトリガーで :NEW / :OLD を使った FOR EACH ROW を追加して行レベルにする
ORA-00942
table or view does not exist
DBA_TRIGGERS の参照権限がない DBA に SELECT ANY DICTIONARY を付与してもらう、または USER_TRIGGERS を使う
ORA-04084
cannot change NEW values for this trigger type
AFTER トリガーで :NEW の値を変更しようとした BEFORE トリガーに変更する

ORA-04091 ミューテーティングテーブル の詳細

ORA-04091 はOracleトリガーで最も遭遇しやすいエラーです。行レベルトリガーの中から、そのトリガーが定義されたテーブル自体を参照すると発生します。

エラーが発生するパターン
-- NG: 行レベルトリガー内で自テーブルを参照 → ORA-04091
CREATE OR REPLACE TRIGGER trg_check_count
    BEFORE INSERT ON employees
    FOR EACH ROW
DECLARE
    v_count NUMBER;
BEGIN
    -- employees テーブル自体を SELECT → ミューテーティングエラー!
    SELECT COUNT(*) INTO v_count FROM employees;
    IF v_count >= 1000 THEN
        RAISE_APPLICATION_ERROR(-20002, '社員数の上限に達しました');
    END IF;
END;
コンパウンドトリガーによる解決
-- OK: コンパウンドトリガーで回避(Oracle 11g以降)
CREATE OR REPLACE TRIGGER trg_check_count
    FOR INSERT ON employees
    COMPOUND TRIGGER

    v_count NUMBER;

    -- 文の実行前に件数を取得
    BEFORE STATEMENT IS
    BEGIN
        SELECT COUNT(*) INTO v_count FROM employees;
    END BEFORE STATEMENT;

    -- 各行のINSERT前にチェック
    BEFORE EACH ROW IS
    BEGIN
        v_count := v_count + 1;
        IF v_count > 1000 THEN
            RAISE_APPLICATION_ERROR(-20002, '社員数の上限に達しました');
        END IF;
    END BEFORE EACH ROW;

END trg_check_count;

実務パターン

パターン1:監査ログの自動記録

誰が・いつ・何を変更したかを自動記録する、最も一般的なトリガーパターンです。

監査テーブル + トリガー
-- 監査テーブルの作成
CREATE TABLE emp_audit_log (
    audit_id     NUMBER GENERATED ALWAYS AS IDENTITY,
    audit_date   TIMESTAMP DEFAULT SYSTIMESTAMP,
    action_type  VARCHAR2(10),   -- INSERT / UPDATE / DELETE
    emp_id       NUMBER,
    old_salary   NUMBER,
    new_salary   NUMBER,
    changed_by   VARCHAR2(30) DEFAULT USER
);

-- 監査トリガーの作成
CREATE OR REPLACE TRIGGER trg_emp_salary_audit
    AFTER UPDATE OF salary ON employees
    FOR EACH ROW
    WHEN (OLD.salary <> NEW.salary)  -- 変更があった場合のみ
BEGIN
    INSERT INTO emp_audit_log (
        action_type, emp_id, old_salary, new_salary
    ) VALUES (
        'UPDATE', :NEW.emp_id, :OLD.salary, :NEW.salary
    );
END;

パターン2:更新日時の自動セット

SQL
CREATE OR REPLACE TRIGGER trg_emp_updated_at
    BEFORE UPDATE ON employees
    FOR EACH ROW
BEGIN
    :NEW.updated_at := SYSTIMESTAMP;
    :NEW.updated_by := USER;
END;

アプリケーション側で updated_at をセットし忘れても、トリガーが自動的に最新のタイムスタンプと更新者を設定します。

パターン3:論理削除フラグの自動セット

SQL
-- 物理削除を論理削除に変換するINSTEAD OFトリガー
CREATE OR REPLACE VIEW v_active_employees AS
SELECT * FROM employees WHERE deleted_flag = 'N';

CREATE OR REPLACE TRIGGER trg_soft_delete
    INSTEAD OF DELETE ON v_active_employees
    FOR EACH ROW
BEGIN
    -- 物理DELETEせず、フラグを更新
    UPDATE employees
    SET
        deleted_flag = 'Y',
        deleted_at   = SYSTIMESTAMP,
        deleted_by   = USER
    WHERE
        emp_id = :OLD.emp_id;
END;

パターン4:自動採番(12c以降はIDENTITY推奨)

SQL
-- シーケンス + トリガーによる自動採番(11g以前の定番パターン)
CREATE SEQUENCE seq_order_id START WITH 1 INCREMENT BY 1;

CREATE OR REPLACE TRIGGER trg_order_seq
    BEFORE INSERT ON orders
    FOR EACH ROW
BEGIN
    :NEW.order_id := seq_order_id.NEXTVAL;
END;

ポイント:Oracle 12c 以降では GENERATED ALWAYS AS IDENTITY カラムが使えるため、自動採番のためだけにトリガーを作る必要はなくなりました。レガシーシステムの保守で見かけるパターンとして覚えておきましょう。

トリガー管理のベストプラクティス

項目 推奨
命名規則 TRG_テーブル名_目的(例: TRG_EMP_AUDIT)で統一する
処理は最小限に 重い処理はキューやジョブに委譲し、トリガーは軽量に保つ
COMMIT / ROLLBACK禁止 DMLトリガー内で COMMIT / ROLLBACK は記述できない(自律型トランザクションを除く)
依存関係の管理 テーブル変更後に ALTER TRIGGER ... COMPILE で再コンパイルを忘れない
テスト INSERT / UPDATE / DELETE それぞれの操作で期待どおり動くか確認する
ドキュメント トリガーの目的・影響範囲をコメントとして記述する

逆引きリファレンス

やりたいことからSQLを素早く引けるようにまとめます。

やりたいこと SQL / コマンド
自分のトリガー一覧を見たい SELECT * FROM USER_TRIGGERS
特定テーブルのトリガーを確認 SELECT * FROM USER_TRIGGERS WHERE table_name = 'テーブル名'
ソースコードを表示したい SELECT text FROM USER_SOURCE WHERE name = '名前' AND type = 'TRIGGER' ORDER BY line
DDL全体を取得したい SELECT DBMS_METADATA.GET_DDL('TRIGGER', '名前') FROM dual
無効なトリガーを見つけたい SELECT * FROM USER_TRIGGERS WHERE status = 'DISABLED'
トリガーを一時停止したい ALTER TRIGGER トリガー名 DISABLE
テーブルの全トリガーを停止 ALTER TABLE テーブル名 DISABLE ALL TRIGGERS
トリガーを再有効化したい ALTER TRIGGER トリガー名 ENABLE
トリガーを削除したい DROP TRIGGER トリガー名
コンパイルエラーを再コンパイル ALTER TRIGGER トリガー名 COMPILE

まとめ

Oracleのトリガーは、データベースレベルで自動処理を実行できる強力な機能です。この記事のポイントを整理します。

記事のまとめ

  • トリガー情報は USER_TRIGGERS(自分)→ ALL_TRIGGERS(アクセス可能)→ DBA_TRIGGERS(全体)の3ビューで確認
  • ソースコードの取得は USER_SOURCE または DBMS_METADATA.GET_DDL
  • BEFORE は値の変更が可能(自動採番等)、AFTER は参照のみ(監査ログ等)
  • 行レベル(FOR EACH ROW)は行ごとに起動、文レベルはDML文ごとに1回
  • :NEW / :OLD で変更前後のデータにアクセス
  • INSTEAD OF トリガーでビューへのDMLを実現
  • ORA-04091(ミューテーティングテーブル)はコンパウンドトリガーで回避
  • 大量データロード時は ALTER TABLE ... DISABLE ALL TRIGGERS で一括無効化
  • Oracle 12c以降の自動採番には IDENTITY カラムが推奨