【Oracle】ORA-04098の原因と解決方法|trigger is invalid and failed re-validation・トリガーが無効です

【Oracle】ORA-04098の原因と解決方法|trigger is invalid and failed re-validation・トリガーが無効です Oracle

ORA-04098: trigger is invalid and failed re-validation は、Oracleのトリガーが無効な状態になっており、実行時の再検証にも失敗した時に発生するエラーです。多くの場合、対象テーブルに INSERTUPDATEDELETE を実行した瞬間に出ます。

このエラーのポイントは、DML文そのものが間違っているとは限らないことです。実際には、裏で動くトリガーのコンパイルエラー、参照先テーブル・列・パッケージの変更、権限不足、無効な依存オブジェクトなどが原因です。

先に結論
ORA-04098が出たら、まずトリガー名を確認し、SHOW ERRORS TRIGGER または USER_ERRORS で本当のコンパイルエラーを見ます。そのうえでトリガー本文、依存オブジェクト、権限を直し、ALTER TRIGGER ... COMPILE で再コンパイルします。原因を見ずにトリガーを無効化するのは、一時回避にはなっても根本解決ではありません。
スポンサーリンク

ORA-04098とは

Oracle公式の説明では、ORA-04098は、指定されたトリガーが無効で、再検証にも失敗した状態を示します。トリガーは作成時や依存オブジェクト変更時に無効になることがあり、次にトリガーが発火したタイミングでOracleが自動的に再コンパイルを試みます。そこで再コンパイルに失敗すると、発火元のDMLも失敗し、ORA-04098が返ります。

トリガーの基本、有効化・無効化、DMLトリガーの使い方は Oracleトリガー完全ガイド、PL/SQLの例外処理は PL/SQL例外処理完全ガイド も参考になります。

状態 意味 見る場所
VALID トリガーは有効で実行可能 USER_OBJECTS.STATUS
INVALID トリガーが無効。実行時に再コンパイルされる可能性がある USER_OBJECTS / USER_TRIGGERS
ORA-04098 再コンパイルを試みたが失敗した USER_ERRORS / SHOW ERRORS

まず確認する順番

ORA-04098は、エラーメッセージにトリガー名が出ます。最初にそのトリガーがどのテーブルに紐づいているか、有効状態か、どんなコンパイルエラーを持っているかを確認します。

順番 確認すること 使うSQL
1 エラーメッセージのトリガー名 ORA-04098: trigger 'SCHEMA.TRG_NAME' ...
2 トリガーの状態 USER_OBJECTS / USER_TRIGGERS
3 コンパイルエラーの本文 SHOW ERRORS TRIGGER / USER_ERRORS
4 参照先テーブル・列・パッケージ USER_DEPENDENCIES
5 権限不足や無効な参照先 ORA-01031ORA-00942PLS-00201 など

トリガーの状態を確認する

まず、対象トリガーが VALIDINVALID かを確認します。トリガー名は通常大文字で格納されるため、検索条件も大文字で指定します。

check-trigger-status.sql
SELECT object_name,
       object_type,
       status,
       last_ddl_time
FROM user_objects
WHERE object_type = 'TRIGGER'
  AND object_name = 'TRG_ORDERS_BI';

SELECT trigger_name,
       table_name,
       status,
       triggering_event
FROM user_triggers
WHERE trigger_name = 'TRG_ORDERS_BI';

USER_TRIGGERS.STATUSENABLED でも、コンパイル状態が INVALID のことがあります。「有効化されているか」と「コンパイルできているか」は別物として見ます。

無効なトリガーを一覧で探す

トリガー名がログに出ていない場合や、リリース後にまとめて確認したい場合は、無効なトリガーを一覧で探します。自スキーマなら USER_OBJECTS、参照権限がある他スキーマを含めるなら ALL_OBJECTS、DBA権限があるなら DBA_OBJECTS を使います。

list-invalid-triggers.sql
-- 自スキーマの無効トリガー
SELECT object_name,
       status,
       last_ddl_time
FROM user_objects
WHERE object_type = 'TRIGGER'
  AND status <> 'VALID'
ORDER BY last_ddl_time DESC;

-- 他スキーマも含めて確認できる場合
SELECT owner,
       object_name,
       status,
       last_ddl_time
