4

我正在使用 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问题,不应该确保整个事务回滚吗?有没有我错过的设置?

谢谢

4

2 回答 2

4

发生这种情况是因为未包含在TRY/中的运行时语法错误CATCH不会中止活动事务,即使XACT_ABORT设置为ON. 什么中止和不中止以及在什么情况下的确切规则一点也不简单或明显。Erland Sommarskog对一般的错误处理以及具体的中止和不中止的规则有一篇出色文章

我不会在这里重现所有这些,但这里的问题归结为它的要点:

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 传递回客户端的任何错误最终都会导致回滚。但是,当然,即使这样也不是灵丹妙药。

于 2016-07-21T23:46:21.337 回答
0

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  

参考

于 2020-08-20T03:40:49.843 回答