トリガーを自動生成2
前回の続き。
非正規化した項目の整合性を維持するトリガーをかいてみます。
トランザクションテーブルにトリガーを設定する。
例として、以下の様な受注明細があったとして、その非正規化項目の整合性を維持するトリガーを書いてみます。
CREATE TABLE T010_受注明細 ( ID int IDENTITY(1, 1) NOT NULL, T000_ID int NOT NULL, 受注CD nchar(10) NOT NULL, 行番号 int NOT NULL, M000_ID int, 製品CD nvarchar(20) NOT NULL, 製品区分 nvarchar(10) NOT NULL, 製品基準日 datetime NOT NULL, 単価 money NOT NULL, 数量 money NOT NULL ) go ALTER TABLE T010_受注明細 ADD CONSTRAINT pkT010_受注明細 PRIMARY KEY (ID) |
本来的には[T000_ID]に対応する[受注CD]も自動更新する必要があります。しかし、内容は同じなので、今回は[製品CD]のみを自動更新します。
まずはルールを決めましょう。
ルールとしていろんな形が考えられますが、[M000_ID]というサロゲートキーを後付けしたときにも使えるパターンで考えてみます。
後付けするために、NOT NULL制約は付けていません。
[製品CD][製品区分][製品基準日]をナチュラルキー、[M000_ID]をサロゲートキーとすると。以下の様なマトリックスになります。
サロゲートキー | |||
---|---|---|---|
更新あり | 更新なし | ||
ナチュラルキー | 更新あり | サロゲートキーからマスタを読みナチュラルキーをセット | ナチュラルキーからマスタを読みサロゲートキーをセット |
更新なし | サロゲートキーからマスタを読みナチュラルキーをセット | 何もしない |
いずれも「更新あり」のときにマスタを読むかは微妙です。どちらもマスタのレコードを特定することができますが、システム的にはサロゲートキーがその役割を担うべきなので、サロゲートキーを優先としています。
トリガーに INSTEAD OF という指定をしておきます。
この指定によりトリガーは連鎖起動しなくなります。逆に INSTEAD OF の指定がなければ、トリガーの中で自身のサロゲートキーやナチュラルキーを更新するために連鎖起動が起きてしまいます。
CREATE TRIGGER trIU_T010_受注明細 ON T010_受注明細 INSTEAD OF INSERT, UPDATE AS BEGIN /* 挿入・変更されるデータを一時テーブルにコピー */ /* どれか変更されたとき、あるいはすべてが変更されてないとき */ IF UPDATE(M000_ID) UPDATE #inserted /* 他のマスタ、トランをしらべ、同様に一時テーブル #inserted を更新する */ DROP TABLE #inserted; END; |
マスタのナチュラルキーの変更も自動更新する。
マスタのナチュラルキーが変更になったとき自動更新するトリガーも作ります。
これによってコード体系の変更などがあっても、自動的に更新することができます。
CREATE TRIGGER trU_M000_製品 ON M000_製品 FOR UPDATE AS BEGIN /* ナチュラルキーが何も変更されていないとき */ /* 同様に M000_製品 を利用している全マスタ、トランを…… */ END; |
※ テストするとき、ツールによってはトリガーが書き換えた内容は再読み込みしないと正しく表示されませんので注意が必要です。
まとめ
トリガーで整合性の維持を行うには、もちろん、すべてのテーブルのトリガーを設定する必要があります。これまで書いてきたようなトリガーをいちいち考えて作るのであれば、1つのテーブルでも結構な量になりますのでほぼ不可能です。しかし、テーブル定義書から自動出力を行うなら仕様書とプログラムは完全に一致しますし、ひな形さえできればすぐに作れます。
SQLServerでは、トリガー実行中に inserted deleted という、メモリー上の読み取り専用の特殊テーブルができます。SQLServerのトリガーは SQL文レベルで実行されますので、inserted deletedテーブルの中には複数のレコードが入ります。
inserted deletedテーブルの中に複数のレコードがあるということを意識する必要はありますが、複数のレコードがあってもカーソルを作ってループしたりする必要はありません。
なお、弊社の取引先に、まだSQLServer2005を使っているところがあるので、SQLServer2005でできる範囲で書きました。
SQLServer2000もあるけど……。SQLServer2000 はOLAPも使えないし、さすがにキツイ(苦笑)。
文法解説は嫌いなのでほとんどしていません。必要に応じてお使いのRDBMSのリファレンスを読んでください。