FROM all_objects
WHERE object_type = 'TRIGGER'
  AND status <> 'VALID'
ORDER BY owner, object_name;

本番障害では、ORA-04098が出たトリガーだけでなく、同じリリースで無効になったトリガーが他にもないか確認しておくと安心です。特にテーブル定義変更、パッケージ差し替え、権限変更の直後は、複数のトリガーが同時に無効化されることがあります。

SHOW ERRORS TRIGGERで原因を見る

SQL*PlusやSQLclを使っている場合は、SHOW ERRORS TRIGGER が最短です。ORA-04098そのものではなく、その裏にある PLS-ORA- のコンパイルエラーを読みます。

show-errors-trigger.sql
SHOW ERRORS TRIGGER trg_orders_bi;

-- 例:
-- LINE/COL ERROR
-- -------- ---------------------------------------------
-- 5/3      PL/SQL: SQL Statement ignored
-- 6/18     PL/SQL: ORA-00942: table or view does not exist

ここで表示された行番号と列番号を見て、トリガー本文のどこが壊れているかを確認します。権限不足が絡む場合は、直接 SELECT できるのにトリガー内では失敗することもあります。

USER_ERRORSでコンパイルエラーを確認する

アプリケーションやGUIツールから調査する場合は、USER_ERRORS を使うと安定します。複数のエラーがある場合は、SEQUENCE 順に読みます。

user-errors-trigger.sql
SELECT name,
       type,
       line,
       position,
       text
FROM user_errors
WHERE type = 'TRIGGER'
  AND name = 'TRG_ORDERS_BI'
ORDER BY sequence;

他スキーマのトリガーを見る場合は、権限に応じて ALL_ERRORSDBA_ERRORS を使います。本番調査では、トリガー所有者で接続して確認するのが分かりやすいです。

all-errors-trigger.sql
-- 他スキーマのトリガーエラーを確認する例
SELECT owner,
       name,
       type,
       line,
       position,
       text
FROM all_errors
WHERE type = 'TRIGGER'
  AND owner = 'APP'
  AND name = 'TRG_ORDERS_BI'
ORDER BY sequence;

原因1: 参照している列名・テーブル名が変わった

トリガー本文で参照している列名やテーブル名が変更された場合、トリガーは無効になります。次にDMLで発火した時に再コンパイルできず、ORA-04098になります。

invalid-column-in-trigger.sql
CREATE OR REPLACE TRIGGER trg_orders_bi
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
  -- order_no列が削除またはリネームされているとコンパイルエラー
  :NEW.order_no := orders_seq.NEXTVAL;
END;
/

ALTER TRIGGER trg_orders_bi COMPILE;

この場合は、トリガー本文を現在のテーブル定義に合わせて修正します。列名の確認は USER_TAB_COLUMNS を使います。

check-table-columns.sql
SELECT column_id,
       column_name,
       data_type,
       nullable
FROM user_tab_columns
WHERE table_name = 'ORDERS'
ORDER BY column_id;

原因2: トリガー内で参照するオブジェクトへの権限がない

トリガー内で別スキーマのテーブル、シーケンス、パッケージを参照している場合、権限不足で無効になることがあります。特にPL/SQLオブジェクトでは、ロール経由の権限ではなく、所有者へ直接付与された権限が必要になる場面があります。

grant-for-trigger.sql
-- 例: APPスキーマのトリガーがMASTERスキーマのテーブルを参照する場合
GRANT SELECT ON master.customers TO app;

-- シーケンスを使う場合
GRANT SELECT ON master.order_seq TO app;

-- パッケージを呼ぶ場合
GRANT EXECUTE ON master.pkg_audit TO app;

権限不足の詳しい切り分けは ORA-01031 insufficient privilegesの原因と対処 も確認してください。トリガー所有者で単体SQLを実行して、同じ参照ができるかを見ると早いです。

原因3: 呼び出し先のパッケージ・関数が無効

トリガーがパッケージや関数を呼び出している場合、その呼び出し先が無効になっていると、トリガー側も再検証に失敗します。USER_DEPENDENCIES で依存関係を確認し、参照先オブジェクトの状態も見ます。

