from:https://www.cnblogs.com/Brambling/archive/2017/04/21/6741666.html
觸發器簡介:
觸發器是一種特殊的存儲過程,它的執行不是由程序調用,也不是手動執行,而是由事件來觸發。觸發器是當對某一個表進行操作。例如:update、insert、delete這些操作的時候,系統會自動調用執行該表上對應的觸發器。
觸發器分類:
1、DML( 數據操縱語言 Data Manipulation Language)觸發器:是指觸發器在數據庫中發生 DML 事件時將啟用。DML事件是指在表或視圖中對數據進行的 insert、update、delete 操作的語句。
2、DDL(數據定義語言 Data Definition Language)觸發器:是指當服務器或數據庫中發生 DDL 事件時將啟用。DDL事件是指在表或索引中的 create、alter、drop 操作語句。
3、登陸觸發器:是指當用戶登錄 SQL SERVER 實例建立會話時觸發。如果身份驗證失敗,登錄觸發器不會觸發。
其中 DML 觸發器比較常用,根據 DML 觸發器觸發的方式不同又分為以下兩種情況:
after 觸發器(之后觸發):其中 after 觸發器要求只有執行 insert、update、delete 某一操作之后觸發器才會被觸發,且只能定義在表上。
instead of 觸發器 (之前觸發):instead of 觸發器並不執行其定義的操作(insert、update、delete)而僅是執行觸發器本身。可以在表或視圖上定義 instead of 觸發器。
DML 觸發器有兩個特殊的表:插入表(instered)和刪除表(deleted),這兩張表是邏輯表。這兩個表是建立在數據庫服務器的內存中,而且兩張表的都是只讀的。這兩張表的結構和觸發器所在的數據表的結構是一樣的。當觸發器完成工作后,這兩張表就會被刪除。Inserted 表的數據是插入或是修改后的數據,而 deleted 表的數據是更新前的或是已刪除的數據。

