SQL Server トリガーの基本と実装

トリガーは特殊な種類のストアドプロシージャで、通常のストアドプロシージャとは異なります。トリガーはイベントによって自動的に呼び出され実行されますが、ストアドプロシージャは名前で呼び出されます。

Ø トリガーとは

トリガーはテーブルに対する挿入、更新、削除操作時に自動的に実行される特殊なストアドプロシージャです。トリガーは通常、CHECK制約よりも複雑な制約チェックに使用されます。トリガーと通常のストアドプロシージャの違いは、特定のテーブルに対して操作(update、insert、deleteなど)が行われた際に、システムが自動的に対応するトリガーを実行することです。SQL Server 2005ではトリガーは2種類に分類されます:DMLトリガーとDDLトリガー。DDLトリガーはcreate、alter、dropなどのデータ定義言語ステートメントによって起動されます。

DMLトリガーは以下のように分類されます:

  1. afterトリガー(後実行トリガー)

a. insertトリガー

b. updateトリガー

c. deleteトリガー

  1. instead ofトリガー(代わり実行トリガー)

afterトリガーはinsert、update、delete操作の後にのみ実行され、テーブルでのみ定義できます。instead ofトリガーは定義された操作(insert、update、delete)を実行せず、トリガー本体のみを実行します。テーブルだけでなくビューにも定義できます。

トリガーには2つの特殊なテーブルがあります:挿入テーブル(insertedテーブル)と削除テーブル(deletedテーブル)。これらは論理テーブルであり、仮想テーブルです。システムがメモリ上に作成しますが、データベースには保存されません。これらのテーブルは読み取り専用で、データの読み取りのみ可能で変更はできません。これらのテーブルの構造は、トリガーが適用されるテーブルの構造と常に同じです。トリガーの処理が完了すると、これらのテーブルは削除されます。insertedテーブルには挿入または更新後のデータが格納され、deletedテーブルには更新前または削除されたデータが格納されます。

テーブル操作 Inserted論理テーブル Deleted論理テーブル
レコード追加(insert) 追加されたレコード なし
レコード削除(delete) なし 削除されたレコード
レコード更新(update) 更新後のレコード 更新前のレコード

更新操作では、まずレコードを削除してから新しいレコードを挿入します。そのため、insertedとdeletedテーブルの両方に更新後のデータレコードが存在します。重要な点は:トリガー自体がトランザクションであるため、トリガー内でデータの変更に対して特殊なチェックを実行できます。条件を満たさない場合は、トランザクションをロールバックして操作を取り消すことができます。

Ø トリガーの作成

構文

create trigger トリガー名<br></br>on テーブル名<br></br>with encryption --トリガーの暗号化<br></br>    for update...<br></br>as<br></br>    Transact-SQL

INSERTトリガーの作成

--INSERTトリガーの作成<br></br>if (object_id('ins_classes_trigger', 'tr') is not null)<br></br>    drop trigger ins_classes_trigger<br></br>go<br></br>create trigger ins_classes_trigger<br></br>on classes<br></br>    for insert --挿入トリガー<br></br>as<br></br>    --変数の定義<br></br>    declare @class_id int, @class_name varchar(20), @temp_id int;<br></br>    --insertedテーブルから挿入されたレコード情報を取得<br></br>    select @class_id = id, @class_name = name from inserted;<br></br>    set @class_name = @class_name + convert(varchar, @class_id);<br></br>    set @temp_id = @class_id / 2;    <br></br>    insert into students values(@class_name, 18 + @class_id, @temp_id, @class_id);<br></br>    print '学生が正常に追加されました!';<br></br>go<br></br>--データの挿入<br></br>insert into classes values('6組', getDate());<br></br>--データの確認<br></br>select * from classes;<br></br>select * from students order by id;

INSERTトリガーは、insertedテーブルに新しく挿入されたレコードを追加します。

DELETEトリガーの作成

