【问题标题】:Transactions breaking when using SP_ExecuteSQL使用 SP_ExecuteSQL 时事务中断
【发布时间】:2016-07-19 14:21:37
【问题描述】:

我使用的是 SQLServer 2014,我有一个简单的数据库,其中有一个表,该表有一个 ID 和一个名为 data 的 varchar 列。当我运行以下语句时有一些奇怪的行为:

SET XACT_ABORT ON
BEGIN TRANSACTION
    exec sp_executesql N'some nonsense'
    insert into testTable values ('b')

COMMIT  

SSMS 显示出现错误,因为我试图在 sp_executesql 调用中运行不正确的查询。但是,它也显示1 row(s) affected。如果我在 testTable 上运行选择查询,我可以看到值 'b' 已插入。

如果我将语句包装在 TRY/CATCH 块中,一切都会按预期工作,并且整个事务操作都会回滚:

BEGIN TRANSACTION
BEGIN TRY
    exec sp_executesql N'some nonsense'
    insert into testTable values ('b')

    COMMIT
END TRY
BEGIN CATCH
    SELECT ERROR_MESSAGE()
    ROLLBACK
END CATCH

SET XACT_ABORT ON 不应该确保在出现问题时回滚整个事务吗?有没有我遗漏的设置?

谢谢

【问题讨论】:

  • 看起来这取决于some nonsense 是什么,如果它是编译时错误,它的行为就像你说的那样。如果是运行时错误(例如exec sp_executesql N'SELECT 1/0'),它会按照您的意愿行事。
  • 好吧,我不知道这会有所作为。我有一个语法错误,这可以解释。但是,是否有一些文档可以解释它为什么这样做?我仍然希望整个事务能够回滚,即使它是编译时错误。还是 XACT_ABORT 选项只处理运行时错误?
  • 我以前不知道这种行为。我不知道这是否是“设计使然”。
  • 文档确实说“当 SET XACT_ABORT 为 ON 时,如果 Transact-SQL 语句引发 run-time 错误,则整个事务将终止并回滚。”所以也许从技术上讲,它正在做它应该做的事情。由于错误是编译时错误,尽管是在外部运行的批处理中。
  • @JeroenMostert 你能把这个写在答案中,以便我选择它吗?我认为这描述了我所追求的,这就是发生这种情况的原因。

标签: sql-server transactions sql-server-2014 xact-abort


【解决方案1】:

发生这种情况是因为未包含在 TRY/CATCH 中的运行时语法错误不会中止活动事务,即使将 XACT_ABORT 设置为 ON 也是如此。什么中止和不中止以及在什么情况下的确切规则一点也不简单或明显。 Erland Sommarskog 有an excellent write-up on error handling in generalthe rules of what does and doesn't abort in particular

我不会在这里复制所有内容,但这里的问题归结为它的本质:

SET XACT_ABORT ON   -- or OFF, it makes no difference
BEGIN TRANSACTION
EXEC ('SELECT')     -- Incorrect syntax near 'SELECT'
PRINT @@TRANCOUNT   -- Prints 1, transaction is still going
COMMIT
PRINT @@TRANCOUNT   -- Prints 0, transaction succeeded

尽管XACT_ABORT ON,执行不仅没有停止,事务甚至没有中止。添加TRY/CATCH会改变规则:

SET XACT_ABORT ON
BEGIN TRANSACTION
BEGIN TRY
    EXEC ('SELECT')              -- Incorrect syntax near 'SELECT'
    PRINT 'After bad statement.' -- Does not print
    COMMIT
END TRY
BEGIN CATCH
    PRINT @@TRANCOUNT            -- Prints 1, transaction is still going, but it's doomed
END CATCH
-- Error here: 
-- 'Uncommittable transaction is detected at the end of the batch. 
--  The transaction is rolled back.'

现在事务注定要失败,如果我们自己不回滚它,SQL Server 会为我们做这件事(有错误)。这种厄运完全是由XACT_ABORT 提供的,因为关闭它会再次产生不同的结果:

SET XACT_ABORT OFF
BEGIN TRANSACTION
BEGIN TRY
    EXEC ('SELECT')               -- Incorrect syntax near 'SELECT'
    PRINT 'After bad statement.'  -- Does not print
    COMMIT
END TRY
BEGIN CATCH
    PRINT @@TRANCOUNT             -- Prints 1, transaction is still going
END CATCH
PRINT @@TRANCOUNT                 -- Prints 1, transaction is still going!
ROLLBACK

故事的寓意是:在 T-SQL 中正确处理错误非常棘手。通常对我有用的是对任何重要的一批语句执行SET XACT_ABORT ON,并在 SQL Server 之外完全启动和提交或回滚事务(通过客户端代码)。这避免了理解什么会暂停或终止事务的许多困难,因为 SQL Server 传递回客户端的任何错误最终都会导致回滚。但是,当然,即使这样也不是灵丹妙药。

【讨论】:

    【解决方案2】:

    TRY...CATCH with TRANSACTION for MSSQL

    BEGIN TRANSACTION;  
      
    BEGIN TRY  
        -- Generate a constraint violation error.  
        SELECT 1/0
    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  
    

    Referenc

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-13
      • 2011-04-18
      • 1970-01-01
      • 2012-06-10
      • 2013-03-16
      • 1970-01-01
      • 2010-12-04
      相关资源
      最近更新 更多