SQL Server何时回滚错误的事务?

时间:2016-09-09 16:50:16

标签: sql-server transactions

我一直在研究这两个问题:

SQL Server - transactions roll back on error?

sql transaction don't roll back

根据我的研究,我得出的结论是查询应该是这样写的:

set XACT_ABORT ON
begin tran FB5773_1
begin try
    -- Do some changes
    commit tran FB5773_1;
end try
begin catch
    IF EXISTS (SELECT [name] FROM sys.dm_tran_active_transactions WHERE name = 'FB5773_1')
        rollback tran FB5773_1;
end catch

但是,我想知道为什么这样的交易

begin tran FB5773_1
--do some stuff
commit tran FB5773_1;

或者

set XACT_ABORT ON
begin tran FB5773_1
--do some stuff
commit tran FB5773_1;

在某些情况下不会因错误而回滚,以及什么时候不回滚?

1 个答案:

答案 0 :(得分:1)

我认为您正在寻找的答案可以在Microsoft XACT_ABORT的文档中找到,我将尝试使用一些示例和其他资源来增强它。在我们开始之前,可以使用几个表来测试在不同情况下发生的情况:

CREATE TABLE _key
    ( id INT PRIMARY KEY CLUSTERED );
GO

INSERT INTO _key VALUES (1), (2), (3);
GO

CREATE TABLE _table1
    ( table1id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, id INT NOT NULL );
GO

ALTER TABLE dbo._table1
    ADD CONSTRAINT FK_table1 FOREIGN KEY (id) REFERENCES dbo._key (id);
GO

CREATE TABLE _table2
    ( table2id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, id INT NOT NULL );
GO

ALTER TABLE dbo._table2
    ADD CONSTRAINT FK_table2 FOREIGN KEY (id) REFERENCES dbo._key (id);
GO

CREATE PROCEDURE _proc
AS
BEGIN
    SET XACT_ABORT ON;
    BEGIN TRANSACTION;
        INSERT INTO dbo._table1 (id) VALUES(1); --valid insert
        DECLARE @query NVARCHAR(MAX) = N'SELECT ** FROM dbo._table1;'
        EXEC (@query); --will result in compile error
    COMMIT TRANSACTION;
    RETURN 0;
END
GO

此外,在每个示例之间我清理表格以避免对刚刚发生的事情产生任何疑问:

TRUNCATE TABLE _table1;
TRUNCATE TABLE _table2;

我不认为SET XACT_ABORT OFF的结果存在混淆,这是默认状态,但让我们来看一个例子,只是为了清楚......

SET XACT_ABORT OFF; --this command has no practical effect
BEGIN TRANSACTION;
    INSERT INTO dbo._table1 (id) VALUES(1); --valid insert
    INSERT INTO dbo._table2 (id) VALUES(4); --invalid insert
COMMIT TRANSACTION;

当我们在SSMS中运行时,我们看到一个错误:

  

INSERT语句与FOREIGN KEY约束冲突   “FK_table2”。冲突发生在数据库“db”,表中   “dbo._key”,列'id'。

当我们查询_table1中的内容时,我们会看到插入并随后提交的记录,因为XACT_ABORT未打开。这个结果很有意义,因为我们没有中止,但真正的问题是SQL Server在打开该选项时的行为方式。现在使用不同的中止设置进行相同的查询:

SET XACT_ABORT ON;
BEGIN TRANSACTION;
    INSERT INTO dbo._table1 (id) VALUES(1); --valid insert
    INSERT INTO dbo._table2 (id) VALUES(4); --invalid insert
COMMIT TRANSACTION;

我们在SSMS中看到相同的错误,但这次没有记录插入_table1,因为当SQL Server遇到运行时错误时整个批处理被回滚。这是MS文档中的关键词:

  

当SET XACT_ABORT为ON时,如果Transact-SQL语句引发了   运行时错误,整个事务终止并回滚。

     

编译错误(例如语法错误)不受SET的影响   XACT_ABORT。

(另外,我使用了多个表来公开和解决XACT_ABORT如果在与生成错误的语句中引用的表不同的表上进行插入或更新而不会回滚语句的神话。该声明是无根据且不真实。无论表格如何,都会回滚事务中的所有语句。)

那么最大的问题是:尽管设置了XACT_ABORT,整个批次何时才会回滚? Erland Sommarskog更加简洁(并且相当早)我已经解决了这个问题。 XACT_ABORT ON在遇到错误时不回滚批次的已知条件是:

  
      
  • 您使用RAISERROR引发的错误。
  •   
  • Compilation errors(通常终止范围)不会终止批处理。
  •   
  • 错误266,EXECUTE之后的事务计数表示缺少COMMIT或ROLLBACK TRANSACTION语句。
  •   

所以,例如:

SET XACT_ABORT ON;
BEGIN TRANSACTION;
    INSERT INTO dbo._table1 (id) VALUES(1); --valid insert
    RAISERROR (N'omg the sky is falling!!!', 16, 1); --my own error
COMMIT TRANSACTION;

由于我的RAISERROR不会触发回滚,因此仍会导致记录被插入和提交。同样,我们可能会遇到不会回滚批处理的语法错误:

EXEC dbo._proc;

这是一个非常好的问题,并强调了在SQL Server中处理错误没有一个通用的解决方案。 XACT_ABORT在某些情况下非常有用,但开发人员需要在依赖设置处理所有情况下的回滚之前了解潜在的影响和限制。