--DELETEトリガーの作成<br></br>if (object_id('del_classes_trigger', 'TR') is not null)<br></br>    drop trigger del_classes_trigger<br></br>go<br></br>create trigger del_classes_trigger<br></br>on classes<br></br>    for delete --削除トリガー<br></br>as<br></br>    print 'データをバックアップ中……';    <br></br>    if (object_id('classes_backup', 'U') is not null)<br></br>        --classes_backupが存在する場合は直接データを挿入<br></br>        insert into classes_backup select name, createDate from deleted;<br></br>    else<br></br>        --classes_backupが存在しない場合は作成してから挿入<br></br>        select * into classes_backup from deleted;<br></br>    print 'データバックアップが成功しました!';<br></br>go<br></br>--<br></br>--影響を受けた行数を表示しない<br></br>--set nocount on;<br></br>delete classes where name = '6組';<br></br>--データの確認<br></br>select * from classes;<br></br>select * from classes_backup;

DELETEトリガーは、データを削除する際に、削除されたデータをdeletedテーブルに保存します。

UPDATEトリガーの作成

--UPDATEトリガーの作成<br></br>if (object_id('upd_classes_trigger', 'TR') is not null)<br></br>    drop trigger upd_classes_trigger<br></br>go<br></br>create trigger upd_classes_trigger<br></br>on classes<br></br>    for update<br></br>as<br></br>    declare @old_class_name varchar(20), @new_class_name varchar(20);<br></br>    --更新前のデータ<br></br>    select @old_class_name = name from deleted;<br></br>    if (exists (select * from students where name like '%'+ @old_class_name + '%'))<br></br>        begin<br></br>            --更新後のデータ<br></br>            select @new_class_name = name from inserted;<br></br>            update students set name = replace(name, @old_class_name, @new_class_name) where name like '%'+ @old_class_name + '%';<br></br>            print '関連データの更新が成功しました!';<br></br>        end<br></br>    else<br></br>        print 'studentsテーブルを更新する必要はありません!';<br></br>go<br></br>--データの確認<br></br>select * from students order by id;<br></br>select * from classes;<br></br>update classes set name = '六組' where name = '6組';

UPDATEトリガーは、データを更新した後、更新前のデータをdeletedテーブルに、更新後のデータをinsertedテーブルに保存します。

列レベルのUPDATEトリガー

if (object_id('upd_class_column_trigger', 'TR') is not null)<br></br>    drop trigger upd_class_column_trigger<br></br>go<br></br>create trigger upd_class_column_trigger<br></br>on classes<br></br>    for update<br></br>as<br></br>    --列レベルトリガー:クラス作成日が更新されたかどうかを確認<br></br>    if (update(createDate))<br></br>    begin<br></br>        raisError('システム通知:クラス作成日時は変更できません!', 16, 11);<br></br>        rollback tran;<br></br>    end<br></br>go<br></br>--テスト<br></br>select * from students order by id;<br></br>select * from classes;<br></br>update classes set createDate = getDate() where id = 3;<br></br>update classes set name = '四組' where id = 7;

列レベルのトリガーでは、update()関数を使用して特定の列が更新されたかどうかを判断できます。

INSTEAD OFトリガー

INSTEAD OFトリガーは、定義された操作(insert、update、delete)を実行せず、トリガー本体のみを実行します。

作成構文

create trigger トリガー名<br></br>on テーブル名<br></br>with encryption<br></br>    instead of update...<br></br>as<br></br>    T-SQL

INSTEAD OFトリガーの作成

if (object_id('instead_of_trigger', 'TR') is not null)<br></br>    drop trigger instead_of_trigger<br></br>go<br></br>create trigger instead_of_trigger<br></br>on classes<br></br>    instead of delete/*, update, insert*/<br></br>as<br></br>    declare @class_id int, @class_name varchar(20);<br></br>    --削除される情報をクエリし、変数に代入<br></br>    select @class_id = id, @class_name = name from deleted;<br></br>    print 'ID: ' + convert(varchar, @class_id) + ', 名前: ' + @class_name;<br></br>    --まずstudentsの情報を削除<br></br>    delete students where cid = @class_id;<br></br>    --次にclassesの情報を削除<br></br>    delete classes where id = @class_id;<br></br>    print 'ID: ' + convert(varchar, @class_id) + ', 名前: ' + @class_name + ' の情報が正常に削除されました!';<br></br>go<br></br>--テスト<br></br>select * from students order by id;<br></br>select * from classes;<br></br>delete classes where id = 7;

