觸發器簡介: 觸發器是一種特殊的存儲過程,它的執行不是由程式調用,也不是手動執行,而是由事件來觸發。觸發器是當對某一個表進行操作。例如:update、insert、delete這些操作的時候,系統會自動調用執行該表上對應的觸發器。 觸發器分類: 1、DML( 數據操縱語言 Data Manipula ...
觸發器簡介:
觸發器是一種特殊的存儲過程,它的執行不是由程式調用,也不是手動執行,而是由事件來觸發。觸發器是當對某一個表進行操作。例如: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 標識符,並且必須作為具有程式集可見性的程式集中的類存在。
先看看示例數據:
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