使用 SP_ExecuteSQL 时交易中断
Transactions breaking when using SP_ExecuteSQL
我正在使用 SQLServer 2014,我有一个简单的数据库,其中有一个 table,它有一个 ID 和一个名为数据的 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 上 运行 一个 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
SET XACT_ABORT ON
难道不应该确保在出现问题时回滚整个事务吗?是否有我缺少的设置?
谢谢
发生这种情况是因为未包含在 TRY
/CATCH
中的运行时语法错误不会中止活动事务,即使 XACT_ABORT
设置为 [=16] =].什么会中止和什么不会中止,以及在什么情况下,确切的规则一点也不直截了当或显而易见。 Erland Sommarskog an excellent write-up on error handling in general and the 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服务器会为我们完成(有错误)。这种厄运完全由 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 服务器外启动和提交或回滚(通过客户端代码)。这避免了很多理解什么会停止或不会停止事务的困难,因为 SQL 服务器传递回客户端的任何错误最终都会导致回滚。但是,当然,即使那样也不是灵丹妙药。
尝试...使用 MSSQL 的 TRANSACTION 捕获
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
我正在使用 SQLServer 2014,我有一个简单的数据库,其中有一个 table,它有一个 ID 和一个名为数据的 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 上 运行 一个 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
SET XACT_ABORT ON
难道不应该确保在出现问题时回滚整个事务吗?是否有我缺少的设置?
谢谢
发生这种情况是因为未包含在 TRY
/CATCH
中的运行时语法错误不会中止活动事务,即使 XACT_ABORT
设置为 [=16] =].什么会中止和什么不会中止,以及在什么情况下,确切的规则一点也不直截了当或显而易见。 Erland Sommarskog an excellent write-up on error handling in general and the 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服务器会为我们完成(有错误)。这种厄运完全由 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 服务器外启动和提交或回滚(通过客户端代码)。这避免了很多理解什么会停止或不会停止事务的困难,因为 SQL 服务器传递回客户端的任何错误最终都会导致回滚。但是,当然,即使那样也不是灵丹妙药。
尝试...使用 MSSQL 的 TRANSACTION 捕获
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