【実務・中級編】 トリガー関数 – PostgreSQL

「魔法の裏方」トリガー関数を使いこなそう:PostgreSQLの自動化術

やあ。データベースを触っていると、「このテーブルが更新されたら、こっちのテーブルにもログを残したいな」とか、「特定のカラムが書き換わった瞬間に、バリデーションを走らせたいな」なんてこと、よくあるよね。

手動でSQLを叩く? いやいや、それはヒューマンエラーの元だし、何より面倒だ。そんなとき、PostgreSQLが誇る強力な武器「トリガー(Trigger)」の出番だ。

今回は、その心臓部である「トリガー関数」について、実務で明日から使えるレベルの話をしようと思う。

—

トリガー関数って、そもそも何者?

トリガー関数は、普通の関数と何が違うのか。結論から言うと、「トリガーというイベントが発生したときに、その裏で密かに起動する、専用の変数が使える関数」だ。

普通の関数と違って、`RETURNS TRIGGER` という戻り値型を使うのがお決まりのルール。そして、トリガー関数の内部では、PostgreSQLが自動的に用意してくれる「魔法の変数」が使えるようになるんだ。

実務で必須の「魔法の変数」たち

まずは、これだけは覚えておいてほしいという代表的な変数を紹介するよ。

  • `NEW`: `INSERT`や`UPDATE`で、新しく入るデータが入っているレコード変数。
  • `OLD`: `UPDATE`や`DELETE`で、以前入っていたデータが入っているレコード変数。
  • `TG_OP`: 現在実行されている操作の名前(’INSERT’, ‘UPDATE’, ‘DELETE’ のいずれか)。
  • `TG_TABLE_NAME`: トリガーが起動したテーブル名。

これらがあるおかげで、1つの関数を複数のテーブルや操作で使い回す、なんて芸当も可能になるんだ。

—

実践:更新履歴を自動保存するトリガー

百聞は一見にしかず。具体例を見てみよう。
例えば、「ユーザーテーブルのメールアドレスが変更されたとき、自動的に変更履歴テーブルに残す」という実装だ。

— 履歴保存用のトリガー関数
CREATE OR REPLACE FUNCTION log_email_change()
RETURNS TRIGGER AS $$
BEGIN
— メールアドレスが変更された時だけ記録する
IF (OLD.email IS DISTINCT FROM NEW.email) THEN
INSERT INTO email_change_logs(user_id, old_email, new_email, changed_at)
VALUES (OLD.id, OLD.email, NEW.email, NOW());
END IF;

— UPDATE操作ならNEWを返して変更を確定させる
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

— トリガーの登録
CREATE TRIGGER trg_log_email_change
AFTER UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION log_email_change();

先輩エンジニアからのアドバイス:ここがハマりどころ!

トリガー関数は便利なんだけど、実務で使う際にはいくつか注意点があるんだ。

1. デバッグが地味に面倒
トリガー関数の中で `RAISE NOTICE ‘変更前: %’, OLD.email;` みたいにログを仕込んでおかないと、裏で何が起きているか追うのが難しくなる。本番環境で動かす前に、必ず開発環境で挙動を確認してくれ。

2. パフォーマンスへの影響
トリガーは「1行更新するごとに」走る。数万件のバルクインサートをするときに重い処理を書くと、DBの負荷が一気に跳ね上がる。複雑な計算や外部API連携なんかは、トリガーの仕事じゃないと割り切る勇気も必要だ。

3. 無限ループの罠
「Aテーブルを更新したらBテーブルを更新する。BテーブルのトリガーでAテーブルを更新する……」なんて実装をすると、即座に無限ループで死ぬ。設計時にはトリガーの連鎖には十分注意してほしい。

最後に

トリガー関数は、まさに「縁の下の力持ち」。適切に使えばアプリケーション側のコードを劇的にクリーンにできるし、データの整合性を担保する最強の守り神にもなる。

まずは簡単なログ出力から始めて、徐々に慣れていってほしい。もし「こんな実装どうすればいい?」みたいな疑問があれば、いつでも聞いてくれよ。

じゃあ、今日はこの辺で。ハッピーなDBライフを!

コメント

タイトルとURLをコピーしました