LoginSignup
112
88

More than 5 years have passed since last update.

3分でわかるトリガー -使い所と問題点を考える-

Last updated at Posted at 2018-04-30

対象読者

以下のような疑問を持つ若手ITエンジニアの方

  • トリガーって一体何なのか?
  • どのようなケースでトリガーを使うのか?
  • トリガーを使う上で注意すべき点は何か?

かくいう私もあまりわかってなかったので記事にしてみました。

トリガーとは

自分なりにまとめてみるとこんな感じになりました。
「特定のテーブルに対する操作(挿入・更新・削除)を契機として、あらかじめ定義された処理を自動的に実行する機能のこと」

例えば、売上を管理するテーブルで顧客ごとの累計売上高を常に最新状態にしたいという要求があるとします。その場合、売上発生を契機(引き金)として累計売上高を更新するトリガーを定義することで上記の要求を実現するこができます。

このように、トリガーの基本的な目的は複数テーブル間のデータ整合性を確保することにあります。もちろん、それ以外の目的でトリガーを使用するケースもあります(後述)。

トリガーの基本事項を表にするとこんな感じです。

5W Answer
目的 整合性確保, テーブル操作の証跡取得, etc.
実行主体 DBMS(*1)
実行タイミング 指定した操作(*2)が実行される直前または直後
処理対象 指定した任意のテーブル
処理内容 指定した任意の操作(*2)

*1 database management systemの略。具体例としてはOracle, MySQL, PostgreSQLなどなど。
DBMS 【 DataBase Management System 】 データベース管理システム
*2 INSERT, UPDATE, DELETEなどのデータ操作

トリガーの構文(MySQLの場合)


CREATE
    [DEFINER = { user | CURRENT_USER }]
    TRIGGER trigger_name
    trigger_time trigger_event
    ON tbl_name FOR EACH ROW
    trigger_body

trigger_time: { BEFORE | AFTER }
trigger_event: { INSERT | UPDATE | DELETE }

13.1.19 CREATE TRIGGER 構文をもとに作成。
これだけ見てもよくわかりませんね。実際のSQLを見てみましょう。

トリガーの具体例

MySQLで実際にSQLを作成し、トリガーの挙動を確認してみました。
ツッコミどころ満載なので何か思うところあればコメントよろしくお願いします。

やりたいこと

  • 顧客ごとの累計売上高をテーブルとして保持したい

使用するテーブル

  • 注文テーブル:orders(order_id, customer_id, total)
  • 累計売上高テーブル:aggregate_table(customer_id, total_amount)

太字は主キー)

SQL


DELIMITER $$ 

CREATE
    TRIGGER aggregate_by_customer
    AFTER INSERT
    ON orders
    FOR EACH ROW
    BEGIN
      INSERT INTO
           aggregate_table (customer_id, total_amount)
       (SELECT customer_id, SUM(total)
            FROM orders
            GROUP BY customer_id)
       ON DUPLICATE KEY UPDATE
            customer_id = NEW.customer_id
            ,total_amount = (SELECT SUM(total)
                                FROM orders
                                WHERE customer_id = NEW.customer_id
                                GROUP BY customer_id);
    END $$

DELIMITER ;

実行前のデータ

orders

order_id customer_id total
1 USR002 400
2 USR003 500
3 USR002 600
4 USR002 1000
5 USR001 9000
6 USR001 1000

aggregate_table
初期データとして行作成済み

customer_id total
USR001 10000
USR002 2000
USR003 500

実際にトリガーを動かしたみた

ordersテーブルに行を追加すると以下のようになります。


insert into 
    orders(order_id, customer_id, total)
values
    ('7','USR002',222);

aggregate_table

customer_id total
USR001 10000
USR002 2222
USR003 500

aggregate_tableの値が勝手に更新されています。
更新前:2000
更新後:2222

アプリ側から明示的に実行せずとも、データベース側だけで自動的なデータ操作が可能となります。
なんとなく便利そうな気がしますね。

トリガーの使い所

トリガーの用途はいろいろあると思いますが、代表的なものをいくつかあげてみました。

用途 説明
イベントロギング 監査やセキュリティ要件充足のために、データ操作に関する責任追跡性の確保する必要がある場合
導出項目・集計値の自動生成 性能要件充足などを目的に、カラムとして導出項目や集計値を保持したい場合
ビジネスルールの実現 何らかの理由でアプリ側ではなくデータベース側でデータ操作を実施したい場合

トリガーの問題点

一見便利そうなトリガーですが、使う際は少々注意が必要です。
トリガーの実装による問題点を少しあげてみました。

  • ビジネスロジックの分離による管理範囲の拡大
    • ビジネスルールを制御するロジックがアプリ側にもデータベース側にも存在することになります。例えば、ビジネスルール変更に伴ってロジックを修正しようとしても、アプリ側だけでなくデータベース側も考慮して修正する必要が出てきます。そして何より、ビジネスロジック全体を理解することが困難になってしまうので、保守性を考えるとおすすめできません。
    • 自動的に実行される点がトリガーのメリットでもありますが、透過的に実行されるがゆえに開発者から忘れ去られるという危険性をはらんでいます。「なんで勝手にデータ変わってるのこれ???」なんてことも起きてしまいそうですね。

  • テーブル間の依存性
    • トリガーを実装することで、あるテーブルへの操作が他のテーブルにも影響を及ぼすことになります。1つ目の問題点と同様ですが、データベースそれ自体を見ても考慮すべき事項と範囲が増加・拡大することになります。
    • AテーブルのトリガーによってBテーブルを更新してそれによりCテーブルへのトリガーが...なんてことになってしまうと保守が困難になることは明らかです。

上記の問題点は、本来アプリ側で実現可能なアプリロジックを不用意にトリガーで実装した場合に生じるものです。保守性を考慮したうえで設計すれば、運用する中で大きな問題は出ないでしょう。

おわりに

この記事では、データベースの機能の一つであるトリガーについて、その意味や使い方、問題点についてまとめました。トリガーに関する理解が少しでも深まったのであれば幸いです。

112
88
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
112
88