SQL Server 觸發器


概述

   觸發器為特殊類型的存儲過程,可在執行語言事件時自動生效。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>0begin
  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 使用級聯 DELETEUPDATE,所以無法對該表 創建 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    李明          22101

update Student
set age=-2
where CNO=101

----對於前面定義的after觸發器age不能為負數也不會執行,instead of 觸發器高於執行語句,高於after 觸發
SNO Sname Age Sex Cno
13 李明 22101 select * from student SNO Sname Age Sex Cno 13 李明 22101 (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

 

注意:

  • After觸發器只能用於數據表中,Instead of觸發器可以用於數據表和視圖上,且兩種觸發器都不能建立在臨時表上。
  • 一個數據表可以建多個觸發器,但是一個觸發器只能對應一張表。
  • 在同一個數據表中,對應每一個操作(insert,delete,update)可以建多個After觸發器,但是只能建一個Instead of觸發器。
  • Instead of觸發器不會執行原本的SQL語句,只執行觸發器中定義的操作。
  • 如果針對某個操作既定義了After觸發器又定義了Instead of觸發器,那么Instead of觸發器一定會觸發,而After觸發器不一定會觸發。
  • Truncate table操作不會激活delete類型的觸發,因為它不進行刪除日志寫入操作。
  • writetext語句不能觸發insert和update類型觸發器。

DLL觸發器

 

像常規觸發器一樣,DDL 觸發器將激發存儲過程以響應事件。但與 DML 觸發器不同的是,它們不會為響應針對表或視圖的 UPDATE、INSERT 或 DELETE 語句而激發。相反,它們將為了響應各種數據定義語言 (DDL)事件而激發。這些事件主要與以關鍵字 CREATE、ALTER 和 DROP 開頭的 Transact-SQL 語句對應。執行 DDL 式操作的系統存儲過程也可以激發 DDL 觸發器。

DLL觸發器有針對服務器級別的觸發事件,也有針對數據庫級別的觸發事件。

語法:
CREATE TRIGGER trigger_name 
ON { ALL SERVER | DATABASE } 
[ WITH <ddl_trigger_option> [ ,...n ] ]
{ FOR | AFTER } { event_type | event_group } [ ,...n ]
AS { sql_statement  [ ; ] [ ,...n ] | EXTERNAL NAME < method specifier >  [ ; ] }

注意:DDL觸發器中不能指定instead of觸發器

 

DDL觸發器在服務器級別下可用的事件有:

DDL觸發器在數據庫級別下可用的事件有:

 

----阻止刪除和修改任意表
CREATE TRIGGER TR_safety ---觸發器名
ON DATABASE ---不用修改
FOR DROP_TABLE, ALTER_TABLE 
AS 
BEGIN
   PRINT 'You must disable Trigger "safety" to drop or alter tables!' 
   ROLLBACK ;
END
 
        

 

ALTER TRIGGER TR_safety_On_Database
ON ALL SERVER
FOR DROP_DATABASE,ALTER_DATABASE
AS 
BEGIN
   PRINT 'You must disable Trigger "TR_safety_On_Database" to drop database or alter database!' 
   ROLLBACK ;
END

 

注意:在當前數據庫下執行

總結

雖然觸發器功能強大,輕松可靠地實現許多復雜的功能,同時過多觸發器會造成數據庫及應用程序的維護困難,同時對觸發器過分的依賴,勢必影響數據庫的結構,同時增加了維護的復雜程序.

 附加一張所有觸發事件圖:

 

 

 

備注:

    作者:pursuer.chen

    博客:http://www.cnblogs.com/chenmh

本站點所有隨筆都是原創,歡迎大家轉載;但轉載時必須注明文章來源,且在文章開頭明顯處給明鏈接,否則保留追究責任的權利。

《歡迎交流討論》


免責聲明!

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



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