📒

PostgreSQLのトリガーについて知ったのでサクッとメモ

2025/03/09に公開

背景

ER図: list_items と lists のリレーション
1つの list に複数の list_item が紐づく「1対多」のリレーションが構築されています。

list_itemsのレコードが挿入された時にlist_idと同じ値であるlist_itemsのidを持つレコードのupdated_atを更新したいです。

案1 アプリケーション側で更新する

if _, err := tx.Update("lists").
			Set(goqu.Record{"updated_at": goqu.L("NOW()")}).
			Where(goqu.C("id").Eq(listID)).
			Executor().ExecContext(ctx); err != nil {
			return err
		}

確かにこれで実現はできますが、今後何らかの原因で更新漏れを起こすことが考えられそうです。
次にlist_itemsのレコードが挿入された時にlist_idと同じ値であるlist_itemsのidを持つレコードのupdated_atを自動的に更新する案を考えます。

案2 PostgreSQL の INSERT トリガーを使う

トリガーとは

PostgreSQLにおけるトリガーは、特定のテーブルで指定されたイベント(Insert、Update、Delete)発生時に自動的に実行される処理を定義するものです。

データベース内での操作を自動化し、データの一貫性を保つのに役立ちます。

引用

新しい target_list_items が挿入されるたびに、対応する target_lists.updated_at を 自動で最新の created_at に更新 するトリガーを作成します

CREATE OR REPLACE FUNCTION update_lists_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    UPDATE lists
    SET updated_at = NEW.created_at
    WHERE id = NEW.list_id;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_lists
BEFORE INSERT ON list_items
FOR EACH ROW
EXECUTE FUNCTION update_lists_updated_at();

アプリケーション側のコードを変更しなくても、このロジックがデータベース内で適用されます。
複雑なロジックの場合は、案1のようにアプリケーション側で制御した方が良いのかなとも思いました。

参考

https://cs-techblog.com/db/postgresql-trigger/#toc4

最後に

  • DB周り、バックエンド共に知識が浅いので少しずつ力をつけたいです。
  • 助言あればコメントいただきたいです。
immedioテックブログ

Discussion