觸發(fā)器是一種特殊的存儲過程,它在試圖更改觸發(fā)器所保護的數(shù)據時自動執(zhí)行。
它被定義為在對表或視圖發(fā)出 UPDATE、INSERT 或 DELETE 語句時自動執(zhí)行,在有數(shù)據修改時自動強制執(zhí)行其業(yè)務規(guī)則。
觸發(fā)器可以擴展 SQL Server 約束、默認值和規(guī)則的完整性檢查邏輯,但只要約束和默認值提供了全部所需的功能,就應使用約束和默認值。
*/
-- 觸發(fā)器的特點
/*
與表相關聯(lián)
觸發(fā)器定義在特定的表上,這個表稱為觸發(fā)器表。
自動激活觸發(fā)器
當對表中的數(shù)據執(zhí)行 INSERT、UPDATE 或 DELETE 操作時,如果對表上的這個特定操作定義了觸發(fā)器,該觸發(fā)器自動執(zhí)行,這是不可撤銷的。
不能直接調用
與存儲過程不同,觸發(fā)器不能被直接調用,也不能傳遞或接受參數(shù)。
作為事務的一部分
觸發(fā)器與激活觸發(fā)器的語句一起做為對一個單一的事務來對待,可以從觸發(fā)器中的任何位置回滾。
當使用觸發(fā)器時,應該考慮以下事實和知道原則:
即使不存在顯式的 BEGIN TRANSACTION 語句,在觸發(fā)器的定義中也可以包括 ROLLBACK TRANSACTION 語句
如果遇到一個 ROLLBACK TRANSACTION 語句,則整個事務回滾。如果在觸發(fā)器腳本中跟隨在 ROLLBACK TRANSACTION 語句后還有別的語句,
這些語句將繼續(xù)被執(zhí)行。需要的話,可以使用 RETURN 語句來防止這些語句的執(zhí)行。
如果包括 ROLLBACK TRANSACTION 語句的觸發(fā)器在一個用戶定義的事務中被激活,這個 ROLLBACK TRANSACTION 語句撤銷整個事務。
在這個用戶定義的事務的批處理語句中,激活該觸發(fā)器的語句之后的語句將不再執(zhí)行。
*/
-- 舉例
/*
use Test
GO
create table dbo.t_record(idx int identity(1,1),col_1 varchar(20))
insert t_record (col_1) values ('Row 01')
insert t_record (col_1) values ('Row 02')
insert t_record (col_1) values ('Row 03')
*/
create trigger tr_record_delete on dbo.t_record
for delete
as
/*
作用:當刪除表 t_record 中的記錄時,若一次刪除 1 條以上,則取消刪除
*/
if (select count(*) from deleted)>1
begin
raiserror('You cannot delete more than one record at a time',16,1)
rollback tran
end
Go
select * from t_record
delete t_record
select * from t_record
delete t_record where idx=1
-- 觸發(fā)器的使用
/*
觸發(fā)器最好用于保持低級的數(shù)據完整性,而不是返回查詢結果。
觸發(fā)器的主要好處是可以包含負責的處理邏輯。
觸發(fā)器能對數(shù)據庫中相關的表進行級聯(lián)修改,強制比 CHECK 約束更復雜的數(shù)據完整性。與 CHECK 約束不同,觸發(fā)器可以引用其他表中的列。
定義自定義的錯誤信息。
維護非規(guī)范化數(shù)據。級聯(lián)是指保持主鍵與外鍵之間的關系,非規(guī)范化數(shù)據是指派生的,冗余的數(shù)據值。
比較修改前后數(shù)據的狀態(tài)。絕大多數(shù)觸發(fā)器提供了訪問由 INSERT、UPDATE 或 DELETE 語句引起的數(shù)據變化前后狀態(tài)的能力。
這樣,就允許在觸發(fā)器中引用由修改語句所影響的行。
*/
-- 使用觸發(fā)器時的考慮
/*
觸發(fā)器是后反應的(Reactive),約束是前反應的(Proactice)
在觸發(fā)器定義的表中執(zhí)行 DML 語句后,觸發(fā)器執(zhí)行。而約束是在 DML 語句執(zhí)行前進行檢查。
首先檢查約束
如果在觸發(fā)器表上存在約束,則約束在觸發(fā)器之前進行檢查。如果違反約束,觸發(fā)器就不執(zhí)行。
表對任何動作可以有多個觸發(fā)器
允許在一個表上嵌套幾個觸發(fā)器。一個表可以定義多個觸發(fā)器,每個觸發(fā)器可以為一個或多個動作定義。
表的所有者能夠指定最先和最后觸發(fā)的觸發(fā)器
當一個表上有多個觸發(fā)器時,表的擁有者可以使用 sp_settriggerorder 系統(tǒng)存儲過程來指定最先和最后激活的觸發(fā)器。
其余的觸發(fā)器的順序不能被設置。
用戶必須具有執(zhí)行觸發(fā)器定義的所有語句的權限
觸發(fā)器創(chuàng)建者必須擁有在所有受影響的表上執(zhí)行觸發(fā)器所定義的所有語句的權限。
如果觸發(fā)器中的任何部分的 T-SQL 語句的權限被拒絕,整個事務將被回滾。
表的屬主不能在視圖或臨時表上創(chuàng)建 AFTER 觸發(fā)器,但是觸發(fā)器可以引用視圖和臨時表。
*/
-- 8.2 --------------------------------------------------
-- 定義觸發(fā)器
-- 創(chuàng)建觸發(fā)器
-- 創(chuàng)建時須定義 觸發(fā)器的名稱、觸發(fā)器所在的表、觸發(fā)器所監(jiān)視的動作
CREATE TRIGGER trigger_name
ON table_name
FOR INSERT|UPDATE|DELETE
AS
......
GO
/*
模擬一個排隊取號系統(tǒng),當號碼到達 50 之后,重新回到 1 開始計數(shù)。
即表中的 idx 到達 50 后,把表情空,把 IDENTITY 種子設置為 1 。
use Test
Go
create table t_queue(idx int identity(1,1),create_time datetime default(getdate()))
*/
create trigger tr_reset_queue on t_queue
for insert
as
if @@identity>50
begin
truncate table t_queue
declare @now datetime
set @now=getdate()
insert t_queue (create_time) values (@now)
end
Go
declare @now datetime
set @now=getdate()
insert t_queue (create_time) values (@now)
select * from t_queue order by idx desc
-- 修改觸發(fā)器: 加密觸發(fā)器、禁用觸發(fā)器
-- 加密觸發(fā)器
alter trigger tr_reset_queue on t_queue
with encryption
for insert
as
if @@identity>100
begin
truncate table t_queue
declare @now datetime
set @now=getdate()
insert t_queue (create_time) values (@now)
end
Go
sp_helptext tr_reset_queue
-- 禁用觸發(fā)器
alter table t_queue
disable trigger tr_reset_queue
alter table t_queue
disable trigger all
-- 啟用觸發(fā)器
alter table t_queue
enable trigger tr_reset_queue
-- 刪除觸發(fā)器
-- 可以直接刪除觸發(fā)器,也可以通過刪除觸發(fā)器所在的表來刪除觸發(fā)器。
drop trigger tr_reset_queue
或者
drop table t_queue
-- 練習 --------------------------------------------------
-- 1 在 11 人制足球比賽中,球員在嚴重犯規(guī)后可被罰下場。
-- 但是當場上一個球隊中的球員被罰到只剩 7 人時,無論再怎么犯規(guī),都不能被罰下場。
/*
use Test
Go
create table dbo.t_RealMadrid_On(Name varchar(50),Num tinyint,Position char(3))
insert t_RealMadrid_On values ('Casillas',1,'GK')
insert t_RealMadrid_On values ('Salgado',2,'RB')
insert t_RealMadrid_On values ('Samuel',19,'CBT')
insert t_RealMadrid_On values ('Pavon',22,'CBT')
insert t_RealMadrid_On values ('Roberto Carlos',3,'LB')
insert t_RealMadrid_On values ('Figo',10,'RMF')
insert t_RealMadrid_On values ('Backham',23,'CMF')
insert t_RealMadrid_On values ('Ivan Helguera',6,'DMF')
insert t_RealMadrid_On values ('Zidane',5,'OMF')
insert t_RealMadrid_On values ('Ronaldo',9,'CF')
insert t_RealMadrid_On values ('Raul',7,'CF')
select * from t_RealMadrid_On
*/
-- 答案 --------------------------------------------------
-- 1
create trigger tr_player_delete
on t_RealMadrid_On
for delete
as
if (select count(*) from t_RealMadrid_On)<7
begin
rollback tran
raiserror('At Least 7 Players should Be On',16,1)
end
GO
delete t_RealMadrid_On where Num in (select max(Num) from t_RealMadrid_On)
select * from t_RealMadrid_On
-- 2 使用觸發(fā)器完成以下功能:當刪除 t_Product 表時,檢查 t_OrderHist
-- 中是否包含該產品的訂購記錄,如果沒有,可以刪除產品,否則回滾并報錯。
use Test
Go
create table dbo.t_Product(p_id int,p_name varchar(50),stock int)
insert t_Product select 1,'SONY CD-R 52X',25
insert t_Product select 2,'SONY DVD+R 16X',0
insert t_Product select 3,'SONY DVD+R 8X',20
insert t_Product select 4,'MITSUBISHI DVD+R 16X',10
Go
create table dbo.t_OrderHist(o_id int,p_id int,quantity int,orderdate smalldatetime default(getdate()))
insert t_OrderHist (o_id,p_id,quantity) select 10522,10,7
insert t_OrderHist (o_id,p_id,quantity) select 10523,2,9
insert t_OrderHist (o_id,p_id,quantity) select 10522,41,24
insert t_OrderHist (o_id,p_id,quantity) select 10522,7,5
GO
select * from t_Product
select * from t_OrderHist
create trigger tr_product_delete
on t_Product
for delete
as
declare @p_id int
select @p_id = p_id from deleted
if @p_id in (select p_id from t_OrderHist )
begin
raiserror('t_OrderHist中是否包含該產品的訂購記錄不允許刪除',16,1)
rollback tran
end
GO
- 為最佳答案評分?
-
好 100% (1)
-
不好 0% (0)
- (目前有 1 個人評價)