2.SQL Server DML触发器之COMMIT和ROLLBACK TRANSACTION

本文摘自《锋利的SQL》:http://item.jd.com/10380652.html

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; 



你可能感兴趣的:(sql,sql,server,事务)