check-trigger-dependencies.sql
SELECT name,
       type,
       referenced_owner,
       referenced_name,
       referenced_type
FROM user_dependencies
WHERE type = 'TRIGGER'
  AND name = 'TRG_ORDERS_BI'
ORDER BY referenced_type, referenced_name;

SELECT object_name,
       object_type,
       status
FROM user_objects
WHERE object_name IN ('PKG_AUDIT', 'FN_CHECK_ORDER');

パッケージ再コンパイル後の状態破棄で発生する ORA-04068 とは別の話ですが、依存オブジェクトの無効化という意味では近い領域です。詳しくは ORA-04068完全ガイド も参考になります。

原因4: :NEW / :OLD の使い方が間違っている

DMLトリガーでは、:NEW:OLD の使える場面が決まっています。INSERTで :OLD の値を前提にしたり、DELETEで :NEW を更新しようとしたりすると、コンパイルエラーの原因になります。

new-old-trigger-rule.sql
-- INSERT: :NEWは使える。:OLDは基本的に値を持たない
-- UPDATE: :NEW / :OLD の両方を参照できる
-- DELETE: :OLDは使える。:NEWは使えない

CREATE OR REPLACE TRIGGER trg_orders_bu
BEFORE UPDATE ON orders
FOR EACH ROW
BEGIN
  :NEW.updated_at := SYSTIMESTAMP;
END;
/

原因5: トリガー本文のPL/SQL構文エラー

単純なセミコロン漏れ、END IF の不足、変数宣言漏れ、引数の型違いでも、トリガーは無効になります。この場合は USER_ERRORS.TEXTPLS-00103PLS-00201PLS-00306 などが出ます。

エラー 意味 よくある原因
PLS-00103 構文として不正 セミコロン漏れ、END不足、予約語の誤用
PLS-00201 識別子が宣言されていない 変数名ミス、権限不足、存在しないパッケージ
PLS-00306 引数の数または型が違う プロシージャ呼び出しの引数不一致
ORA-00942 表またはビューが存在しない 参照先名ミス、直接権限不足
ORA-00904 無効な識別子 列名ミス、エイリアス誤り

ALTER TRIGGER COMPILEで再コンパイルする

原因を直したら、トリガーを手動で再コンパイルします。コンパイル後にもう一度 USER_OBJECTSUSER_ERRORS を確認します。

compile-trigger.sql
ALTER TRIGGER trg_orders_bi COMPILE;

SELECT object_name,
       status
FROM user_objects
WHERE object_type = 'TRIGGER'
  AND object_name = 'TRG_ORDERS_BI';

SELECT line,
       position,
       text
FROM user_errors
WHERE type = 'TRIGGER'
  AND name = 'TRG_ORDERS_BI'
ORDER BY sequence;

USER_ERRORS が0件で、USER_OBJECTS.STATUSVALID になれば、ORA-04098の直接原因は解消しています。最後に、実際に対象DMLをテストして発火時の動作も確認します。

一時的にトリガーを無効化する場合の注意

緊急対応として、トリガーを無効化してDMLを通すことはできます。ただし、監査ログ、自動採番、整合性チェック、連携テーブル更新など、トリガーが担っている処理が実行されなくなります。必ず影響範囲を確認してから実行します。

disable-enable-trigger.sql
-- 一時的に無効化
ALTER TRIGGER trg_orders_bi DISABLE;

-- 修正後に有効化
ALTER TRIGGER trg_orders_bi ENABLE;

-- テーブル上の全トリガーを無効化・有効化する場合
ALTER TABLE orders DISABLE ALL TRIGGERS;
ALTER TABLE orders ENABLE ALL TRIGGERS;

本番では、無効化前にトリガー本文と役割を確認し、復旧後に漏れた処理を補正する必要がないかも見ます。原因が権限や参照先オブジェクトの変更なら、無効化より先にコンパイルエラーの修正を優先します。

本番調査用チェックSQL

本番でORA-04098が出た時は、次のように状態、エラー、依存関係をまとめて確認すると早いです。トリガー名だけ差し替えて使います。

ora04098-diagnosis.sql
DEFINE trigger_name = 'TRG_ORDERS_BI'

SELECT object_name, object_type, status, last_ddl_time
FROM user_objects
WHERE object_type = 'TRIGGER'
  AND object_name = UPPER('&trigger_name');

