使用 try..catch.. 时无法提交 SQL 事务。为什么?

新手上路,请多包涵

我们在这里遇到了一个问题,我无法弄清楚它为什么会表现得如此。

给定 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

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

如果您随后执行 exec datetransaction1 ,我们会看到对 datetransaction2 的所有 3 个调用都已执行,第一个和最后一个(如预期的那样)运行正确,第二个进入 CATCH 阻止在 datetransaction2 内。

到目前为止,一切都很好。

但随后我们进入 datetransaction1 的 catch 块,并显示事务不可提交的消息:

 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.

这不应该发生(我认为)。我们在子过程中发现了错误,为什么事务会突然变得不可提交?

有人可以向我解释吗?

请注意,我们可能会找到解决此问题的方法,但我对它背后的想法更感兴趣。为什么这个交易在这里突然变得不可提交?

原文由 bartlaarhoven 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 981
2 个回答

原因是:SQL Server 在发生错误时终止事务,无论错误是什么,是否在 TRY 块中,是否保存了事务状态,错误是否发生在过程中,无论你做。

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

最后,由于交易注定要失败,你不能提交它……

尝试这个:

 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

原文由 Jean.C 发布,翻译遵循 CC BY-SA 4.0 许可协议

我有一个像你一样的错误,我意识到它发生是因为我在开始事务之前启动了托盘!我改变了他们的顺序,所以问题已经解决了!

  create procedure [datetransaction1]
as
begin
     begin transaction
        begin try
            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

原文由 Alireza Shamsian 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
logo
Stack Overflow 翻译
子站问答
访问
宣传栏