AFTER 觸發器語法:
1 CREATE [ OR ALTER ] TRIGGER [ schema_name . ]trigger_name
2 ON { table }
3 [ WITH <dml_trigger_option> [ ,...n ] ]
4 { FOR | AFTER }
5 { [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
6 AS { sql_statement [ ; ] [ ,...n ] }
7
8 <dml_trigger_option> ::=
9 [ NATIVE_COMPILATION ]
10 [ SCHEMABINDING ]
11 [ EXECUTE AS Clause ]
INSTEAD OF 觸發器語法:
1 CREATE [ OR ALTER ] TRIGGER [ schema_name . ]trigger_name
2 ON { table | view }
3 [ WITH <dml_trigger_option> [ ,...n ] ]
4 { FOR | AFTER | INSTEAD OF }
5 { [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
6 [ WITH APPEND ]
7 [ NOT FOR REPLICATION ]
8 AS { sql_statement [ ; ] [ ,...n ] | EXTERNAL NAME <method specifier [ ; ] > }
9
10 <dml_trigger_option> ::=
11 [ ENCRYPTION ]
12 [ EXECUTE AS Clause ]
13
14 <method_specifier> ::=
15 assembly_name.class_name.method_name
DDL 觸發器語法:
1 CREATE [ OR ALTER ] TRIGGER trigger_name
2 ON { ALL SERVER | DATABASE }
3 [ WITH <ddl_trigger_option> [ ,...n ] ]
4 { FOR | AFTER } { event_type | event_group } [ ,...n ]
5 AS { sql_statement [ ; ] [ ,...n ] | EXTERNAL NAME < method specifier > [ ; ] }
6
7 <ddl_trigger_option> ::=
8 [ ENCRYPTION ]
9 [ EXECUTE AS Clause ]
登陸觸發器語法:
1 CREATE [ OR ALTER ] TRIGGER trigger_name
2 ON ALL SERVER
3 [ WITH <logon_trigger_option> [ ,...n ] ]
4 { FOR| AFTER } LOGON
5 AS { sql_statement [ ; ] [ ,...n ] | EXTERNAL NAME < method specifier > [ ; ] }
6
7 <logon_trigger_option> ::=
8 [ ENCRYPTION ]
9 [ EXECUTE AS Clause ]
參數:
CREATE OR ALTER:
創建或者有條件的修改觸發器(即要修改的觸發器必須已經存在)。
schema_name:
DML觸發器所屬的模式的名稱(即所有者,例如:dbo)。
trigger_name:
是觸發器的名稱。
table | view:
是執行 DML 觸發器的表或視圖,有時稱為觸發器表或觸發器視圖。指定表格或視圖的完全限定名稱是可選的。視圖只能由 INSTEAD OF 觸發器引用。
DATABASE:
將 DDL 觸發器的范圍應用於當前數據庫。如果指定,觸發器會在當前數據庫中發生 event_type 或 event_group 時觸發。
ALL SERVER:
將 DDL 或登錄觸發器的作用域應用於當前服務器。如果指定,觸發器會在當前服務器的任何地方發生 event_type 或 event_group 時觸發。
WITH ENCRYPTION:
加密 CREATE TRIGGER 語句的文本。使用 WITH ENCRYPTION 可以防止觸發器作為 SQL Server 復制的一部分進行發布。無法為 CLR 觸發器指定 WITH ENCRYPTION。
EXECUTE AS:
指定執行觸發器的安全上下文。以便能夠控制 SQL Server 實例用於驗證觸發器引用的任何數據庫對象的權限的用戶帳戶。
NATIVE_COMPILATION:
表示觸發器是本地編譯的。
SCHEMABINDING:
指定觸發器引用的表不能被刪除或更改。
FOR | AFTER:
AFTER 指定僅在觸發 SQL 語句中指定的所有操作成功執行時觸發 DML 觸發器。所有引用級聯操作和約束檢查在此觸發器觸發之前也必須成功。當 FOR 是指定的唯一關鍵字時,AFTER 是默認值。視圖無法定義AFTER觸發器。
INSTEAD OF:
指定執行 DML 觸發器而不是觸發 SQL 語句,因此覆蓋觸發語句的操作。無法為 DDL 或登錄觸發器指定 INSTEAD OF。
對於 INSTEAD OF 觸發器,在具有指定級聯動作 ON DELETE 的引用關系的表上不允許使用 DELETE 選項。類似地,在具有指定級聯動作 ON UPDATE 的引用關系的表上,不允許 UPDATE 選項。
{[DELETE] [,] [INSERT] [,] [UPDATE]} :
指定在針對此表或視圖進行嘗試時激活 DML 觸發器的數據修改語句。必須至少指定一個選項。在觸發器定義中允許以任何順序對這些選項進行任意組合。
event_type:
是執行后導致 DDL 觸發器觸發的 Transact-SQL 語言事件的名稱。
event_group:
是 Transact-SQL 語言事件的預定義分組的名稱。屬於任何 Transact-SQL 語言事件執行后的 DDL 觸發器觸發 event_group。
sql_statement:
是觸發條件和動作。觸發條件指定附加條件,以確定嘗試的 DML,DDL 或登錄事件是否導致執行觸發器操作。
<method_specifier>:
對於 CLR 觸發器,指定要與觸發器綁定的程序集的方法。該方法不得不引用任何參數並返回 void。class_name 必須是有效的 SQL Server 標識符,並且必須作為具有程序集可見性的程序集中的類存在。
以下是DML觸發器的使用,先看看示例數據:

insert 觸發器:
1 if(OBJECT_ID('trigger_Stu_Insert') is not null) -- 判斷名為 trigger_Stu_Insert 的觸發器是否存在
2 drop trigger trigger_Stu_Insert -- 刪除觸發器
3 go
4 create trigger trigger_Stu_Insert
5 on Student -- 指定創建觸發器的表
6 for insert -- insert 觸發器,也可以寫為 after insert
7 as
8
9 declare @C_Id int
10 declare @S_Id int
11
12 select @C_Id=C_Id from Course where C_Name='SQL' -- 獲取課程為 SQL 的ID
13 select @S_Id=S_Id from inserted --插入一條學生的數據,那么這條數據就存在 inserted 這個表中
14
15 select @C_Id
16 select @S_Id
17
18 select * from inserted
19
20 update Student set C_S_Id=@C_Id where S_Id=@S_Id
21 go
22
23 insert into Student(S_StuNo,S_Name,S_Sex,S_Height,S_BirthDate)
24 values('016','大熊','男','210','2017-01-01')
25
26 select * from Student
27 select * from Course


這個例子是:當 Student 表新增一條數據時,修改這條數據的課程ID。
delete 觸發器:
1 if(OBJECT_ID('trigger_Stu_Delete') is not null) -- 判斷名為 trigger_Stu_Delete 的觸發器是否存在
2 drop trigger trigger_Stu_Delete -- 刪除觸發器
3 go
4 create trigger trigger_Stu_Delete
5 on Student -- 指定創建觸發器的表
6 for delete -- delete 觸發器,也可以寫為 after delete
7 as
8
9 declare @C_S_Id int
10
11 select @C_S_Id=C_S_Id from deleted --刪除的學生的數據就存在 deleted 這個表中
12
13 select @C_S_Id
14
15 select * from deleted
16
17 delete from Course where C_Id=@C_S_Id -- 刪除具有刪除的學生的課程ID的課程
18 go
19
20 delete from Student where C_S_Id='1'
21
22 select * from Student
23 select * from Course


這個例子是:刪除指定課程ID的學生時,並刪除指定課程ID的課程。
update 觸發器:
1 if(OBJECT_ID('trigger_Cou_Update') is not null) -- 判斷名為 trigger_Cou_Update 的觸發器是否存在
2 drop trigger trigger_Cou_Update -- 刪除觸發器
3 go
4 create trigger trigger_Cou_Update
5 on Course -- 指定創建觸發器的表
6 for update -- update 觸發器,也可以寫為 after update
7 as
8
9 declare @C_Id int
10
11 select @C_Id=C_Id from deleted
12
13 select * from deleted -- 修改前的數據就存在 deleted 這個表中
14
15 select * from inserted -- 修改后的數據就存在 inserted 這個表中
16
17 update Student set C_S_Id=@C_Id where C_S_Id is null
18 go
19
20 update Course set C_Name='C#' where C_Id='4'
21
22 select * from Student
23 select * from Course


這個例子是:修改課程名稱時,把課程ID為空(null)的學生的課程ID默認為修改的課程ID。
禁止修改學生學號觸發器,觸發器進行數據回滾:
1 if(OBJECT_ID('trigger_Stu_Update') is not null) -- 判斷名為 trigger_Stu_Update 的觸發器是否存在
2 drop trigger trigger_Stu_Update -- 刪除觸發器
3 go
4 create trigger trigger_Stu_Update
5 on Student -- 指定創建觸發器的表
6 for update -- update 觸發器,也可以寫為 after update
7 as
8 begin try
9 if(UPDATE(S_StuNo)) -- 列級觸發器:判斷是否更新了學生學號(學號不允許更改)
10 begin
11 raiserror(66666,16,1)
12 end
13 end try
14 begin catch
15 select * from deleted -- 修改前的數據就存在 deleted 這個表中
16 select * from inserted -- 修改后的數據就存在 inserted 這個表中
17 rollback tran;
18 end catch
19 go
20
21 update Student set S_StuNo='006' where S_Id='20'
22
23 select * from Student

after 觸發器可以指定多個操作都可以觸發該觸發器。只需要在 for/after 后面添加逗號和觸發器的類型,例如:
1 for update,insert,delete 2 3 after update,insert,delete
instead of 觸發器:
這個觸發器就好玩了,下面先看看數據。

1 if(OBJECT_ID('trigger_Stu_InsteadOf') is not null) -- 判斷名為 trigger_Stu_InsteadOf 的觸發器是否存在
2 drop trigger trigger_Stu_InsteadOf -- 刪除觸發器
3 go
4 create trigger trigger_Stu_InsteadOf
5 on Student -- 指定創建觸發器的表
6 instead of update,insert,delete -- instead of 觸發器
7 as
8 select * from deleted -- 修改前的數據就存在 deleted 這個表中
9 select * from inserted -- 修改后的數據就存在 inserted 這個表中
10 go
11
12 update Student set S_StuNo='006' where S_Id='20'
13
14 insert into Student([S_StuNo],[S_Name],[S_Sex],[S_Height],[S_BirthDate])
15 values('017','清紅','女','180','2017-01-01')
16
17 delete from Student where C_S_Id='5'
18
19 select * from Student

執行上面的語句之后,咦,數據怎么一點變化都沒有?看看上面的介紹。instead of 觸發器是之前觸發。
instead of 觸發器並不執行其定義的操作(insert、update、delete)而僅是執行觸發器本身,並且會覆蓋觸發語句的操作,即 after 觸發器 T-SQL 語句的操作,很明顯我們上面定義的表 Student 的 after 觸發器也沒有效果了,現在理解了這句話了吧。
修改觸發器:
1 alter trigger trigger_Stu_InsteadOf -- 修改觸發器
2 on Student -- 指定創建觸發器的表
3 instead of update,insert,delete -- instead of 觸發器
4 as
5 declare @Count1 int
6 declare @Count2 int
7
8 select @Count1=COUNT(1) from deleted
9 select @Count2=COUNT(1) from inserted
10
11 if(@Count1>0 and @Count2>0)
12 begin
13 select 'update操作'
14 end
15 else if(@Count1>0)
16 begin
17 select 'delete操作'
18 end
19 else if(@Count2>0)
20 begin
21 select 'insert操作'
22 end
23 go
24
25 update Student set S_StuNo='006' where S_Id='20'
26
27 insert into Student([S_StuNo],[S_Name],[S_Sex],[S_Height],[S_BirthDate])
28 values('017','清紅','女','180','2017-01-01')
29
30 delete from Student where C_S_Id='5'
31
32 select * from Student

啟用/禁用觸發器:
1 --禁用觸發器 2 disable trigger trigger_Stu_InsteadOf on Student; -- trigger_Stu_InsteadOf 觸發器名稱 3 --啟用觸發器 4 enable trigger trigger_Stu_InsteadOf on Student; -- trigger_Stu_InsteadOf 觸發器名稱
查詢已存在的觸發器:
1 -- 查詢已存在的觸發器 2 select * from sys.triggers; 3 select * from sys.objects where type = 'TR'; 4 select * from sysobjects where xtype='TR'
1 -- sys.trigger_events 觸發器事件對象視圖 2 select * from sys.trigger_events 3 4 -- 查看觸發器觸發事件對象 5 select a.type_desc,b.* from sys.trigger_events a 6 inner join sys.triggers b on a.object_id = b.object_id 7 where b.name = 'trigger_Stu_Insert'; 8 9 -- 查詢創建觸發器的 T-SQL 文本 10 exec sp_helptext 'trigger_Stu_Insert'
參考:
http://www.cnblogs.com/hoojo/archive/2011/07/20/2111316.html
https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql#remarks-dml-triggers