SELECT trigger_name, table_name, status, triggering_event
FROM user_triggers
WHERE trigger_name = UPPER('&trigger_name');

SELECT object_name, status, last_ddl_time
FROM user_objects
WHERE object_type = 'TRIGGER'
  AND status <> 'VALID'
ORDER BY last_ddl_time DESC;

SELECT line, position, text
FROM user_errors
WHERE type = 'TRIGGER'
  AND name = UPPER('&trigger_name')
ORDER BY sequence;

SELECT referenced_owner, referenced_name, referenced_type
FROM user_dependencies
WHERE type = 'TRIGGER'
  AND name = UPPER('&trigger_name')
ORDER BY referenced_type, referenced_name;

ORA-04098と関連エラーの違い

エラー 主な意味 切り分け
ORA-04098 トリガーが無効で再検証に失敗 トリガーの USER_ERRORS を見る
ORA-04063 ビュー・パッケージなどのオブジェクトにエラーがある 対象オブジェクトの USER_ERRORS を見る
ORA-04068 パッケージ状態が破棄された パッケージ再コンパイル後の既存セッションを疑う
ORA-01031 権限不足 ロールではなく直接権限があるか確認する
ORA-04091 ミュータルテーブル 行トリガーで発火元テーブルを参照していないか確認する

ミュータルテーブルの対処は コンパウンドトリガー完全ガイド にまとめています。ORA-04098は「トリガーが無効で実行できない」ことが中心なので、まずコンパイルエラーを読むのが近道です。

修正チェックリスト

項目 確認すること 対応
トリガー名 エラーメッセージのトリガーと調査対象が一致しているか USER_TRIGGERS で確認
状態 USER_OBJECTS.STATUS がINVALIDか 無効ならエラー本文を確認
コンパイルエラー USER_ERRORS に何が出ているか 最初のエラーから直す
参照先 列名・テーブル名・パッケージ名が変わっていないか USER_DEPENDENCIES と定義を確認
権限 トリガー所有者に直接権限があるか GRANT を直接付与
再コンパイル ALTER TRIGGER ... COMPILE 後にVALIDか DMLで発火テストする

よくある質問

ORA-04098が出たDML文を直せば解決しますか?

多くの場合、DML文ではなく発火したトリガー側の問題です。エラーメッセージに出ているトリガー名を確認し、USER_ERRORS でトリガーのコンパイルエラーを見てください。

トリガーをDISABLEすればよいですか?

一時回避にはなりますが、トリガーが担っている処理が止まります。監査、自動採番、整合性チェックなどの影響を確認し、原則はコンパイルエラーを直して再有効化します。

CREATE OR REPLACE TRIGGERは成功したのにORA-04098になりますか?

環境や内容によって、トリガーが無効な状態で残ることがあります。作成後は SHOW ERRORS TRIGGER または USER_ERRORS でエラーがないことを確認してください。

直接SELECTできるのにトリガー内でORA-00942になります

ロール経由の権限で見えている可能性があります。PL/SQLオブジェクトでは直接付与された権限が必要になることがあるため、トリガー所有者に対象オブジェクト権限を直接GRANTしてください。

ALTER TRIGGER COMPILEしてもINVALIDのままです

まだコンパイルエラーが残っています。USER_ERRORS を再確認し、行番号の先頭から順に直します。参照先パッケージやビューが無効な場合は、先に参照先をコンパイルしてください。

まとめ

ORA-04098は、トリガーが無効で、実行時の再検証にも失敗した時に発生します。発火元のINSERTやUPDATEではなく、トリガーのコンパイルエラーや依存オブジェクト、権限不足が原因になっていることが多いです。

対応手順は、トリガー名の確認、USER_OBJECTS で状態確認、SHOW ERRORS TRIGGER または USER_ERRORS で原因確認、本文や権限の修正、ALTER TRIGGER ... COMPILE、最後にDMLで発火テスト、という流れです。本番では、無効化で一時回避する前に、トリガーが担っている業務処理への影響を必ず確認してください。

参考

ORA-04098 – Oracle Database Error Help

Trigger Compilation, Invalidation, and Recompilation – Oracle Database PL/SQL Language Reference