觸發器
概述
? ? 觸發器為特殊類型的存儲過程,可在執行語言事件時自動生效。SQL Server 包括三種常規類型的觸發器:DML 觸發器、DDL 觸發器和登錄觸發器。
當服務器或數據庫中發生數據定義語言 (DDL) 事件時將調用 DDL 觸發器。登錄觸發器將為響應 LOGON 事件而激發存儲過程。與 SQL Server 實例建立用戶會話時將引發此事件。
當數據庫中發生數據操作語言 (DML) 事件時將調用 DML 觸發器。DML 事件包括在指定表或視圖中修改數據的 INSERT 語句、UPDATE 語句或 DELETE 語句。DML 觸發器可以查詢其他表,還可以包含復雜的 Transact-SQL 語句。將觸發器和觸發它的語句作為可在觸發器內回滾的單個事務對待。如果檢測到錯誤(例如,磁盤空間不足),則整個事務即自動回滾。
?
步驟
?? 本文主要講述DML觸發器,DML觸發器有兩種:AFTER,INSTEAD OF觸發器,同時DML 觸發器使用 deleted 和 inserted 邏輯(概念)表。 它們在結構上類似于定義了觸發器的表,即對其嘗試執行了用戶操作的表。 在 deleted 和 inserted 表保存了可能會被用戶更改的行的舊值或新值。
- 對于INSERT 操作,inserted保留新增的記錄,deleted無記錄
- 對于DELETE 操作,inserted無記錄,deleted保留被刪除的記錄
- 對于UPDATE操作,inserted保留修改后的記錄,deleted保留修改前的記錄
? ? 一.語法
CREATE TRIGGER [ schema_name . ] trigger_name ON { table | view } [ WITH <dml_trigger_option> [ ,...n ] ] { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] } AS { sql_statement [ ; ] [ ,...n ] [ ; ] > }
?
? ? ?二.創建表
CREATE TABLE Class (Cno INT PRIMARY KEY , Cname nvarchar ( 20 ) not null ) go CREATE TABLE Student (SNO INT PRIMARY KEY IDENTITY ( 1 , 1 ), Sname CHAR ( 10 ) not null , Age int not null , Sex char ( 2 ) not null , Cno int NOT NULL ) ALTER TABLE Student ADD CONSTRAINT FK_SNO_Cno FOREIGN KEY (Cno) REFERENCES Class(Cno) go
AFTER觸發器
AFTER 指定 DML 觸發器僅在觸發 SQL 語句中指定的所有操作都已成功執行時才被觸發。 所有的引用級聯操作和約束檢查也必須在激發此觸發器之前成功完成。
如果僅指定 FOR 關鍵字,則 AFTER 為默認值。
不能對視圖定義 AFTER 觸發器
- insert觸發
當向Class表中插入一條數據時,獲取插入的cno,同時向Student表中插入一條數據
IF OBJECT_ID(' TR_Class_insert ','TR') IS NOT NULL
DROP TRIGGER TR_Class_insert
G0
CREATE TRIGGER TR_Class_insert on Class AFTER INSERT AS BEGIN DECLARE @Cno INT SELECT @Cno = Cno FROM inserted -- --獲取插入的數據CNO INSERT INTO Student(Sname,Age,Sex,Cno) VALUES ( ' 李明 ' , 20 , ' 男 ' , @Cno ) END go INSERT INTO Class SELECT 101 , ' 一班 ' SELECT * FROM Class SELECT * FROM Student
- UPDATE觸發
獲取修改的Age值,如果Age為負數則執行回滾操作,否則輸出修改前后的Age值
IF OBJECT_ID ( ' TR_Student_update ' , ' TR ' ) IS NOT NULL DROP TRIGGER TR_Student_update GO CREATE TRIGGER TR_Student_update on Student AFTER UPDATE AS BEGIN DECLARE @Age_old int , @Age_new int SELECT @Age_old = Age from deleted -- --獲取修改前的 SELECT @Age_new = Age FROM inserted -- --獲取更改后的數據 if @Age_new < 0 begin print ' 年齡不能為負數 ' rollback ; end else BEGIN print @Age_old print @Age_new END END go update Student set Age =- 20 where SNO = 1 SELECT * FROM Class SELECT * FROM Student update Student set Age = 25 where SNO = 1 SELECT * FROM Class SELECT * FROM Student
- ?delete觸發
獲取被刪除的數據,返回錯誤提示,該步驟正好驗證了“所有的引用級聯操作和約束檢查也必須在激發此觸發器之前成功完成”,該步驟不會返回制定的錯誤提示,因為被刪除的數據作用于外鍵約束,所以先于觸發器操作執行外鍵約束,返回約束錯誤提示,并執行回滾.
IF OBJECT_ID ( ' TR_Class_delete ' , ' TR ' ) IS NOT NULL DROP TRIGGER TR_Class_delete GO CREATE TRIGGER TR_Class_delete on Class AFTER DELETE AS BEGIN DECLARE @Cno int SELECT @Cno = Cno from DELETED -- -獲取被刪除的記錄 IF @Cno>0 begin RAISERROR ( ' 數據不能被刪除,被用于外鍵約束 ' , 16 , 10 ); rollback -- --執行回滾操作 end END SELECT * FROM Class SELECT * FROM Student DELETE FROM Class where CNO = 101 SELECT * FROM Class SELECT * FROM Student
對Student表建立外鍵約束,用于級聯操作 ON DELETE,對于表的級聯刪除更新操作這里就不講述了
刪除之前創建的外鍵約束,并創建具有級聯更新刪除操作的外鍵約束
alter table student drop constraint FK_SNO_Cno ALTER TABLE Student ADD CONSTRAINT FK_SNO_Cno FOREIGN KEY (Cno) REFERENCES Class (Cno) ON DELETE CASCADE ON UPDATE CASCADE
再執行刪除語句,返回制定錯誤提示“數據不能被刪除,被用于外鍵約束”并執行回滾操作
DELETE FROM Class where CNO = 101 SELECT * FROM Class SELECT * FROM Student
?
INSTEAD OF觸發器
指定執行 DML 觸發器而不是觸發 SQL 語句,因此,其優先級高于觸發語句的操作。? 不能為 DDL 或登錄觸發器指定 INSTEAD OF。
對于表或視圖,每個 INSERT、UPDATE 或 DELETE 語句最多可定義一個 INSTEAD OF 觸發器。? 但是,可以為具有自己的 INSTEAD OF 觸發器的多個視圖定義視圖。
INSTEAD OF 觸發器不可以用于使用 WITH CHECK OPTION 的可更新視圖。? 如果將 INSTEAD OF 觸發器添加到指定了 WITH CHECK OPTION 的可更新視圖中,則 SQL Server 將引發錯誤。 ? 用戶須用 ALTER VIEW 刪除該選項后才能定義 INSTEAD OF 觸發器
對于 INSTEAD OF 觸發器,不允許對具有指定級聯操作 ON DELETE 的引用關系的表使用 DELETE 選項。? 同樣,也不允許對具有指定級聯操作 ON UPDATE 的引用關系的表使用 UPDATE 選項
- ?Insert 觸發
-- -----insert 觸發 -- --刪除已有的instead of觸發器 declare @name nvarchar ( 100 ) select @name = name from sys.triggers where object_name (parent_id) = ' student ' and is_instead_of_trigger = 1 set @name = ' drop trigger ' + @name exec ( @name ) IF OBJECT_ID ( ' TR_Student_instead_insert ' , ' TR ' ) IS NOT NULL DROP TRIGGER TR_Student_instead_insert GO CREATE TRIGGER TR_Student_instead_insert on Student INSTEAD OF insert AS BEGIN SELECT * into T_back from inserted -- --獲取即將插入的數據 END select * from Student select * from Class INSERT INTO Student(Sname,Age,Sex,Cno) values ( ' 張三 ' , 23 , ' 男 ' , 102 ) select * from T_back
?
- ?delete觸發
創建觸發器失敗,因為之前創建外鍵約束時添加了on delete cascade
IF OBJECT_ID ( ' TR_Student_instead_delete ' , ' TR ' ) IS NOT NULL DROP TRIGGER TR_Student_instead_delete GO CREATE TRIGGER TR_Student_instead_delete on Student INSTEAD OF DELETE AS BEGIN DECLARE @Cno int SELECT @Cno = Cno from DELETED -- -獲取被刪除的記錄 IF EXISTS ( SELECT * FROM Class where Cno = @cno ) begin rollback -- --執行回滾操作 RAISERROR ( ' 數據不能被刪除,被用于外鍵約束1 ' , 16 , 10 ); end END 消息 2113 ,級別 16 ,狀態 1 ,過程 TR_Student_instead_delete,第 10 行 因為表 ' Student ' 的 FOREIGN KEY 使用級聯 DELETE 或 UPDATE ,所以無法對該表 創建 INSTEAD OF DELETE 或 INSTEAD OF UPDATE TRIGGER ' TR_Student_instead_delete ' 。
重建外鍵約束,刪除級聯
alter table student drop constraint FK_SNO_Cno ALTER TABLE Student ADD CONSTRAINT FK_SNO_Cno FOREIGN KEY (Cno) REFERENCES Class (Cno)
?
- ?UPDATE觸發
-- ---同一張表中只能定義一個instead of 觸發器,刪除表之前創建的instead of 觸發 declare @name nvarchar ( 100 ) select @name = name from sys.triggers where object_name (parent_id) = ' student ' and is_instead_of_trigger = 1 set @name = ' drop trigger ' + @name exec ( @name ) IF OBJECT_ID ( ' TR_Student_instead_update ' , ' TR ' ) IS NOT NULL DROP TRIGGER TR_Student_instead_update GO CREATE TRIGGER TR_Student_instead_update on Student INSTEAD OF update AS BEGIN DECLARE @Age_del int , @Age_up int SELECT @Age_del = Age from DELETED -- -獲取被更改的記錄 SELECT @Age_up = Age from Inserted begin print @Age_del print @Age_up select * from Student -- --查詢數據是否被更改 end END -- --查詢更新前的表數據 select * from student SNO Sname Age Sex Cno 13 李明 22 男 101 update Student set age = -2 where CNO = 101
----對于前面定義的after觸發器age不能為負數也不會執行,instead of 觸發器高于執行語句,高于after 觸發
SNO Sname Age Sex Cno 13 李明 22 男 101 select * from student SNO Sname Age Sex Cno 13 李明 22 男 101 ( 1 行受影響) 22 -2 ( 1 行受影響) ( 1 行受影響) 當表上面定義了instead of 觸發器,指定執行 DML 觸發器而不是觸發 SQL 語句,因此,其優先級高于觸發語句的操作,而且也不會執行表上面定義的after觸發器
?
?創建帶字段判斷的觸發器, 根據對特定列的 UPDATE 或 INSERT 修改來執行某些操作
-- ----創建字段更新判斷的update觸發器 ALTER TABLE Class ADD Address nvarchar ( 50 ) IF OBJECT_ID ( ' TR_Class_Update ' , ' TR ' ) IS NOT NULL DROP TRIGGER TR_Class_Update GO CREATE TRIGGER TR_Class_Update on Class AFTER UPDATE AS BEGIN IF UPDATE (Cname) or UPDATE (Address) BEGIN RAISERROR ( ' 數據不能被修改 ' , 16 , 10 ) ROLLBACK END END SELECT * FROM Class UPDATE Class set Address = ' 5棟101 ' where Cno = 101 SELECT * FROM Class
?
總結
? ?
雖然觸發器功能強大,輕松可靠地實現許多復雜的功能,同時過多觸發器會造成數據庫及應用程序的維護困難,同時對觸發器過分的依賴,勢必影響數據庫的結構,同時增加了維護的復雜程序.
?
備注: ??? 作者: 沉寂的石頭 ??? 博客: http://www.cnblogs.com/chenmh 歡迎大家轉載,但轉載時必須注明文章來源,且在文章開頭明顯處給明鏈接,否則保留追究責任的權利。 歡迎大家拍磚 |
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
