1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【MySQL】トリガーとは?基礎から実務で使える例までわかりやすく解説

1
Posted at

はじめに

Databaseを使っているとこんな要件はありませんか?

  • ステータス更新の履歴を残したい
  • 誰がいつ更新したかを記録したい
  • 更新前と更新後の値を保存したい

実務ではこういった監査ログを実装することがあり、
その時に便利なのがトリガー(trigger)です。

この記事では、

  • トリガーの基礎
  • トリガーの基本構文
  • 実務で使える例
  • 注意点

について解説していきます。

トリガーの基礎

トリガーとは、「テーブルで INSERT(挿入) / UPDATE(更新) / DELETE(削除) がトリガーとなり、自動で実行される処理」 のことです。
実行される処理はあらかじめ定義された処理が実行されます。
処理の定義については次の「トリガーの基本構文」で解説します。

トリガーの基本構文

CREATE TRIGGER トリガー名
{ BEFORE | AFTER }
{ INSERT | UPDATE | DELETE }
ON テーブル名
FOR EACH ROW
BEGIN
    -- 実行したい処理
END;

①対象のテーブル

トリガーは「どのテーブルにぶら下げるか」が最初の設計です。

CREATE TRIGGER after_update_orders
AFTER UPDATE ON orders

orders が対象テーブルです。

②トリガーを起動するイベント

トリガーは以下のイベントで起動します。

  • INSERT(挿入)
  • UPDATE(更新)
  • DELETE(削除)
CREATE TRIGGER after_update_orders
AFTER UPDATE ON orders

UPDATE がトリガーを起動するイベントになります。

INSERTの場合
  • NEWのみ存在
  • 初期値保存や履歴生成に使われる
UPDATEの場合
  • NEWとOLDが存在
  • 「何かが変更されたら」ではなく「特定カラムが変更されたら」
DELETEの場合
  • OLDのみ存在
  • 削除後のデータは消える
ポイント
  • NEW → 更新後の値
  • OLD → 更新前の値(UPDATEやDELETEで使える)

③トリガーの起動タイミング

CREATE TRIGGER after_update_orders
AFTER UPDATE ON orders

AFTER がトリガーを起動するタイミングになります。

BEFORE

特徴:

  • NEWを書き換えられる
  • バリデーション可能

用途:

  • 自動補正
  • 更新値の強制

例:強制的にupdated_atを更新する

SET NEW.updated_at = NOW();

アプリ側での更新漏れや誤操作を防ぐことができます。

AFTER

特徴:

  • 実際に確定した値を扱える

用途:

  • ログ用途に最適
イベントとタイミング
イベントとタイミング
INSERT BEFORE データ挿入前
INSERT AFTER データ挿入後
UPDATE BEFORE データ更新前
UPDATE AFTER データ更新後
DELETE BEFORE データ削除前
DELETE AFTER データ削除後

④トリガーの処理内容

トリガーは元のSQLと同一トランザクション内で実行されます。
つまり、

  • 失敗すれば本処理も失敗
  • ロールバックも連動

処理内容の設計原則

原則1:副作用は小さく

良い例:

  • ログ保存
  • フラグ補正
  • 軽い整合性チェック

悪い例:

  • 集計更新
  • 別テーブルへの大量更新
  • 複雑なビジネスロジック
原則2:条件を明確にし、コメントを書く

トリガーはブラックボックス化しやすいため、ドキュメント化は必須です。
ドキュメントで条件を明確にし、SQLでもコメントを残すと処理の可読性が上がります。

実務で使える例

例えば、商品の管理システムを想定し、受注データを管理するテーブルとステータスの変更ログテーブルを作成します。

①対象テーブル

orders

CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    customer_name VARCHAR(100),
    status VARCHAR(50),
    created_at DATETIME,
    updated_at DATETIME,
    updated_by INT
);

想定する注文の「ステータス」

  1. 受付中
  2. 支払済
  3. 発送済
  4. 配達完了
  5. キャンセル
②ログテーブル

orders_log

CREATE TABLE status_change_log (
    id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT,
    old_status VARCHAR(50),
    new_status VARCHAR(50),
    created_at DATETIME,
    updated_by INT
);
③トリガー名

after_update_orders

④イベントとタイミング

AFTER UPDATE(更新後)

⑤トリガーの作成
CREATE TRIGGER after_update_orders
AFTER UPDATE ON orders
FOR EACH ROW
BEGIN
    -- ステータスが変更された場合のみログを残す
    IF OLD.status <> NEW.status THEN
        INSERT INTO orders_log (
            order_id,
            old_status,
            new_status,
            created_at,
            updated_by
        )
        VALUES (
            order_id
            OLD.status,
            NEW.status,
            OLD.user_id,
            NEW.user_id,
            NOW(),
            user_id
        );
    END IF;
END;
注文からキャンセルまでの処理の流れ
  1. アプリから注文が入る
  2. ステータスを支払済にUPDATE
  3. 更新成功
  4. AFTER UPDATEトリガー発火
  5. 更新前と更新後のstatusが異なる場合orders_logにログをINSERT
  6. その後管理者が注文をキャンセル
  7. 3,4,5の処理
ログテーブルのデータ
id order_id old_status new_status created_at updated_by
1 1 受付中 支払済 2026/02/24 10:00:00 (顧客のID)
2 1 支払済 キャンセル 2026/02/24 10:05:00 (管理者のID)

ordersテーブルだけではデータが上書きされてしまうので更新履歴を遡ることはできませんが、このトリガーの処理によって「いつ」、「誰が」、「更新前と更新後の値」をログテーブルに残すことができます。

トリガーの注意点

①デバッグがしにくい

トリガーは裏で動くため、「なぜこのデータが入ったのか?」が分かりにくくなります。

②パフォーマンスに影響

大量INSERT時に重い処理を書くとパフォーマンスが落ちます。

③依存関係が見えづらい

スキーマだけ見ても処理内容が分かりづらいです。
確認コマンド:

SHOW TRIGGERS;

まとめ

トリガーは「データベースレベルで自動処理を実行できる強力な機能」ですが、
便利な反面、設計を誤るとブラックボックス化します。
使うなら

  • 目的を明確にする
  • シンプルに保つ
  • 必ずドキュメント化する

この3点を意識しましょう。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?