CodeGym /コース /SQL SELF /トリガー開発時によくあるミスの分析

トリガー開発時によくあるミスの分析

SQL SELF
レベル 58, レッスン 4
使用可能

さて、みんな、もうトリガーの基本や種類、動作原理を知ってるし、いろんな用途で作れるようになったよね。でも、プログラミングでは「できること」を知るのと同じくらい、「やっちゃダメなこと」を知るのも大事なんだ。今日はトリガーを使うときにありがちなミスを一緒に見ていくよ。これを知っておけば、デバッグに何時間も、もしかしたら何日も無駄にしなくて済むからね。

トリガーの再帰:トリガーが自分自身を呼び出す

これは初心者が一番やりがちなミスかな。例えば、テーブルのカラムlast_modifiedを更新するトリガーを作ったとする。でも、その更新操作自体がまたトリガーを呼んじゃう。これで無限ループになって、最終的にサーバーがスタックオーバーフローで落ちちゃうんだ。

例:

CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
    -- last_modifiedフィールドを更新
    UPDATE my_table
    SET last_modified = NOW()
    WHERE id = NEW.id;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER after_update
AFTER UPDATE ON my_table
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();

どこがダメか分かる?関数内のUPDATEが同じトリガーをまた呼んじゃうんだ。これで無限ループ完成。

どうやって防ぐか:

OLDを使って値が本当に変わったかチェックしてから更新しよう:

CREATE OR REPLACE FUNCTION update_last_modified_safe()
RETURNS TRIGGER AS $$
BEGIN
    -- 値が変わったかチェック
    IF NEW.last_modified IS DISTINCT FROM OLD.last_modified THEN
        NEW.last_modified = NOW();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

トリガー内で余計な操作を呼ばないように気をつけて!

OLDとNEWの誤用

この変数たちはトリガーでめっちゃ便利だけど、使い方を間違えると頭痛のタネになる。OLDは行が変更される前のデータ、NEWは変更後に保存されるデータだよ。

よくあるミスは、変数が使えない場面で使おうとしちゃうこと。例えばBEFORE INSERTトリガーではOLDは使えない。だって、まだ行ができてないからね。

ミスの例:

-- 挿入時はOLDが存在しないのでエラーになる
CREATE OR REPLACE FUNCTION log_inserts()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO audit_log (old_data, new_data)
    VALUES (OLD.my_column, NEW.my_column);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

どうやって防ぐか:

OLDとNEWを使う場面をちゃんと選ぼう:

  • OLDはUPDATEとDELETEで使える。
  • NEWはINSERTとUPDATEで使える。

1つの操作に複数トリガー

PostgreSQLでは、同じ操作・同じテーブルに複数のトリガーを作れる。一見便利そうだけど、トリガー同士が競合したり、同じデータを変更しちゃうとカオスになるよ。

例:

-- トリガー1
CREATE OR REPLACE FUNCTION trigger_one()
RETURNS TRIGGER AS $$
BEGIN
    -- トリガー1のロジック
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- トリガー2
CREATE OR REPLACE FUNCTION trigger_two()
RETURNS TRIGGER AS $$
BEGIN
    -- トリガー2のロジック
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 2つのトリガーを作成
CREATE TRIGGER trigger_one AFTER INSERT ON my_table EXECUTE FUNCTION trigger_one();
CREATE TRIGGER trigger_two AFTER INSERT ON my_table EXECUTE FUNCTION trigger_two();

どっちのトリガーもmy_tableへのINSERTで動く。ロジックがうまく同期できてないと、予想外の結果になるかも。

どうやって防ぐか:

  • トリガーの設計は事前にちゃんと考えよう。
  • 同じロジックなら1つのトリガーにまとめよう。

パフォーマンスの問題

トリガーは関連する操作ごとに追加の処理をするから、操作やレコードが多いテーブルで使うとパフォーマンスがガクッと落ちることがある。

悪い例:

CREATE OR REPLACE FUNCTION heavy_trigger_function()
RETURNS TRIGGER AS $$
BEGIN
    -- 各行の更新ごとに重い処理を実行
    PERFORM some_heavy_query();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER performance_killer AFTER UPDATE ON huge_table EXECUTE FUNCTION heavy_trigger_function();

どうやって防ぐか:

  • トリガー内のロジックは最小限に。重い処理はバックグラウンドジョブに回すのもアリ。
  • WHEN条件でトリガーの発動を制限しよう:
CREATE TRIGGER optimized_trigger
AFTER UPDATE ON my_table
WHEN (OLD.column_name IS DISTINCT FROM NEW.column_name)
EXECUTE FUNCTION light_function();

トリガーとトランザクション

トリガーは、君のクエリで始まったトランザクションの中で実行される。もしトリガー内でエラーが起きたら、トランザクション全体がロールバックされる。これは便利な場合もあるけど、エラーハンドリングを考えてないと予想外の問題になることも。

ミスの例:

CREATE OR REPLACE FUNCTION error_prone_trigger()
RETURNS TRIGGER AS $$
BEGIN
    -- わざとエラーを発生
    RAISE EXCEPTION '何かがおかしいよ!';
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

このトリガーが動くと、メインのクエリのトランザクションもロールバックされちゃう。

どうやって防ぐか:

トリガー内でエラーハンドリングを入れて、メイントランザクションへの影響を最小限にしよう:

CREATE OR REPLACE FUNCTION safe_trigger()
RETURNS TRIGGER AS $$
BEGIN
    BEGIN
        -- エラーが起きるかもしれないコード
        INSERT INTO another_table VALUES (NEW.data);
    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'エラーが発生したけど、ちゃんと処理したよ。';
    END;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

実践的なアドバイス

  1. トリガーはできるだけシンプルに。 もしトリガーが大きすぎたり複雑だと感じたら、関数を分けたりロジックを見直そう。

  2. まずは少量のデータでテストしよう。 重要なテーブルにトリガーを付ける前に、テスト環境のデータで動作確認しよう。

  3. トリガーのドキュメントを書こう。 数ヶ月後には、なんでそのトリガーを作ったのか自分も同僚も忘れてるかも。ちゃんとドキュメントを書いておけば後で困らないよ。

  4. アプリ側でできることはトリガーでやらない。 トリガーは即時実行が必要な自動化には便利だけど、複雑なビジネスロジックには向かないこともあるから注意。

  5. パフォーマンスに気を配ろう。 データ量や負荷が増えたとき、トリガーがDBのパフォーマンスにどう影響してるか常にモニタリングしよう。

これらのアドバイスと、今日学んだ知識があれば、ただトリガーを作るだけじゃなくて、ちゃんと動いて効率的でトラブルのないトリガーを書けるようになるよ!

2
タスク
SQL SELF, レベル 58, レッスン 4
ロック未解除
カラムの安全な更新トリガーの作成
カラムの安全な更新トリガーの作成
1
アンケート/クイズ
行レベルとテーブルレベルのトリガー、レベル 58、レッスン 4
使用不可
行レベルとテーブルレベルのトリガー
行レベルとテーブルレベルのトリガー
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION