SQL Server 創建觸發器(trigger)


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM