sql try / catch rollback / commit - 防止回滚后的错误提交

时间:2014-08-05 19:17:58

标签: sql sql-server exception transactions try-catch

我正在尝试编写一个具有事务和try / catch块的MS sql脚本。如果它捕获异常,则回滚事务。如果不是,则提交事务。我见过几个不同的网站说这样做:

begin transaction
begin try
    --main content of script here
end try
begin catch
    rollback transaction
end catch

commit transaction

但即使在捕获异常的情况下,我们仍然不会点击“提交事务”行吗?这不会导致SQL错误,因为事务已经回滚了吗?我认为应该这样做:

declare @success bit = 1

begin transaction
begin try
    --main content of script here
end try
begin catch
    rollback transaction
    set @success = 0
end catch

if(@success = 1)
begin
    commit transaction
end

如果常用的解决方案不包含@success变量?提交已经回滚的事务是否没有发生sql错误?我是否错误地说在捕获异常的情况下仍然会遇到第一个代码示例的“commit transaction”行?

5 个答案:

答案 0 :(得分:67)

我一直认为this was one of the better articles就这个问题而言。它包括以下示例,我认为它清楚,并包括可靠的嵌套事务所需的经常被忽视的@@ trancount

PRINT 'BEFORE TRY'
BEGIN TRY
    BEGIN TRAN
     PRINT 'First Statement in the TRY block'
     INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(1, 'Account1',  10000)
     UPDATE dbo.Account SET Balance = Balance + CAST('TEN THOUSAND' AS MONEY) WHERE AccountId = 1
     INSERT INTO dbo.Account(AccountId, Name , Balance) VALUES(2, 'Account2',  20000)
     PRINT 'Last Statement in the TRY block'
    COMMIT TRAN
END TRY
BEGIN CATCH
    PRINT 'In CATCH Block'
    IF(@@TRANCOUNT > 0)
        ROLLBACK TRAN;

    THROW; -- raise error to the client
END CATCH
PRINT 'After END CATCH'
SELECT * FROM dbo.Account WITH(NOLOCK)
GO

答案 1 :(得分:27)

在你的第一个例子中,你是对的。无论try块是否触发,批处理都将触发提交事务。

在您的第二个例子中,我同意其他评论者的意见。使用成功标志是不必要的。

我认为以下方法基本上是一种轻量级的最佳实践方法。

如果要查看它如何处理异常,请将第二个插入的值从255更改为256.

CREATE TABLE #TEMP ( ID TINYINT NOT NULL );
INSERT  INTO #TEMP( ID ) VALUES  ( 1 )

BEGIN TRY
    BEGIN TRANSACTION

    INSERT  INTO #TEMP( ID ) VALUES  ( 2 )
    INSERT  INTO #TEMP( ID ) VALUES  ( 255 )

    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    DECLARE 
        @ErrorMessage NVARCHAR(4000),
        @ErrorSeverity INT,
        @ErrorState INT;
    SELECT 
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE();
    RAISERROR (
        @ErrorMessage,
        @ErrorSeverity,
        @ErrorState    
        );
    ROLLBACK TRANSACTION
END CATCH

SET NOCOUNT ON

SELECT ID
FROM #TEMP

DROP TABLE #TEMP

答案 2 :(得分:1)

我在ms sql脚本模式下多次成功使用了 Try-Catch 提交事务-回滚事务错误跟踪。 / p>

您的TRY块如下所示

 BEGIN TRY
 BEGIN TRANSACTION T
 ----
 //your script block
 ----
 COMMIT TRANSACTION T 
 END TRY

您的CATCH块将如下所示

BEGIN CATCH
DECLARE @ErrMsg NVarChar(4000), 
        @ErrNum Int, 
        @ErrSeverity Int, 
        @ErrState Int, 
        @ErrLine Int, 
        @ErrProc NVarChar(200)
 SELECT @ErrNum = Error_Number(), 
       @ErrSeverity = Error_Severity(), 
       @ErrState = Error_State(), 
       @ErrLine = Error_Line(), 
       @ErrProc = IsNull(Error_Procedure(), '-')
 SET @ErrMsg = N'ErrLine: ' + rtrim(@ErrLine) + ', proc: ' + RTRIM(@ErrProc) + ', 
       Message: '+ Error_Message()

您的ROLLBACK脚本将成为CATCH块的一部分,如下所示

IF (@@TRANCOUNT) > 0 
BEGIN
PRINT 'ROLLBACK: ' + SUBSTRING(@ErrMsg,1,4000)
ROLLBACK TRANSACTION T
END
ELSE
BEGIN
PRINT SUBSTRING(@ErrMsg,1,4000);   
END

END CATCH

您需要将不同的脚本块用作一个块。如果 TRY 块中发生任何错误,它将进入 CATCH 块。在那里设置有关错误号,错误严重性,错误行等的各种详细信息。最后,所有这些详细信息将附加到@ErrMsg参数。然后它将检查事务计数(@@ TRANCOUNT> 0),即事务中是否有任何要回滚的内容。如果存在,则显示错误消息和 ROLLBACK TRANSACTION 。否则,只需打印错误消息。

我们将 COMMIT TRANSACTION T 脚本保留在TRY块的最后一行,以确保仅在交易代码中的所有代码之后才提交交易(数据库中的最终更改)。 TRY块已成功运行。

答案 3 :(得分:0)

交易柜台 - @@ TRANCOUNT = 0 开始尝试 - @@ TRANCOUNT = 0 BEGIN TRANSACTION tran1   - @@ TRANCOUNT = 1          - 你的代码          - 如果失败@@ TRANCOUNT = 1          - 如果成功@@ TRANCOUNT = 0 COMMIT TRANSACTION tran1 结束尝试 开始捕捉     打印' FAILED' 结束捕获

答案 4 :(得分:-1)

下面可能有用。

来源:https://msdn.microsoft.com/en-us/library/ms175976.aspx

BEGIN TRANSACTION;

BEGIN TRY
    -- your code --
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() AS ErrorState
        ,ERROR_PROCEDURE() AS ErrorProcedure
        ,ERROR_LINE() AS ErrorLine
        ,ERROR_MESSAGE() AS ErrorMessage;

    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
END CATCH;

IF @@TRANCOUNT > 0
    COMMIT TRANSACTION;
GO
相关问题