/*
SQL Server觸發器
1.什么是觸發器
觸發器是一種特殊類型的存儲過程,因為它和存儲過程有一樣的特征,都是預用寫好的Sql命令存儲在SqlServer服務器中。
觸發器在指定的表中的數據發生變化(insert/update/delete)的時候會自動執行。它與存儲過程的區別也在此,因為存儲過程是需要我們用命令去調用的。
觸發器一般用在比check約束更加復雜的約束上面。
注意:觸發器本身就是一個事務,所以在觸發器里面可以對修改數據進行一些特殊的檢查。如果不滿足可以利用事務回滾,撤銷操作。
2.觸發器分類
DML觸發器和DDL觸發器。
DML觸發器:
當數據庫中表中的數據發生變化時,包括insert,update,delete任意操作,如果我們對該表寫了對應的DML觸發器,那么該觸發器將自動執行。
DML觸發器的主要作用在於強制執行業 務規則,以及擴展Sql Server約束,默認值等。因為我們知道約束只能約束同一個表中的數據,而觸發器中則可以執行任意Sql命令。
例子:我們想要在送貨后減少庫存的數量,那我們就可以在送貨單的表上寫update觸發器,用來減少庫存的數量。
DDL觸發器:
它是Sql Server2005新增的觸發器,主要用於審核與規范對數據庫中表,觸發器,視圖等結構上的操作。比如在修改表,修改列,新增表,新增列等。
它在數據庫結構發生變化時執行,我們主要用它來記錄數據庫的修改過程,以及限制程序員對數據庫的修改,比如不允許刪除某些指定表等。
DML觸發器:
1.after觸發器(之后觸發)
a.insert觸發器
b.update觸發器
c.delete觸發器
2.insert of 觸發器 之前觸發
after觸發器只有在執行insert、update、delete之后才能觸發,且只能定義在表上。
而insert of 觸發器表示並不執行其定義的操作(insert、update、delete)而僅是執行觸發器本身。既可以在表上定義,也可在視圖上定義。
3.觸發器有兩個非常特殊,非常重要的兩個表。
insetred:插入表
deleted:刪除表
這兩個是邏輯表,也是虛表。由系統在內存中自動創建這兩張表,這兩張表不會存儲在數據庫中,而且這兩張表都是只讀的。這兩張表的結果總是與被
觸發器應用的表的結構相同。當觸發器完成后,這兩表將會被刪除。
Inserted表的數據是插入或是修改后的數據,而deleted表的數據是更新前的或是刪除的數據。
4.編碼實現
業務邏輯:有水果銷售表和水果庫存表,
*/
--創建表
create table FruitsSaleTable
(
FruitsName varchar(20) primary key not null, --水果名稱
Supplier varchar(50), --供貨商
SaleNumber int, --銷售數量
SaleUnitPrice money, --單價
SaleTotalSum money --銷售總金額
)
insert into FruitsSaleTable(FruitsName,Supplier,SaleNumber,SaleUnitPrice,SaleTotalSum)
values('冰糖雪梨','B',20,10,200)
create table FruitsStock
(
FruitsName varchar(20) primary key not null, --水果名稱
StockNumber int, --庫存數量
StockUnitPrice money, --庫存單價
StockTotalSum money --庫存總金額
)
/*
Insert:
首先我們在庫存表(FruitsStock)中創建一個Insert觸發器,
目的:1.庫存總金額=庫存數量*庫存單價 這個等式成立
2.確保收貨時 “庫存數量”>0 不然收貨動作將毫無意義
*/
create trigger I_insert_FruitsStock --創建觸發器
on FruitsStock --作用那張表
for insert --觸發條件
as
--提交事務
begin transaction
--判斷收貨庫存是否>0
if not exists
(
select StockNumber from FruitsStock where FruitsName in (select FruitsName from inserted) and StockNumber >0
)
begin
--不滿足條件,提示
raiserror('錯誤,收貨數量不能小於0',16,1)
--事務回滾
rollback
return
end
--條件滿足,強制執行SQL,保證業務邏輯
update FruitsStock set StockTotalSum = StockNumber * StockUnitPrice
where FruitsName in (select FruitsName from inserted)
--提交事務
commit transaction
go
--測試觸發器
--1.數據都正確
insert into FruitsStock
(FruitsName,StockNumber,StockUnitPrice,StockTotalSum)
values('蘋果',50,10,500)
/*
結果:OK
(1 行受影響)
*/
--2.庫存總金額<>庫存數量*庫存單件
insert into FruitsStock
(FruitsName,StockNumber,StockUnitPrice,StockTotalSum)
values('冰糖雪梨',30,10,100)
/*
結果:OK
(1 行受影響)
查詢結果可的:觸發器自動進行計算,結果中顯示正確的庫存總金額
*/
--3.當收貨數量為<=0時
insert into FruitsStock
(FruitsName,StockNumber,StockUnitPrice,StockTotalSum)
values('藍莓',0,10,100)
/*
結果:OK
消息 50000,級別 16,狀態 1,過程 I_insert_FruitsStock,第 138 行
錯誤,收貨數量不能小於0
消息 3609,級別 16,狀態 1,第 124 行
事務在觸發器中結束。批處理已中止。
*/
/*
update:
首先我們在銷售表(FruitsSaleTable)中創建一個update觸發器,
業務邏輯:更改銷售價格,銷售總金額一起改變
*/
create trigger U_update_FruitsSaleTable
on FruitsSaleTable
for update
as
if update(SaleUnitPrice)
declare @price money --更改后的銷售價格
--提交事務
begin transaction
select @price=SaleUnitPrice from FruitsSaleTable where FruitsName in (select FruitsName from inserted)
--begin
--if(@price<=0)
-- print'價格修改錯誤'
-- rollback
-- return
-- end
update FruitsSaleTable set SaleTotalSum =SaleNumber * @price where FruitsName in (select FruitsName from inserted)
commit transaction
go
/*
update觸發器測試
*/
update FruitsSaleTable set SaleUnitPrice=7 where FruitsName='蘋果'
/*
結果:
(1 行受影響)
*/
/*
delete觸發器:
業務邏輯:刪除庫存表中數據時同時刪除銷售表
*/
create trigger D_delete_FruitsStock
on FruitsStock
for delete
as
begin
delete FruitsStock from FruitsStock fs,deleted d where fs.FruitsName=d.FruitsName
delete FruitsSaleTable from FruitsSaleTable st,deleted d where st.FruitsName=d.FruitsName
end
go
/*
delete觸發器
(0 行受影響)
(1 行受影響)
*/
delete from FruitsStock where FruitsName='冰糖雪梨'
select * from FruitsStock
select * from FruitsSaleTable
/*
寫的比較簡單,本人也處於學習階段,有錯誤的地方希望可以指點一下。
*/