カスタムメッセージの表示 raiserror

if (object_id('msg_trigger', 'TR') is not null)<br></br>    drop trigger msg_trigger<br></br>go<br></br>create trigger msg_trigger<br></br>on students<br></br>    after insert, update<br></br>as raisError('msg_triggerがトリガーされました', 16, 10);<br></br>go<br></br>--テスト<br></br>insert into students values('lily', 22, 1, 7);<br></br>update students set sex = 0 where name = 'lucy';<br></br>select * from students order by id;

トリガーの変更

alter trigger msg_trigger<br></br>on students<br></br>after delete<br></br>as raisError('msg_triggerがトリガーされました', 16, 10);<br></br>go<br></br>--テスト<br></br>delete from students where name = 'lucy';

トリガーの有効化と無効化

--トリガーの無効化<br></br>disable trigger msg_trigger on students;<br></br>--トリガーの有効化<br></br>enable trigger msg_trigger on students;

作成されたトリガー情報のクエリ

--既存のトリガーのクエリ<br></br>select * from sys.triggers;<br></br>select * from sys.objects where type = 'TR';<br></br><br></br>--トリガーのトリガーイベントの確認<br></br>select te.* from sys.trigger_events te join sys.triggers t<br></br>on t.object_id = te.object_id<br></br>where t.parent_class = 0 and t.name = 'valid_data_trigger';<br></br><br></br>--トリガー作成ステートメントの確認<br></br>exec sp_helptext 'msg_trigger';

例:挿入データの検証

if ((object_id('valid_data_trigger', 'TR') is not null))<br></br>    drop trigger valid_data_trigger<br></br>go<br></br>create trigger valid_data_trigger<br></br>on students<br></br>after insert<br></br>as<br></br>    declare @age int,<br></br>            @name varchar(20);<br></br>    select @name = s.name, @age = s.age from inserted s;<br></br>    if (@age < 18)<br></br>    begin<br></br>        raisError('挿入されたデータの年齢に問題があります', 16, 1);<br></br>        rollback tran;<br></br>    end<br></br>go<br></br>--テスト<br></br>insert into students values('forest', 2, 0, 7);<br></br>insert into students values('forest', 22, 0, 7);<br></br>select * from students order by id;

例:操作ログ

if (object_id('operation_log', 'U') is not null)<br></br>    drop table operation_log<br></br>go<br></br>create table operation_log(<br></br>    id int identity(1, 1) primary key,<br></br>    action varchar(20),<br></br>    createDate datetime default getDate()<br></br>)<br></br>go<br></br>if (exists (select * from sys.objects where name = 'student_log_trigger'))<br></br>    drop trigger student_log_trigger<br></br>go<br></br>create trigger student_log_trigger<br></br>on students<br></br>after insert, update, delete<br></br>as<br></br>    if ((exists (select 1 from inserted)) and (exists (select 1 from deleted)))<br></br>    begin<br></br>        insert into operation_log(action) values('updated');<br></br>    end<br></br>    else if (exists (select 1 from inserted) and not exists (select 1 from deleted))<br></br>    begin<br></br>        insert into operation_log(action) values('inserted');<br></br>    end<br></br>    else if (not exists (select 1 from inserted) and exists (select 1 from deleted))<br></br>    begin<br></br>        insert into operation_log(action) values('deleted');<br></br>    end<br></br>go<br></br>--テスト<br></br>insert into students values('king', 22, 1, 7);<br></br>update students set sex = 0 where name = 'king';<br></br>delete students where name = 'king';<br></br>select * from operation_log;<br></br>select * from students order by id;

タグ: SQL Server トリガー データベース ストアドプロシージャ DML

5月19日 04:39 投稿