觸發器是一種特殊的存儲過程,類似於其它編程語言中的事件函數,SQL Server™ 允許為 INSERT、UPDATE、DELETE 創建觸發器,當在表(視圖)中插入、更新、刪除記錄時,觸發一個或一系列 T-SQL 語句。
本專題的目的並非是向您詳盡地介紹觸發器,而是系統地向您介紹觸發器的常用知識與相關應用,並與您一起探討應用經驗,如果您需要了解觸發器不常用的知識點,請參見 SQL Server™ 聯機叢書(安裝 SQL Server™ 時默認是安裝的)。
在視圖上創建普通觸發器可能會出現“對象無效”的錯誤,實際上,我們不能在視圖上創建 FOR 觸發器,而應該創建 INSTEAD OF 觸發器。在 SQL Server™ 聯機叢書中,是沒有說觸發器不能在視圖上創建的, 並且在語法解釋中表明:在 CREATE TRIGGER 的 ON 之後可以是視圖。 然而,事實似乎並不是如此,很多專家也說觸發器不能在視圖上創建。 我也專門作了測試,的確如此,不管是普通視圖還是索引視圖,都無法在上面創建觸發器. 但是無可厚非的是:當在臨時表或系統表上創建觸發器時會遭到拒絕。
FOR CREATE TRIGGER 語句
FOR 關鍵字之後可以跟 INSERT、UPDATE、DELETE 中的一個或多個,也就是說在其它情況下是不會觸發觸發器的, 包括 SELECT、TRUNCATE、WRITETEXT、UPDATETEXT。
TRUNCATE TABLE 和不帶 WHERE 的 DELETE 功能是一樣的,都是刪除表中的所有數據,不過 TRUNCATE TABLE 速度更快,占用的日志更少,這是因為 TRUNCATE TABLE 直接釋放數據頁並且在事務日志中也只記錄數據頁的釋放,而 DELETE 是一行一行地刪除,在事務日志中要記錄每一條記錄的刪除。
那麼可不可以用 TRUNCATE TABLE 代替不帶 WHERE 的 DELETE 呢?在以下情況是不行的:
1、要保留標識的情況下不能用 TRUNCATE TABLE,因為 TRUNCATE TABLE 會重置標識。
2、需要使用觸發器的情況下不能使用 TRUNCATE TABLE ,它不會激發觸發器。
3、對於由 FOREIGN KEY 約束引用的表(即主鍵所在的表,不是外鍵所在的表)不能使用 TRUNCATE TABLE。
4、對於參與了索引視圖的表不能使用 TRUNCATE TABLE ,注意指索引視圖,並非普通視圖。
觸發器內部語句出錯時,前面對數據更改操作將會無效。 舉個例子,在表中插入數據時觸發觸發器,而觸發器內部此時發生了運行時錯誤,那麼將返回一個錯誤值,並且拒絕剛才的數據插入。
[NextPage]
一個有趣的觸發器應用 觸發器回滾
我們看到許多注冊系統在注冊後都不能更改用戶名,但這多半是由應用程序決定的, 如果直接打開數據庫表進行更改,同樣可以更改其用戶名,在觸發器中利用回滾就可以巧妙地實現無法更改用戶名。
use 數據庫名
go
create trigger tr
on 表名
for update
as
if update(userName)
rollback tran
關鍵在最後兩句,其解釋為:如果更新了 userName 列,就回滾事務。
不能在觸發器中使用的語句
觸發器中可以使用大多數 T-
#013499" href="http://www.86oo.com/html/164/index.Html">SQL 語句,但如下一些語句是不能在觸發器中使用的。
CREATE 語句,如:CREATE DATABASE、CREATE TABLE、CREATE INDEX 等。
ALTER 語句,如:ALTER DATABASE、ALTER TABLE、ALTER INDEX 等。
DROP 語句,如:DROP DATABASE、DROP TABLE、DROP INDEX 等。
DISK 語句,如:DISK INIT、DISK RESIZE。
LOAD 語句,如:LOAD DATABASE、LOAD LOG。
RESTORE 語句,如:RESTORE DATABASE、RESTORE LOG。
RECONFIGURE
說明:有人說不能用 TRUNCATE TABLE 語句,其實是可以的。
[NextPage]
遞歸、嵌套觸發器
遞歸觸發器
遞歸分兩種,間接遞歸和直接遞歸。我們舉例解釋如下,假如有表1、表2名稱分別為 T1、T2,在 T1、T2 上分別有觸發器 G1、G2。
間接遞歸:對 T1 操作從而觸發 G1,G1 對 T2 操作從而觸發 G2,G2 對 T1 操作從而再次觸發 G1...
直接遞歸:對 T1 操作從而觸發 G1,G1 對 T1 操作從而再次觸發 G1...
嵌套觸發器
類似於間接遞歸,間接遞歸必然要形成一個環,而嵌套觸發器不一定要形成一個環,它可以 T1->T2->T3...這樣一直觸發下去,最多允許嵌套 32 層。
--------------------------------------------------------------------------------
設置直接遞歸
默認情況下是禁止直接遞歸的,要設置為允許有兩種方法:
T-SQL:exec sp_dboption 'dbName', 'recursive triggers', true
EM:數據庫上點右鍵->屬性->選項。
設置間接遞歸、嵌套
默認情況下是允許間接遞歸、嵌套的,要設置為禁止有兩種方法:
T-SQL:exec sp_configure 'nested triggers', 0 --第二個參數為 1 則為允許
EM:注冊上點右鍵->屬性->服務器設置。
0 --第二個參數為 1 則為允許 EM:注冊上點右鍵->屬性->服務器設置。
[NextPage]
多個觸發器
觸發器的名稱不同,觸發事件相同(INSERT、UPDATE、DELETE),我們稱為多個觸發器。
多個觸發器之間的執行順序並不確定。
我個人認為應該避免使用多個觸發器,因為它不利於維護。
查看某個觸發器的內容
use 數據庫名
go
exec sp_helptext '觸發器名稱'
將會以表的樣式顯示觸發器內容。
除了觸發器外,sp_helptext 還可以顯示 規則、默認值、未加密的存儲過程、用戶定義函數、視圖的文本
用企業管理器查看
在表上點右鍵->“所有任務”->“管理觸發器”,選擇所要查看的觸發器
查看當前數據庫中有哪些觸發器
在查詢分析器中運行:
use 數據庫名
go
select * from sysobjects where xtype='TR'
sysobjects 保存著數據庫的對象,其中 xtype 為 TR 的記錄即為觸發器對象。在 name 一列,我們可以看到觸發器名稱。
[NextPage]
刪除觸發器
用查詢分析器刪除
在查詢分析器中使用 drop trigger 觸發器名稱 來刪除觸發器。
也可以同時刪除多個觸發器:drop trigger 觸發器名稱,觸發器名稱...
注意:觸發器名稱是不加引號的。在刪除觸發器之前可以先看一下觸發器是否存在:
if Exists(select name from sysobjects where name=觸發器名稱 and xtype='TR')
用企業管理器刪除
在企業管理器中,在表上點右鍵->“所有任務”->“管理觸發器”,選中所要刪除的觸發器,然後點擊“刪除”。
重命名觸發器
用查詢分析器重命名
exec sp_rename 原名稱, 新名稱
sp_rename 是 SQL Server™ 自帶的一個存儲過程,用於更改當前數據庫中用戶創建的對象的名稱,如表名、列表、索引名等。
用企業管理器重命名
在表上點右鍵->“所有任務”->“管理觸發器”,選中所要重命名的觸發器,修改觸發器語句中的觸發器名稱,點擊“確定”。
查看觸發器的屬性
存儲過程 sp_helptrigger 用於查看觸發器的屬性。
sp_helptrigger 有兩個參數:第一個參數為表名;第二個為觸發器類型,為 char(6) 類型,可以是 INSERT、UPDATE、DELETE,如果省略則顯示指定表中所有類型觸發器的屬性。
例:
use 數據庫名
go
exec sp_helptrigger tbl
[NextPage]
觸發器更多語法
INSTEAD OF
執行觸發器語句,但不執行觸發觸發器的 SQL 語句,比如試圖刪除一條記錄時,將執行觸發器指定的語句,此時不再執行 delete 語句。例:
create trigger f
on tbl
instead of delete
as insert into Logs...
IF UPDATE(列名)
檢查是否更新了某一列,用於 insert 或 update,不能用於 delete。例:
create trigger f
on tbl
for update
as
[NextPage]
慎用觸發器
觸發器功能強大,輕松可靠地實現許多復雜的功能,為什麼又要慎用呢。 觸發器本身沒有過錯,但由於我們的濫用會造成數據庫及應用程序的維護困難。
在數據庫操作中,我們可以通過關系、觸發器、存儲過程、應用程序等來實現數據操作,比如刪除 T1 表記錄時期望刪除 T2 表相關的記錄,此時可以建立級聯刪除的關系,也可以為 T1 表建立觸發器使同時刪除 T2 表相關記錄,也可以自定義存儲過程刪除 T1 和 T2 表的記錄,也可以在應用程序中使用兩個 SQL 語句來刪除……到底用哪一種好呢,應該說我們建立通過建立關系來實現級聯刪除是最好的,除非更有高的需求。
觸發器還有一個用途可以用來保障數據的完整性,但同時規則、約束、默認值也可以保障數據完整性,到底哪一個好呢,一般說來,較為簡單的完整性要求我們不應該使用觸發器。兩者在運行機制上也是有區別的,像規則、約束、默認值這些是在數據更改之前進行數據驗證,而觸發器是在數據更改之後進行驗證(如果事務回滾,該表將不會產生變化)。
總之,如果我們對觸發器過分的依賴,就會造成遍地是程序的情況,因為觸發器本身就需要別的程序給它一個觸發條件,也就是說至少在兩個地方存在著程序,同時我們拋棄了約束、默認值等而選用觸發器,勢必影響數據庫的結構。