TL

トリガとルールの実行モデルと副作用順序

トリガが「いつ・どの順で・何回」発火するかを正確に把握できれば、二重更新や無限再帰の事故を未然に防げます。BEFORE/AFTER/INSTEAD OF の発火順序とルール書き換えの意味論を整理します。

応用データベーストリガルールシステムPostgreSQL副作用最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.トリガは BEFORE→(本体操作)→AFTER の順に発火し、BEFORE 行トリガだけが NEW を書き換えて値を確定でき、AFTER は確定後の値しか触れない。
  • 2.行レベルは影響行ごと、文レベルは1文につき1回発火する。同名タイミングの行トリガが複数あると、多くは名前の昇順という実装依存の順で連鎖する。
  • 3.ルールシステムはクエリ実行前に問い合わせツリーを書き換える仕組みで、トリガ(実行時に行ごと発火)とは作用する層が根本的に異なる。

トリガが発火する3つのタイミング

トリガは、テーブルへの INSERT/UPDATE/DELETE をきっかけに自動実行される手続きです。問題は「いつ呼ばれるか」で、これは3種のタイミングで定義されます。

  • BEFORE: 実際の行操作が起きるに発火する。ここで NEW(挿入・更新後の行イメージ)を書き換えると、その書き換えがそのまま実テーブルに反映される。値の補正・バリデーション・既定値の動的計算に使う。
  • AFTER: 行操作が完了した後に発火する。NEW/OLD は読めるが書き換えても無視される。確定済みのデータを前提とした派生処理(監査ログ記録、集計テーブル更新、別テーブルへの伝播)に使う。
  • INSTEAD OF: 主にビューに対して定義し、本来の操作を置き換える。ビューは実体を持たないので、INSERT などをそのまま適用できない。代わりに INSTEAD OF トリガが「実テーブルへどう書くか」を肩代わりする。
BEFORE と AFTER の決定的な違いは“いつ値が確定するか”

BEFORE トリガが返した NEW が、その行の最終的な書き込み内容になります。だから値の改変は BEFORE でしか効きません。AFTER の時点ではすでに行は確定しており、NEW への代入は単に捨てられます。「値を直したいなら BEFORE、確定後に反応したいなら AFTER」が原則です。BEFORE 行トリガが NULL を返すと、その行に対する操作自体がスキップされます。

行レベルと文レベル:発火回数が違う

トリガには発火の粒度が2つあり、回数と参照できる情報が変わります。

観点行レベル(FOR EACH ROW)文レベル(FOR EACH STATEMENT)
発火回数操作の影響を受けた行ごとに1回1つの SQL 文につき1回(0行更新でも1回)
NEW / OLDその行の新旧イメージを参照できる個々の行は参照できない(遷移テーブルで全体は可)
主な用途行単位の検証・補正・派生更新文の前後フック、集計の一括処理、監査
コスト感大量行 UPDATE で行数ぶん呼ばれ重い行数に依存せず一定

1000行を1文で更新すると、行レベルトリガは1000回、文レベルトリガは1回呼ばれます。文レベルでも個々の変更行をまとめて扱いたい場合は、遷移テーブルREFERENCING ... OLD/NEW TABLE)で変更行の集合を1つのリレーションとして受け取れます。これにより「1文ぶんの差分」に対して集合演算(→ マテリアライズドビュー の差分更新など)を1回で適用でき、行ごと発火より効率的です。

発火の全体順序は次の通りで、BEFORE が本体を挟み込む構造になっています。

BEFORE 文トリガ
  └ 各影響行について:
       BEFORE 行トリガ(NEW を書き換え可能 / NULL でスキップ)
       実際の行操作(テーブルへの書き込み・制約チェック)
       AFTER 行トリガ(確定値を参照、書き換え不可)
AFTER 文トリガ

同タイミングのトリガが複数あるときの順序

同じテーブル・同じタイミング(例: BEFORE UPDATE 行レベル)に複数のトリガが定義されていると、その相互の発火順が問題になります。ここは標準と実装で扱いが分かれます。

  • SQL 標準: 同一タイミングのトリガは作成時刻順に発火すると規定。
  • PostgreSQL: **トリガ名の昇順(アルファベット順)**で発火する。順序を制御したいなら 01_validate02_audit のように名前で並びを設計するのが定石。
  • Oracle / SQL Server: 明示指定がなければ順序は未定義に近く、Oracle は FOLLOWS/PRECEDES、SQL Server は sp_settriggerorder で順序を限定的に制御する。
発火順に依存した設計は移植性の地雷

「監査トリガは検証トリガより後に動く」といった暗黙の順序前提でロジックを組むと、別の DB へ移したときや名前を変えたときに静かに壊れます。順序が結果に影響するなら、名前付け規約で明示するか、1つのトリガ関数に処理を集約して順序を内部で固定するのが安全です。複数トリガが同じ NEW を奪い合って書き換える構成は特に事故りやすいので避けます。

なお、AFTER トリガの実行は「文の完了時点」あるいは「トランザクションのコミット時点(DEFERRED 制約トリガ)」までキューに積まれて遅延されることがあります。外部キー制約は内部的に AFTER トリガとして実装されており、この遅延キューに乗ります。AFTER 行トリガが大量にキューされるとメモリを圧迫する点も実務上の注意点です。

再帰トリガ:トリガが次のトリガを呼ぶ

トリガ本体が SQL を実行し、その SQL がまた別のトリガ(あるいは自分自身)を発火させる連鎖を再帰トリガと呼びます。これは強力ですが、停止性が保証されません。

