Sql server 使用SP_ExecuteSQL时事务中断
我使用的是SQLServer2014,我有一个简单的db,其中一个表有一个ID和一个名为data的varchar列。当我运行以下语句时,出现了一些奇怪的行为: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调用中运行错误的查询。但是,
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你能把它写在一个答案中,这样我就可以选择它吗?我想这描述了我所追求的,这就是为什么会发生这种情况。