SQL Server-事务会因错误而回滚?


192

我们拥有在SQL Server 2005上运行某些SQL的客户端应用程序,例如:

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

它由一个长字符串命令发送。

如果插入操作之一失败,或者命令的任何部分失败,SQL Server会回滚事务吗?如果不回滚,是否必须发送第二条命令来回滚?

我可以提供有关所使用的api和语言的详细信息,但我认为SQL Server对于任何语言都应做出相同的响应。


Answers:


204

您可以将set xact_abort on事务放在事务之前,以确保sql在发生错误的情况下自动回滚。


1
可以在MS SQL 2K及更高版本上使用吗?这似乎是最简单的解决方案。
jonathanpeppers

1
它出现在2000、2005和2008的文档中,所以我认为是的。我们在2008

8
我需要关闭它还是每次会话关闭它?
Marc

5
@Marc的范围xact_abort在连接级别。
基思

2
@AlexMcMillan DROP PROCEDURE语句修改数据库结构,这与仅对数据起作用的INSERT不同。因此它不能包装在交易中。我过于简化了,但是基本上就是这样。
eksortso

195

您是正确的,因为整个事务都会回滚。您应该发出命令以将其回滚。

您可以TRY CATCH按以下步骤将其包装

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
    RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH

2
我更喜欢DyingCactus的解决方案,他是1行代码更改。如果您的出于某些原因更好(或更可靠),请告诉我。
jonathanpeppers

13
尝试捕获使您能够捕获(并可能修复)错误,并在需要时发出自定义错误消息。
Raj More

10
我认为,“捕获和记录”比“捕获和修复”更为频繁。
quillbreaker 2012年

24
至少在SQL Server 2008R2和更高版本中,RAISERROR的语法不正确。有关正确的语法,请参见msdn.microsoft.com/en-us/library/ms178592.aspx
Eric J.

2
@BornToCode要确保事务存在。.假设您已在给定条件下(在中try)回滚了事务,但是此后代码失败。没有更多交易了,但是您仍然要进入catch
加百利总经理

42

这是获取与错误消息一起使用MSSQL Server 2016的代码:

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
        DECLARE @ErrorState INT = ERROR_STATE()

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

1
我不得不DECLARE @Var TYPE; SET @Var = ERROR;在sql server 2005中使用引发错误。否则,上述引发错误的代码也适用于较旧的DB。尝试将默认值分配给局部变量是造成问题的原因。
jtlindsey

您可以使用简单的THROW;而不是RAISERROR和ERROR_ *声明。
rodzmkii

21

来自MDSN文章,控制事务(数据库引擎)

如果批处理中发生运行时语句错误(例如违反约束),则数据库引擎中的默认行为是仅回滚生成错误的语句。您可以使用SET XACT_ABORT语句更改此行为。执行SET XACT_ABORT ON后,任何运行时语句错误都会导致当前事务的自动回滚。编译错误(例如语法错误)不受SET XACT_ABORT的影响。有关更多信息,请参见SET XACT_ABORT(Transact-SQL)。

在您的情况下,如果任何插入失败,它将回滚整个事务。


3
我们需要处理什么语法错误?还是编译错误?如果其中有人发生,则应回滚整个交易
MonsterMMORPG '16

捕获编译/语法错误是SSDT项目的目的。:-)
编码员乔

10

如果插入操作之一失败,或者命令的任何部分失败,SQL Server会回滚事务吗?

不,不是的。

如果不回滚,是否必须发送第二条命令来回滚?

当然,您应该发出ROLLBACK而不是COMMIT

如果要决定是提交还是回滚事务,则应从COMMIT语句中删除该语句,检查插入的结果,然后根据检查结果发出COMMITROLLBACK执行。


因此,如果遇到错误,请说“主键冲突”,是否需要发送第二个回滚调用?我想这很有道理。如果出现与网络有关的错误,例如在运行时间很长的SQL语句中断开了连接,该怎么办?
jonathanpeppers

2
当连接超时时,基础网络协议(例如Named PipesTCP)会断开连接。连接断开时,SQL Server停止所有当前正在运行的命令并回滚事务。
Quassnoi

1
因此,DyingCactus的解决方案可以解决我的问题,感谢您的帮助。
jonathanpeppers

如果您需要中止任何错误,那么可以,这是最佳选择。
Quassnoi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.