【问题标题】:SQL Server 2005: Confusion using XACT_ABORT=ON with TRY...CATCHSQL Server 2005:混淆使用 XACT_ABORT=ON 和 TRY...CATCH
【发布时间】:2015-03-22 15:52:20
【问题描述】:

当 TRY 块中出现错误时,我对使用 XACT_ABORT ON 和 TRY...CATCH 构造来尝试回滚 CATCH 块中的事务感到有些困惑。

我有一个像这样结构化的存储过程(当然在这里简化了):

CREATE PROCEDURE dbo.usp_clean_and_re_Insert
AS
   SET XACT_ABORT ON;

   BEGIN TRY

      BEGIN TRANSACTION

      -- first clear the table
      DELETE FROM dbo.table1

      -- re-populate the table
      INSERT INTO dbo.table1
      (col1, col2, col3)
      SELECT  1
              ,dbo.fn_DoSomething('20150101')
              ,dbo.fn_DoSomething('20150123')

      COMMIT TRANSACTION

   END TRY

BEGIN CATCH
-- Test XACT_STATE for 0, 1, or -1.
-- If 1, the transaction is committable.
-- If -1, the transaction is uncommittable and should 
--     be rolled back.
-- XACT_STATE = 0 means there is no transaction and
--     a commit or rollback operation would generate an error.

-- Test whether the transaction is uncommittable.
IF (XACT_STATE()) = -1
BEGIN
    PRINT 'The transaction is in an uncommittable state.' +
          ' Rolling back transaction.'
    ROLLBACK TRANSACTION;
END;

-- Test whether the transaction is active and valid.
IF (XACT_STATE()) = 1
BEGIN
    PRINT 'The transaction is committable.' + 
          ' Committing transaction.'
    COMMIT TRANSACTION;   
END;
END CATCH;

因此,SP 旨在像这样工作:如果事务在任何时候失败,它应该回滚。所以当插入位失败时,删除位应该回滚,即表应该和之前一样。

现在,假设在运行时 dbo.fn_DoSomething() 函数不可用(它已被 DBA 错误地删除)。上面写的 SP 按预期工作,即事务回滚,表保持不变,SSMS 中显示的错误消息如下所示:

"消息 208,级别 16,状态 1,过程 usp_clean_and_re_Insert,第 15 行 无效的对象名称 'dbo.fn_DoSOthing'。"

但是由于某种原因,来自 CATCH 块的 PRINT 语句似乎没有执行,即我在 SSMS 中看不到它们?关于 TRY...CATCH 的 Microsoft 文档说,如果在 TRY 块中执行期间发生错误,则会将执行传递给 CATCH 块 (https://msdn.microsoft.com/en-us/library/ms175976(v=sql.90).aspx)。

但是,如果我删除 XACT_ABORT ON,事情会变得更加奇怪:

  1. PRINT 语句仍然没有出现在 SSMS 中

  2. 正确显示如上相同的错误,即

"消息 208,级别 16,状态 1,过程 usp_clean_and_re_Insert,第 15 行 无效的对象名称 'dbo.fn_DoSOthing'。"

  1. 最后一个错误提示:

“消息 266,级别 16,状态 2,程序 usp_clean_and_re_Insert,第 52 行 EXECUTE 之后的事务计数表明缺少 COMMIT 或 ROLLBACK TRANSACTION 语句。先前计数 = 0,当前计数 = 1。”

这会导致表被锁定,直到我断开 SSMS(SP 运行的查询窗口),然后表再次可用,所有结果都完好无损(因此数据库引擎必须隐式回滚不可提交的事务)。

阅读有关此错误消息的其他帖子(例如此:Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0),我了解我需要检查 CATCH 块中的 XACT_STATE 并回滚不可提交的事务(这与来自:https://msdn.microsoft.com/en-us/library/ms189797.aspx 的建议相同) ,但这正是我在上述 SP 中所做的,但在我断开 SSMS 之前,事务不会回滚(没有 XACT_ABORT ON)?

我很困惑!总结:

  1. 为什么我在 SSMS 中看不到 PRINT 语句?

  2. 为什么当 XACT_ABORT ON 从存储过程中移除时,CATCH 块中的 ROLLBACK TRANSACTION 没有得到执行?

  3. 如果 XACT_ABORT ON 似乎可以自己完成这项工作,为什么还要使用 TRY...CACTH? IE。如果我删除 Try..catch 并将 XACT_ABORT 保留为 ON,它会回滚事务,那么为什么我需要在 catch 块中使用隐式 ROLLBACK TRANSACTION 的 TRY CATCH?

【问题讨论】:

    标签: sql sql-server sql-server-2005 try-catch xact-abort


    【解决方案1】:

    我认为,并且我可能是错的,XACT_ABORT 在这种情况下将不起作用,因为您实际上还没有开始交易。您的函数不存在,这意味着 SQL Server 甚至在您接触数据库之前就使您的事务失败。阅读 XACT_STATE 的手册页和提供的示例,看起来您实际上必须在读/写时失败。您的查询没有走那么远,因为优化器发现您有语法错误(正在调用的不存在的函数,在它应该执行的时候您还没有创建)。

    如果您阅读此页面(XACT_STATE 的手册页)和可用示例,它强烈建议只有在遇到约束错误时才设置 XACT_STATE,这只会在实际尝试更改数据时发生:

    https://msdn.microsoft.com/en-us/library/ms189797.aspx

    作为一种测试方式,您可以让分析器监视 dbo.table1 上的事务。我敢打赌,在查询中使用 XACT_ABORT ON 时,删除不会发生。删除它后,优化器可能会选择不同的执行计划来允许删除运行。

    【讨论】:

    • XACT_ABORT ON 在这种情况下似乎确实有效,因为 Table1 是完整的 - DELETE FROM TABLE1 似乎通过 SET XACT_ABORT ON 回滚。
    • 你知道它实际上在工作吗?比如,你能看到数据被删除然后事务被回滚吗?就像我说的,我认为数据完好无损的原因不是因为它实际上被回滚了,而是因为删除实际上并没有首先发生。如果您不想分析,最简单的测试方法是添加更多打印以查看查询失败的确切位置。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多