1. 程式人生 > >2.SQL Server DML觸發器之COMMIT和ROLLBACK TRANSACTION

2.SQL Server DML觸發器之COMMIT和ROLLBACK TRANSACTION

14.1.2 COMMIT和ROLLBACK TRANSACTION
當執行能夠激發觸發器操作的語句時,觸發器中的操作也將包含在該語句的事務處理過程中。即使在SQL Server的自動事務處理模式下,也是如此。在自動事務處理模式下,當語句遇到錯誤時,會有隱含的BEGIN TRANSACTION語句來回滾該語句所影響的修改。但是,這個回滾操作對批處理中的其它語句沒有影響,不會回滾前面操作正常的語句。因為當語句完成時,該事務要麼提交,要麼回滾,事務處理已經結束。但是,當該語句激發觸發器時,這個隱含事務將一直有效,直至觸發器操作完成。因此,這也就給予了在觸發器中回滾該語句操作的機會。需要注意的是,這個回滾操作也會終止批中對該語句後面語句的執行。
例如,下面的示例為DetailTable表的INSERT和UPDATE操作定義了觸發器,當向表中插入新行或更新行時,要求ProductID、ProductCount和Price列的值不能為0,否則將執行ROLLBACK TRANSACTION進行回滾。
USE AdventureWorks;
GO



-- 建立表
CREATE TABLE DetailTable
(OrderID int, ProductID int, ProductCount int NOT NULL, Price money);
GO

-- 為DetailTable表的INSERT和UPDATE操作定義觸發器
CREATE TRIGGER DetailTrigger
ON DetailTable
AFTER INSERT, UPDATE
AS
SET NOCOUNT ON
IF (EXISTS(SELECT *
FROM Inserted
WHERE ProductID = 0 OR ProductCount = 0 OR Price = 0))
BEGIN
ROLLBACK TRANSACTION ;
END
GO

INSERT INTO DetailTable VALUES(1,1,10,1000.00); -- 插入一行資料
執行下面的語句。在自動事務處理模式下,由於UPDATE語句沒有違反ProductID、ProductCount和Price列的值不為0的規則,因此這句可以正常提交。下面的INSERT語句由於設定ProductID列的值為0,因此會執行觸發器中的ROLLBACK TRANSACTION語句,被回滾操作。但是,由於UPDATE語句已經正常提交,所以對於INSERT語句的回滾操作不會涉及到它。由於執行了回滾操作,將終止對批處理中剩餘語句的執行,下面的DELETE語句不會被執行。
UPDATE DetailTable
SET ProductCount = 100
WHERE OrderID = 1 AND ProductID = 1;
INSERT INTO DetailTable VALUES(2,0,10,1000.00);
DELETE FROM DetailTable
WHERE OrderID = 1;

對於顯式或隱式事務處理,觸發器中的ROLLBACK TRANSACTION語句將回滾整個事務處理。例如,下面的示例將UPDATE和INSERT語句包含在了一個顯式事務處理中,由INSERT語句引發的回滾操作也將回滾UPDATE語句所做的修改。
BEGIN TRANSACTION
UPDATE DetailTable
SET ProductCount = 100
WHERE OrderID = 1 AND ProductID = 1;
INSERT INTO DetailTable VALUES(2,0,10,1000.00);
COMMIT TRANSACTION

從前面的分析也可以看出,無論是在自動事務處理模式下,還是在隱式或顯式事務處理模式下,只要在觸發器中發出BEGIN TRANSACTION語句,實際上就開始了一個巢狀事務。當從觸發器中使用ROLLBACK TRANSACTION語句回滾巢狀事務時,觸發器本身發出的所有BEGIN TRANSACTION語句都將被忽略,ROLLBACK將回滾到最外部的BEGIN TRANSACTION。
如果要在觸發器中進行部分回滾,應當使用SAVE TRANSACTION語句設定一個事務儲存點。例如:
CREATE TRIGGER Trigger1
ON MyTable
AFTER UPDATE
AS
SAVE TRANSACTION MyName
  INSERT INTO MyTable1
   SELECT * FROM inserted;
  IF (@@error <> 0)
  BEGIN
   ROLLBACK TRANSACTION MyName;
  END  

在觸發器中使用BEGIN TRANSACTION語句的情況下,其後面的COMMIT TRANSACTION語句只應用於該巢狀事務。如果在COMMIT TRANSACTION之後執行ROLLBACK TRANSACTION語句,則ROLLBACK仍舊一直回滾到最外部的BEGIN TRANSACTION。因此,建議不要在觸發器中使用COMMIT TRANSACTION語句。
例如,下面的示例為DetailTable表建立了一個用於INSERT操作的觸發器,當向DetailTable表中插入行時,該行將被複制到DetailTable1表中。雖然在觸發器內已經使用COMMIT TRANSACTION語句進行了提交,但是後面的ROLLBACK TRANSACTION語句仍舊回滾所有巢狀操作。DetailTable表始終不能插入任何資料。
USE AdventureWorks;
GO

-- 建立表
CREATE TABLE DetailTable
(OrderID int, ProductID int, ProductCount int NOT NULL, Price money);
CREATE TABLE DetailTable1
(OrderID int, ProductID int, ProductCount int NOT NULL, Price money);
GO

-- 建立觸發器,將插入到DetailTable表的行復制到DetailTable1中
CREATE TRIGGER TransTrigger
ON DetailTable
AFTER INSERT
AS
BEGIN TRANSACTION
INSERT INTO DetailTable1
SELECT * FROM Inserted;
COMMIT TRANSACTION
ROLLBACK TRANSACTION

可以使用下面的語句測試插入情況:
INSERT INTO DetailTable VALUES(1,1,10,1000.00);
GO
SELECT * FROM DetailTable;
SELECT * FROM DetailTable1;