-- 親カテゴリの更新が子へ伝播し、子の更新がさらに孫へ…と連鎖
CREATE FUNCTION cascade_flag() RETURNS trigger AS $$
BEGIN
  UPDATE node SET flag = NEW.flag
  WHERE parent_id = NEW.id AND flag <> NEW.flag;  -- 終了条件が要
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

上の例で flag <> NEW.flag という終了条件を外すと、同じ行を何度も更新し続けて無限ループに陥ります。再帰の深さには実装上の上限(PostgreSQL の max_stack_depth 由来のスタック制限など)があり、超えるとエラーで全体がロールバックします。

再帰トリガの3つの落とし穴

(1) 無限再帰: 更新が必ず収束するよう「変化がなければ書かない」ガードを入れる。(2) 可視性の罠: BEFORE トリガ内で同じテーブルを問い合わせると、まだ確定していない行が見えたり見えなかったりして直感に反する。(3) AFTER の遅延: AFTER トリガの再帰は文完了時にまとめて処理されるため、想定と異なる順で大量に走ることがある。連鎖更新は深さと収束性をセットで設計してください。

SQL Server には RECURSIVE_TRIGGERS というデータベース設定があり、直接再帰(トリガが同じテーブルを更新して自分を再発火)をオフにできます。PostgreSQL にこの大域スイッチはなく、終了条件で自衛するのが基本方針です。トリガとトランザクションの関係上、連鎖の途中で例外が出れば(→ トランザクションの基礎)その文(またはトランザクション)全体が巻き戻ります。

ルールシステム:実行前に問い合わせを書き換える

PostgreSQL 固有のルールシステムCREATE RULE)は、トリガと混同されがちですが作用する層がまったく異なります。トリガが実行時に行ごと・文ごとに手続きを走らせるのに対し、ルールは問い合わせの計画前に、入力された問い合わせツリー(パースツリー)を別のツリーへ書き換える仕組みです。これを問い合わせ書き換え(query rewriting)と呼びます。

最も身近な例がビューです。CREATE VIEW は内部的に、ビューへの SELECT を元テーブルへの SELECT に置換する ON SELECT ルールとして実装されています。更新可能ビューも、ON INSERT/ON UPDATE/ON DELETE ルール(または前述の INSTEAD OF トリガ)で実テーブルへの操作に書き換わります。

観点トリガルール(書き換え)
作用する層実行エンジン(行・文の実行時)問い合わせ書き換え(計画前のツリー変換)
発火単位影響行ごと、または文ごと問い合わせ1つを別の問い合わせ群へ展開
大量行への効率行数ぶん手続きが走り重くなりがち集合演算1回に書き換わるので有利なことが多い
意味論の直感性手続き的で追いやすいツリー置換のため挙動が直感に反しやすい

ルールの意味論で躓きやすいのが「副作用の重複」です。DO INSTEAD でない条件付きルールは、元の問い合わせとルールが生成した問い合わせの両方を実行します。さらに、ルール本体の中で NEWOLD を参照する式が複数回現れると、その参照ぶん副問い合わせが展開され、揮発性関数(nextval など連番採番)が想定より多く評価されることがあります。これがルールが「危険」と言われる典型例です。

迷ったらトリガを選ぶ

PostgreSQL 公式も、ビュー定義以外の用途ではルールよりトリガを推奨しています。理由は意味論の直感性です。ルールはツリー置換なので「文が1回」のつもりが内部で複数回展開され、副作用回数がずれる事故を起こしやすい。手続き的に「この行に対しこの処理を1回」と読めるトリガの方が、保守時の事故が圧倒的に少なくて済みます。ビュー更新の肩代わりは INSTEAD OF トリガで書くのが現在の定石です。

まとめ

  • 発火順序は BEFORE →(本体操作)→ AFTER。値を確定・改変できるのは BEFORE 行トリガだけで、AFTER は確定後の値にしか反応できない。INSTEAD OF はビューへの操作を実テーブルへ肩代わりする。
  • 行レベルは影響行ごと、文レベルは1文1回。同タイミングの複数トリガの順は実装依存(PostgreSQL は名前昇順)で、順序前提の設計は避ける。遷移テーブルで差分集合を1回で扱える。
  • 再帰トリガは終了条件と収束性をセットで設計し、無限再帰とスタック超過を防ぐ。AFTER の遅延キューと例外時の巻き戻りも織り込む。
  • ルールシステムは計画前のツリー書き換えで、トリガとは層が違う。副作用の重複や揮発性関数の多重評価という罠があるため、ビュー定義以外は手続きが直感的なトリガ(→ ストアドプロシージャ)や、MVCC 下での可視性(→ MVCC の内部実装)も踏まえて選ぶのが安全。

データベース Article

トリガとルールの実行モデルと副作用順序を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

データベース

比較で見る軸

難易度: advanced / カテゴリ: データベース / タグ数: 5

導入後に効く点

行レベルは影響行ごと、文レベルは1文につき1回発火する。同名タイミングの行トリガが複数あると、多くは名前の昇順という実装依存の順で連鎖する。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
データベース
タグ数
5

判断チェックリスト

  • 自社の用途が「データベース / トリガ」に近いか確認する。
  • 強みである「トリガは BEFORE→(本体操作)→AFTER の順に発火し、BEFORE 行トリガだけが NEW を書き換えて値を確定でき、AFTER は確定後の値しか触れない。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

データベーストリガルールシステムPostgreSQL副作用データベーストリガルールシステム
参考: 公式情報