#対象読者
以下のような疑問を持つ若手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テーブルへのトリガーが...なんてことになってしまうと保守が困難になることは明らかです。
上記の問題点は、本来アプリ側で実現可能なアプリロジックを不用意にトリガーで実装した場合に生じるものです。保守性を考慮したうえで設計すれば、運用する中で大きな問題は出ないでしょう。
#おわりに
この記事では、データベースの機能の一つであるトリガーについて、その意味や使い方、問題点についてまとめました。トリガーに関する理解が少しでも深まったのであれば幸いです。