在下面的內(nèi)容,用到一些SQL Server 觸發(fā)器和事務(wù)的一些術(shù)語(yǔ),如果有些不明白的地方,可以查閱MSDN資料庫(kù),或SQL Server本地幫助文檔:
DML觸發(fā)器(DML Triggers) DDL觸發(fā)器(DDL Triggers) 事務(wù)模式(Transaction modes) 顯式事務(wù)(Explicit Transactions) 自動(dòng)提交事務(wù)(Autocommit Transactions) 隱式事務(wù)(Implicit Transactions) 批范圍的事務(wù)(Batch-scoped Transactions) After觸發(fā)器 Vs Instead Of觸發(fā)器After 觸發(fā)器將在處理觸發(fā)操作(Insert、Update 或 Delete)、Instead Of 觸發(fā)器和約束之后激發(fā)。Instead Of是將在處理約束前激發(fā),以替代觸發(fā)操作。下面兩張圖描述了After觸發(fā)器和Instead Of觸發(fā)器的執(zhí)行先后順序。
圖1 圖2
左邊的圖1,描述了After觸發(fā)器執(zhí)行順序情況,我在這里通過一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明After觸發(fā)器的執(zhí)行順序,以便能加深對(duì)左圖1 After觸發(fā)器的理解。
先創(chuàng)建表Contact
use tempdb
Go
if object_id('Contact') Is Not null
Drop Table Contact
Go
Create Table Contact
(
ID int Primary Key Identity(1,1),
Name nvarchar(50),
Sex nchar(2) Check(Sex In(N'F',N'M')) Default('M')
)
Go
再創(chuàng)建After觸發(fā)器tr_Contact
use tempdb
Go
If Exists(Select 1 From sys.triggers Where name='tr_Contact')
Drop Trigger tr_Contact
Go
Create Trigger tr_Contact On Contact After Insert
As
Select Name,Sex From Inserted /*顯示Inserted表的內(nèi)容,用來(lái)判斷觸發(fā)器執(zhí)行的先后順序*/
Go
然后Insert數(shù)據(jù),判斷After觸發(fā)器的執(zhí)行順序
use tempdb
Go
Insert Into Contact (Name,Sex) Values ('Bill','U')
Go
這里,在沒有運(yùn)行Insert語(yǔ)句之前,我們可以判斷,執(zhí)行Insert過程會(huì)觸發(fā)Check錯(cuò)誤,因?yàn)樽侄蜸ex的值必須是”F” Or “M”,而這里將要插入的是”U”.好了,再來(lái)看運(yùn)行Insert語(yǔ)句后的情況。
本例子,只看到引發(fā)Check約束沖突的錯(cuò)誤,而無(wú)法看到Inserted表的數(shù)據(jù),說(shuō)明一點(diǎn)就是,引起Check約束之前,不會(huì)引發(fā)After觸發(fā)器tr_Contact的操作。這就驗(yàn)證了圖1的After觸發(fā)器執(zhí)行順序情況。
好了,接下來(lái),我們?cè)贉y(cè)試Instead Of觸發(fā)器 圖2的情況;我使用上邊建好的測(cè)試表Contact來(lái)舉例。
先修改觸發(fā)器tr_Contact內(nèi)容,
use tempdb
Go
If Exists(Select 1 From sys.triggers Where name='tr_Contact')
Drop Trigger tr_Contact
Go
Create Trigger tr_Contact On Contact Instead Of Insert
As
print '觸發(fā)器作代替執(zhí)行操作'
Insert Into Contact (Name,Sex) Select Name,Sex From Inserted /*代替觸發(fā)器外面的Insert行為*/
Go
再Insert數(shù)據(jù),觀察SQL Server執(zhí)行后的提示信息,
use tempdb
Go
Insert Into Contact (Name,Sex) Values ('Bill','U')
Go
這里,看到,先是觸發(fā)器操作,再是Check約束處理。本例中,在觸發(fā)器里面使用一條Insert的語(yǔ)句來(lái)描述觸發(fā)器的代替執(zhí)行操作,這SQL語(yǔ)句通過Select表Inserted得到觸發(fā)器外面Insert內(nèi)容。當(dāng)SQL Server執(zhí)行到觸發(fā)器里面的Insert語(yǔ)句,才會(huì)引起Check約束處理.倘若,在觸發(fā)器tr_Contact沒有Insert的代替行為,那么就不會(huì)出現(xiàn)Check約束處理錯(cuò)誤的信息(注:沒有Check錯(cuò)誤信息,并不表示沒有作Check處理)。修改上邊的觸發(fā)器tr_Contact內(nèi)容,做個(gè)簡(jiǎn)易的驗(yàn)證.
use tempdb
Go
If Exists(Select 1 From sys.triggers Where name='tr_Contact')
Drop Trigger tr_Contact
Go
Create Trigger tr_Contact On Contact Instead Of Insert
As
print '觸發(fā)器作代替執(zhí)行操作'
Go
use tempdb
Go
Insert Into Contact (Name,Sex) Values ('Bill','U')
Go
Select * From Contact
可以看到,Instead Of 觸發(fā)器tr_Contact內(nèi)容沒有Insert的SQL語(yǔ)句,不會(huì)引發(fā)Check處理錯(cuò)誤,而且檢查Insert動(dòng)作后的結(jié)果,發(fā)現(xiàn)表Contact也沒有之前我們Insert的數(shù)據(jù)。這些足夠驗(yàn)證了Instead Of觸發(fā)器的執(zhí)行先后順序和代替執(zhí)行操作。
DML 觸發(fā)器 Vs DDL 觸發(fā)器
DML 觸發(fā)器在 Insert、Update 和 Delete 語(yǔ)句上操作,可以作為After 觸發(fā)器 和 Instead Of 觸發(fā)器。
DDL 觸發(fā)器對(duì) Create、Alter、Drop 和其他 DDL 語(yǔ)句以及執(zhí)行 DDL 式操作的存儲(chǔ)過程執(zhí)行操作,只可作為After觸發(fā)器,不能Instead Of觸發(fā)器。
前面的內(nèi)容,有描述DML觸發(fā)器中的After Instead Of觸發(fā)器內(nèi)容,下面直接來(lái)看DDL的操作順序:
圖3.
從圖3.可以知道,在DDL觸發(fā)器中,是沒有創(chuàng)建Inserted Deleted過程的,我們通過簡(jiǎn)單的例子去測(cè)試下。
創(chuàng)建一個(gè)服務(wù)器范圍內(nèi)的DDL觸發(fā)器,檢查有沒有Inserted 表,
use master
Go
If Exists(Select 1 From sys.server_triggers Where name='tr_createDataBase')
Drop Trigger tr_createDataBase On All Server
Go
Create Trigger tr_createDataBase On All Server After Create_DataBase
As
Select * From inserted
Go
執(zhí)行創(chuàng)建數(shù)據(jù)庫(kù)SQL語(yǔ)句,
use master
Go
Create Database myDataBase On Primary
(Name='MyDataBase_Data',Filename='E:\DATA\SQL2008DE01\MyDataBase_Data.mdf') Log On
(Name='MyDataBase_Log',Filename='E:\DATA\SQL2008DE01\MyDataBase_Log.ldf')
Go
返回錯(cuò)誤信息,
使用上邊相同的方法,我們驗(yàn)證DDL觸發(fā)器中,不會(huì)創(chuàng)建Deleted表;是否創(chuàng)建Deleted Inserted,也可以認(rèn)為是DDL觸發(fā)器與DML觸發(fā)器不同之處。在DLL觸發(fā)器與DML觸發(fā)器不同的一個(gè)重要特征是作用域,DML觸發(fā)器只能應(yīng)用在數(shù)據(jù)庫(kù)層(Database Level)的表和視圖上,而DDL觸發(fā)器應(yīng)用于數(shù)據(jù)庫(kù)層(Database Level)和服務(wù)器層(Server Level);DDL觸發(fā)器的作用域取決于事件。下面簡(jiǎn)單描述下事件組的內(nèi)容。
數(shù)據(jù)庫(kù)層事件主要包含:
服務(wù)器層事件主要包含:
觸發(fā)器和事務(wù)的故事
創(chuàng)建一個(gè)表ContactHIST,用于對(duì)表Contact作Update Or Delete操作時(shí),把操作前的數(shù)據(jù)Insert到表ContactHIST中。
use tempdb
Go
if object_id('ContactHIST') Is Not null
Drop Table ContactHIST
Go
Create Table ContactHIST
(
ID int Primary Key Identity(1,1),
ContactID int,
Name nvarchar(50),
Sex nchar(2),
ActionType nvarchar(10) Check(ActionType In('Update','Delete')),
LastUpdateDate datetime Default(getdate())
)
Go
修改觸發(fā)器tr_Contact內(nèi)容,
use tempdb
Go
If Exists(Select 1 From sys.triggers Where name='tr_Contact')
Drop Trigger tr_Contact
Go
Create Trigger tr_Contact On Contact After Update,Delete
As
Insert Into ContactHIST(ContactID,Name,Sex)
Select ID,Name,Sex From deleted
Rollback Tran
Begin Tran
Go
測(cè)試數(shù)據(jù),
use tempdb
Go
Insert Into Contact (Name,Sex) Values ('Bill','F')
Go
--Update
Update Contact
Set Sex='M'
Where Name='Bill'
Go
Select * From Contact
Select * From ContactHIST
Go
測(cè)試結(jié)果:
從上邊的測(cè)試情況,看出,Update Contact觸發(fā)tr_Contact觸發(fā)器操作,觸發(fā)器里面的Rollback Tran 動(dòng)作導(dǎo)致了觸發(fā)器外面的Update語(yǔ)句執(zhí)行回滾,而Rollback Tran 語(yǔ)句后面的Begin Tran語(yǔ)句,主要是應(yīng)用于保持整個(gè)事務(wù)的完整性。為了更能理解這一過程,我模擬了一個(gè)觸發(fā)器中的事務(wù)開始結(jié)束過程。
圖4.
在SQL Server 2005 和 SQL Server 2008上面,可以看到如圖4.的效果。在低版本的SQL Server上,可能會(huì)出現(xiàn)錯(cuò)誤提示情況,不管如何,在觸發(fā)器外面,SQL Server都會(huì)Rollback Tran。下面我做個(gè)錯(cuò)誤提示的例子。
修改觸發(fā)器tr_Contact內(nèi)容
use tempdb
Go
If Exists(Select 1 From sys.triggers Where name='tr_Contact')
Drop Trigger tr_Contact
Go
Create Trigger tr_Contact On Contact After Update,Delete
As
Insert Into ContactHIST(ContactID,Name,Sex)
Select ID,Name,Sex From deleted
Rollback Tran
--Begin Tran
Go
重新執(zhí)行Update操作,
use tempdb
Go
Update Contact
Set Sex='M'
Where Name='Bill'
Go
Select @@TRANCOUNT
Go
Select * From Contact
Select * From ContactHIST
Go
在觸發(fā)器里面沒有Begin Tran語(yǔ)句動(dòng)作,觸發(fā)器外面也能回滾操作。這里我們可以通過查詢表數(shù)據(jù)和@@Trancount來(lái)判斷。
其實(shí),上面的例子,Update語(yǔ)句,是以自動(dòng)提交事務(wù)(Autocommit Transactions)模式 開始執(zhí)行的,觸發(fā)器里Rollback Tran后面,不管有沒有Begin Tran ,最后都會(huì)事務(wù)都會(huì)交回給SQL Server自動(dòng)提交事務(wù)管理。當(dāng)然,在DML觸發(fā)器中,你可以使用顯式事務(wù)(Explicit Transactions),或開啟隱式事務(wù)(Implicit Transactions) 來(lái)控制,當(dāng)然你也可以應(yīng)用于批范圍的事務(wù)(Batch-scoped Transactions) 中。這里,我通過開啟隱式事務(wù)(Implicit Transactions) 的例子來(lái)說(shuō),觸發(fā)器與事務(wù)的關(guān)系。
修改觸發(fā)器tr_Contact的內(nèi)容,
use tempdb
Go
If Exists(Select 1 From sys.triggers Where name='tr_Contact')
Drop Trigger tr_Contact
Go
Create Trigger tr_Contact On Contact After Update,Delete
As
Print N'觸發(fā)器里Insert 前,@@Trancount='+Rtrim(@@Trancount)
Insert Into ContactHIST(ContactID,Name,Sex)
Select ID,Name,Sex From deleted
Print N'觸發(fā)器里Insert后,Rollback Tran 前,@@Trancount='+Rtrim(@@Trancount)
Rollback Tran
Print N'觸發(fā)器里Rollback Tran 后,@@Trancount='+Rtrim(@@Trancount)
Begin Tran
Go
開啟隱式事務(wù)(Implicit Transactions) 來(lái)測(cè)試,
use tempdb
Go
Set Implicit_transactions On /**/
Go
Print N'Update Contact前,@@Trancount='+Rtrim(@@Trancount)
Update Contact
Set Sex='M'
Where Name='Bill'
Print N'Update Contact后,@@Trancount='+Rtrim(@@Trancount)
Rollback Tran
Print N'觸發(fā)器外面Rollback Tran 后,@@Trancount='+Rtrim(@@Trancount)
Go
Set Implicit_transactions Off /**/
Go
Go
Select * From Contact
Select * From ContactHIST
Go
這里,你是否發(fā)現(xiàn)一個(gè)很有意思的問題,在觸發(fā)器理,執(zhí)行Insert ContactHIST之前,@@Trancount=1,執(zhí)行Insert后,@@Trancount還是為1,觸發(fā)器外面Update Contact后,@@Trancount就變成了2,。這里可以理解成,你在觸發(fā)器里面,發(fā)出一個(gè)Begin Tran,那么SQL Server 就會(huì)創(chuàng)建一個(gè)嵌套事務(wù)。當(dāng)你在觸發(fā)器里面,在Rollback Tran后面屏蔽掉Begin Tran,就會(huì)出現(xiàn)錯(cuò)誤3609,如,
use tempdb
Go
If Exists(Select 1 From sys.triggers Where name='tr_Contact')
Drop Trigger tr_Contact
Go
Create Trigger tr_Contact On Contact After Update,Delete
As
Print N'觸發(fā)器里Insert 前,@@Trancount='+Rtrim(@@Trancount)
Insert Into ContactHIST(ContactID,Name,Sex)
Select ID,Name,Sex From deleted
Print N'觸發(fā)器里Insert后,Rollback Tran 前,@@Trancount='+Rtrim(@@Trancount)
Rollback Tran
Print N'觸發(fā)器里Rollback Tran 后,@@Trancount='+Rtrim(@@Trancount)
Go
這里,可以看到事務(wù)在觸發(fā)器中Rollback,又沒有開啟新的事務(wù),導(dǎo)致整個(gè)批處理就中止,不會(huì)繼續(xù)執(zhí)行觸發(fā)器外面的Rollback Tran操作。倘若,你在觸發(fā)器中使用Begin Tran …… Commit Tran格式,那么觸發(fā)器Commit Tran不會(huì)影響到外面的事務(wù);下面描述三種常見觸發(fā)器中事務(wù)的情況:
圖5. 圖6. 圖7.
圖5.描述在觸發(fā)器中含有Begin Tran …… Commit Tran的情況,
圖6.描述在觸發(fā)器中含有Save Tran savepoint_name …… Rollback Tran savepoint_name 的情況,觸發(fā)器中的Rollback Tran 只會(huì)回滾指定的保存點(diǎn),不會(huì)影響到觸發(fā)器外面的Commit Tran Or Rollback Tran操作。
圖7.描述在觸發(fā)器中含有Rollback Tran的情況,不管觸發(fā)器里面有沒有Begin Tran,都會(huì)出現(xiàn)錯(cuò)誤3609,中止批處理。
注:DDL觸發(fā)器操作可以觸發(fā)器中回滾操作,可以使用命令如Rollback,但嚴(yán)重錯(cuò)誤可能會(huì)導(dǎo)致整個(gè)事務(wù)自動(dòng)回滾。不能回滾發(fā)生在 DDL 觸發(fā)器正文內(nèi)的 Alter Database事件。在觸發(fā)器中使用Rollback … Begin Tran 可能會(huì)導(dǎo)致意想不到的結(jié)果,在沒有確認(rèn)和測(cè)試情況下,請(qǐng)不要隨便在觸發(fā)器中直接使用Rollback …Begin Tran處理方式.特別是Create Database事件,在SQL Server 2008和SQL Server 2005環(huán)境下,產(chǎn)生的結(jié)果不同。
Rollback …Begin Tran情況:
Create Trigger ….
As
……
Rollback
Begin Tran
End
小結(jié)
回顧前文至后文,從After觸發(fā)器VsInstead Of 觸發(fā)器,說(shuō)到DML觸發(fā)器 Vs DDL觸發(fā)器,再到觸發(fā)器中事務(wù)的故事。也許有些地方描述的有些模糊,有些地方只有一筆帶過;你在測(cè)試代碼過程中,可能發(fā)現(xiàn)有些地方與這里測(cè)試的情況不同,那可能是因?yàn)镾QL Server版本的不同,導(dǎo)致一些測(cè)試結(jié)果不同。無(wú)論如何,只要你感覺對(duì)你了解觸發(fā)器,有些幫助,就OK了。
標(biāo)簽:宜春 朔州 通化 邢臺(tái) 鹽城 巴彥淖爾 遼源 本溪
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《SQL Server觸發(fā)器及觸發(fā)器中的事務(wù)學(xué)習(xí)》,本文關(guān)鍵詞 SQL,Server,觸發(fā)器,及,中的,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。