トリガーで“自動化と整合性”をデータ層に実装する
SQL Serverのトリガー(Trigger)は、テーブルやビューへのINSERT/UPDATE/DELETEなどの操作を契機に自動実行される仕組みです。
アプリケーション層に依存せず、整合性チェック・履歴管理・監査ログを実現できる一方、誤用するとパフォーマンスや可読性を損なうこともあります。
本記事では、AFTER/INSTEAD OFトリガーの違い・実装例・副作用防止策・ベストプラクティスを体系的に解説します。
目次
第1章 トリガーの基本構造と種類
トリガーは特定のデータ操作(DML:INSERT/UPDATE/DELETE)やDDL操作(CREATE/ALTERなど)に反応して自動実行される仕組みです。
特に業務ロジックの自動化や履歴保持に用いられるのはDMLトリガーです。
- AFTERトリガー: 対象操作が完了した後に実行(整合性チェック・ログ記録に最適)
- INSTEAD OFトリガー: 対象操作の代わりに実行(ビュー上の更新などに利用)
- DDLトリガー: CREATE TABLE/ALTER DATABASE などスキーマ変更を検知
-- 基本構文
CREATE TRIGGER トリガー名
ON テーブル名
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
-- 処理内容
END;
トリガー内では、直前の変更データを参照するために inserted/deleted という仮想テーブルが利用できます。
第2章 AFTERトリガーの動作と活用例
AFTERトリガーは、対象操作が完了した後に発火します。 もっとも一般的で、整合性チェックや監査ログ記録などに最適です。
CREATE TABLE dbo.Customers (
CustomerID INT PRIMARY KEY,
Name NVARCHAR(50),
UpdatedAt DATETIME
);
GO
CREATE TABLE dbo.CustomerLog (
LogID INT IDENTITY PRIMARY KEY,
CustomerID INT,
Action NVARCHAR(10),
ChangedAt DATETIME
);
GO
CREATE TRIGGER trg_AfterUpdate_Customer
ON dbo.Customers
AFTER UPDATE
AS
BEGIN
INSERT INTO dbo.CustomerLog (CustomerID, Action, ChangedAt)
SELECT i.CustomerID, 'UPDATE', SYSDATETIME()
FROM inserted AS i;
END;
この例では、顧客テーブルが更新されるたびに自動的にログが追記されます。 アプリ側の変更漏れを防ぎ、監査要件をデータベース層で担保できます。
第3章 INSTEAD OFトリガーの仕組みと使いどころ
INSTEAD OFトリガーは、指定された操作の「代わり」に実行されます。
主にビューへのINSERT/UPDATE/DELETEを有効化するために使われます。
CREATE VIEW dbo.vw_ActiveOrders AS
SELECT OrderID, CustomerID, Status
FROM dbo.Orders
WHERE Status = 'Active';
GO
CREATE TRIGGER trg_InsteadOfDelete_vwOrders
ON dbo.vw_ActiveOrders
INSTEAD OF DELETE
AS
BEGIN
UPDATE dbo.Orders
SET Status = 'Canceled'
WHERE OrderID IN (SELECT d.OrderID FROM deleted AS d);
END;
このようにビュー上で削除操作を行っても、実際にはキャンセル扱いとして更新処理に置き換わります。 業務ルールをSQL層で保証する設計パターンとして有効です。
第4章 監査・履歴管理トリガーの実装
トリガーの代表的な用途は履歴管理(監査ログ)です。 変更履歴を保持することで、追跡・再現・復旧が容易になります。
CREATE TABLE dbo.Sales (
SalesID INT PRIMARY KEY,
Amount MONEY,
UpdatedAt DATETIME
);
GO
CREATE TABLE dbo.SalesHistory (
HistoryID INT IDENTITY PRIMARY KEY,
SalesID INT,
OldAmount MONEY,
NewAmount MONEY,
ChangedAt DATETIME
);
GO
CREATE TRIGGER trg_AfterUpdate_Sales
ON dbo.Sales
AFTER UPDATE
AS
BEGIN
INSERT INTO dbo.SalesHistory (SalesID, OldAmount, NewAmount, ChangedAt)
SELECT d.SalesID, d.Amount, i.Amount, SYSDATETIME()
FROM deleted AS d
JOIN inserted AS i ON d.SalesID = i.SalesID;
END;
この設計により、更新のたびに前後データが履歴テーブルに保存されます。 アプリ側で特別な実装をしなくても、完全な変更ログを残せます。
第5章 副作用・多重発火を防ぐ設計指針
トリガーの最大の落とし穴は副作用です。 トリガー内で別テーブルを更新し、そのテーブルにもトリガーが存在する場合、連鎖的に発火して意図しない再帰処理が発生します。
- SET NOCOUNT ON:メッセージ出力を抑制し、再発火防止。
- 再帰抑止:
ALTER DATABASE SET RECURSIVE_TRIGGERS OFF - 自己検知:実行中セッションや処理回数を確認して多重実行を防ぐ。
- 性能監視:拡張イベント
sqlserver.trigger_executionで発火状況を分析。
ALTER DATABASE CURRENT SET RECURSIVE_TRIGGERS OFF;
GO
-- 自己再発防止例
CREATE TRIGGER trg_PreventRecurse
ON dbo.Example
AFTER UPDATE
AS
BEGIN
IF TRIGGER_NESTLEVEL() > 1 RETURN;
-- 本来の処理
END;
トリガーの使用範囲は最小限に留め、必ず「監査・整合性保持」など限定目的で利用することが推奨されます。
まとめ:トリガーを安全に使うルール
トリガーはアプリ層に依存せず自動処理を行える強力な仕組みですが、
乱用すると副作用や性能問題を招きます。
目的を明確にし、単一責務・再帰防止・ログ監査設計を徹底すれば、
信頼性と保守性を両立した自動化基盤を構築できます。
参考リンク(公式ドキュメント)

0 件のコメント:
コメントを投稿