Sql server 使用SP_ExecuteSQL时事务中断

Sql server 使用SP_ExecuteSQL时事务中断,sql-server,transactions,sql-server-2014,xact-abort,Sql Server,Transactions,Sql Server 2014,Xact Abort,我使用的是SQLServer2014,我有一个简单的db,其中一个表有一个ID和一个名为data的varchar列。当我运行以下语句时,出现了一些奇怪的行为: SET XACT_ABORT ON BEGIN TRANSACTION exec sp_executesql N'some nonsense' insert into testTable values ('b') COMMIT SSMS显示出现错误,因为我试图在sp_executesql调用中运行错误的查询。但是,

我使用的是SQLServer2014,我有一个简单的db,其中一个表有一个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行
。如果我在testTable上运行select查询,我可以看到值“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
将XACT\u ABORT设置为ON是否应该确保在出现问题时回滚整个事务?有没有我错过的场景


谢谢

发生这种情况是因为没有包装在
TRY
/
CATCH
中的运行时语法错误不会中止活动事务,即使
XACT\u abort
设置为
ON
。关于什么可以和不可以中止,以及在什么情况下中止的确切规则一点也不直接或明显。埃尔兰·索马尔斯科格拥有和

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

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\u 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\u 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中正确的错误处理非常棘手。通常对我有效的方法是对任何非平凡的语句批执行
设置XACT_ABORT ON
,并让事务完全在SQL Server外部启动、提交或回滚(通过客户端代码)。这就避免了理解什么是、什么不是停止或终止事务的许多困难,因为SQL Server传递回客户端的任何错误最终都会导致回滚。但是,当然,即使这样也不是什么灵丹妙药。

尝试为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  

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