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

sql try/catch rollback/commit-防止回滚后的错误提交,sql,sql-server,exception,transactions,try-catch,Sql,Sql Server,Exception,Transactions,Try Catch,我正在尝试编写一个MS sql脚本,其中包含一个事务和一个try/catch块。如果捕获到异常,事务将回滚。如果没有,则提交事务。我看到一些不同的网站这样说: begin transaction begin try --main content of script here end try begin catch rollback transaction end catch commit transaction declare @success bit = 1 begin

我正在尝试编写一个MS sql脚本,其中包含一个事务和一个try/catch块。如果捕获到异常,事务将回滚。如果没有,则提交事务。我看到一些不同的网站这样说:

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

commit transaction
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
但是,即使在捕获异常的情况下,我们是否仍然会点击“提交事务”行?这不会因为事务已经回滚而导致SQL错误吗?我认为应该这样做:

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

commit transaction
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”行在捕获异常时仍然会被命中,这是不对的吗?

在第一个示例中,您是正确的。无论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
我一直在思考这个问题。它包括了下面的示例,我认为这是一个很清楚的例子,其中包括了可靠的嵌套事务所需的经常被忽略的@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
交易柜台

--@@TRANCOUNT = 0
begin try
--@@TRANCOUNT = 0
BEGIN TRANSACTION tran1
 --@@TRANCOUNT = 1

        --your code
        -- if failed  @@TRANCOUNT = 1
        -- if success @@TRANCOUNT = 0

COMMIT TRANSACTION tran1

end try

begin catch
    print 'FAILED'
end catch
下面可能有用

资料来源:


我多次成功地使用了下面的ms sql脚本模式,它使用了Try Catch提交事务-回滚事务错误跟踪

您的TRY块将如下所示

 BEGIN TRY
 BEGIN TRANSACTION T
 ----
 //your script block
 ----
 COMMIT TRANSACTION T 
 END TRY
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()
IF (@@TRANCOUNT) > 0 
BEGIN
PRINT 'ROLLBACK: ' + SUBSTRING(@ErrMsg,1,4000)
ROLLBACK TRANSACTION T
END
ELSE
BEGIN
PRINT SUBSTRING(@ErrMsg,1,4000);   
END

END CATCH
您的捕获块将如下所示

 BEGIN TRY
 BEGIN TRANSACTION T
 ----
 //your script block
 ----
 COMMIT TRANSACTION T 
 END TRY
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()
IF (@@TRANCOUNT) > 0 
BEGIN
PRINT 'ROLLBACK: ' + SUBSTRING(@ErrMsg,1,4000)
ROLLBACK TRANSACTION T
END
ELSE
BEGIN
PRINT SUBSTRING(@ErrMsg,1,4000);   
END

END CATCH
您的回滚脚本将是CATCH块的一部分,如下所示

 BEGIN TRY
 BEGIN TRANSACTION T
 ----
 //your script block
 ----
 COMMIT TRANSACTION T 
 END TRY
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()
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),即事务中是否有要回滚的内容。如果存在,则显示错误消息和回滚事务。否则,只需打印错误消息


我们将提交事务T脚本保留在TRY块的最后一行,以确保只有在TRY块中的所有代码成功运行后,它才提交事务(数据库中的最终更改)

实际上我不会做这两件事。我会将提交移动到TRY块中。不需要像您发布的那样添加其他变量。这是TRY/CATCH的一大优点,我们可以摆脱变量的混乱,到处检查它们。我同意Sean的方法,但要确定它是否会在提交事务中导致SQL错误,最简单的方法是尝试它。如果它是最被接受的方法,它可能不会。这应该通过简单地将它粘贴到SSMS中来实现(它对我不起作用),还是我需要一个存储过程?如果你不处理嵌套事务,是否有任何理由使用@TRANCOUNT?@DavidHammond-否,但是,您通常不知道调用方是否有活动事务add
;在
结束捕获之前抛出
,以引发错误,否则错误会被吃掉,您将不知道它发生了。竖起大拇指检查@tracount first-1。。。您的提交事务应该介于BEGIN TRY和END TRY之间-否则,一旦发生错误,您将回滚(不抛出任何内容),然后尝试提交已关闭的事务。