使用try..catch时,SQL Transaction无法提供..为什么?

bar*_*ven 13 sql sql-server transactions try-catch sql-server-2008-r2

你好有用的朋友,

我们在这里遇到了一个问题,我无法弄清楚它为什么会表现出来.希望你能帮助我.

给出TSQL中的以下两个(简化的)存储过程(SQL Server 2008R2)

create procedure [datetransaction1] 
as
begin
    begin try
        begin transaction
        declare @a datetime
        exec datetransaction2 '2013-02-02 22:21', @a output
        select @a
        exec datetransaction2 '2013-020222:22', @a output
        select @a
        exec datetransaction2 '2013-02-02 22:23', @a output
        select @a

        commit transaction
    end try
    begin catch
        print 'Catch'
    end catch
end
Run Code Online (Sandbox Code Playgroud)

create procedure [dbo].[datetransaction2] @text nvarchar(100), @res datetime OUTPUT  
AS
BEGIN 
    BEGIN TRY
        if (LEN(@text) = 16) SET @text = replace(@text, ' ', 'T') + ':00.000'
        else if (LEN(@text) = 19) SET @text = replace(@text, ' ', 'T') + '.000'
        else SET @text = replace(@text, ' ', 'T') 
        PRINT 'trydate:' + @text
        SELECT @res =convert(datetime, @text, 126)
    END TRY
    BEGIN CATCH
        PRINT ERROR_SEVERITY()
        PRINT 'errordate:' + @text
    END CATCH
END
Run Code Online (Sandbox Code Playgroud)

如果然后执行exec datetransaction1,我们看到所有3个调用datetransaction2都被执行,第一个和最后一个(正如预期)正确运行,第二个进入CATCH块内datetransaction2.

到现在为止还挺好.

但随后我们登上了catch块,datetransaction1传递的信息是交易不可持续:

Msg 266, Level 16, State 2, Procedure datetransaction1, Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
Msg 3998, Level 16, State 1, Line 1
Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.
Run Code Online (Sandbox Code Playgroud)

这不应该发生(我认为).我们在子程序中发现了错误,为什么交易突然变得无法承受?

有人可以向我解释一下吗?

请注意,我们可能会找到解决此问题的方法,但我对其背后的想法更感兴趣.为什么这笔交易在这里突然变得无法承受?

Jea*_*n.C 16

原因是:无论错误发生在什么位置,无论是否处于TRY块中,无论是否保存了事务状态,是否在程序中发生错误,无论您是什么,Sql Server都会在事务发生故障时执行事务.做.

当其中一个过程调用发生错误时,事务就注定了.您只能完全回滚它(任何保存点都无济于事).

最后,由于交易注定失败,你无法提交......

试试这个:

SET XACT_ABORT OFF -- pityful attempt to avoid the doom
BEGIN TRANSACTION
--
-- some useful TSQL instructions could be here
--
SAVE TRANSACTION SQL_SERVER_IS_GARBAGE -- another pityful attempt to do a partial restore
PRINT 'XACT_STATE='+CONVERT(varchar(10),XACT_STATE())
BEGIN TRY
  DECLARE @n int
  SELECT @n = CONVERT(int,'ABC') -- some very benign data error here (example)
  COMMIT TRANSACTION -- will never reach here
END TRY
BEGIN CATCH
  PRINT ERROR_MESSAGE()
  PRINT 'XACT_STATE='+CONVERT(varchar(10),XACT_STATE())
  IF XACT_STATE()=-1 BEGIN
    PRINT 'The transaction is doomed, say thanks to Sql Server!'
    PRINT 'CANNOT restore to the save point!'
    -- You can just cry here and abort all, you lost all the useful work
    ROLLBACK TRANSACTION
  END
  ELSE BEGIN
    -- would restore before the error if the transaction was not doomed
    ROLLBACK TRANSACTION SQL_SERVER_IS_GARBAGE -- will never reach here either!
  END  
END CATCH  
Run Code Online (Sandbox Code Playgroud)

  • 这是不正确的.调用SET XACT_ABORT OFF将使事务处于可提交状态**some*errors.例如,虽然这个特定的转换错误是一个真正的不可恢复的错误,但类似主键违规的东西却不是.在使用SET XACT_ABORT OFF捕获主键冲突后,事务仍处于可提交状态.与您的断言相反,SQL服务器不会导致事务"无论错误是什么",它只会导致某些类型的错误.证明:http://imgur.com/a/M4KVU (3认同)
  • 谢谢。虽然这不是我所希望的答案(我希望会有一个解决方案..),但这表明这个问题没有好的答案。很遗憾。 (2认同)