SQL Server - 事务在错误时回滚?

问题描述 投票:0回答:6

我们有在 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 应该对任何语言做出相同的响应。

sql sql-server sql-server-2005 transactions
6个回答
256
投票

您可以在事务前加上

set xact_abort on
,以确保sql在出现错误时自动回滚。


245
投票

您是对的,整个事务将被回滚。您应该发出命令将其回滚。

您可以将其包装在

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

    -- <EDIT>: From SQL2008 on, you must raise error messages as follows:
    DECLARE @ErrorMessage NVARCHAR(4000);  
    DECLARE @ErrorSeverity INT;  
    DECLARE @ErrorState INT;  

    SELECT   
       @ErrorMessage = ERROR_MESSAGE(),  
       @ErrorSeverity = ERROR_SEVERITY(),  
       @ErrorState = ERROR_STATE();  

    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);  
    -- </EDIT>
END CATCH

59
投票

这里是使用 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

28
投票

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

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

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


14
投票

如果其中一个插入失败,或者命令的任何部分失败,SQL Server 是否会回滚事务?

不,没有。

如果不回滚,我是否需要发送第二个命令来回滚?

当然,您应该发出

ROLLBACK
而不是
COMMIT

如果你想决定是提交还是回滚事务,你应该从语句中删除

COMMIT
语句,检查插入的结果,然后根据插入的结果发出
COMMIT
ROLLBACK
检查一下。


0
投票

作为替代方法,您还可以捕获每个语句后的错误号,然后使用 if 语句来确定是提交还是回滚。接受的答案是最好的一句话,但如果您想更多地了解所遇到的问题,而不只是将其回滚,您可以使用下面的示例并添加一些其他信息来查看问题。当然,您也可以使用

RAISERROR
来执行 Try-Catch 块。

这是我所拥有的快速示例:

DECLARE @errorNumber int;

BEGIN TRANSACTION;
    INSERT INTO [table2] ([field1], [field2])
    SELECT [fieldA], [fieldB]
    FROM [table1];
        
    SET @errorNumber = @@ERROR;

    UPDATE [table3]
    SET [field1] =
        (SELECT COUNT(ID)
        FROM [table2]
        WHERE [table2].[fieldA] = [table3].[field2])
    WHERE [field1] IS NULL;
        
    SET @errorNumber = @@ERROR;

IF @errorNumber = 0
    COMMIT TRANSACTION;
ELSE
    BEGIN
        ROLLBACK TRANSACTION;
        PRINT CONCAT('Transaction rolled back with error number: ',@errorNumber);
    END
© www.soinside.com 2019 - 2024. All rights reserved.