我对使用XACT_ABORT ON和TRY ... CATCH构造稍微感到困惑,以便在TRY块中出现错误时尝试回滚CATCH块中的事务。
我有一个像这样结构化的存储过程(当然简化):
CREATE PROCEDURE dbo.usp_clean_and_re_Insert
AS
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION
-- first clear the table
DELETE FROM dbo.table1
-- re-populate the table
INSERT INTO dbo.table1
(col1, col2, col3)
SELECT 1
,dbo.fn_DoSomething('20150101')
,dbo.fn_DoSomething('20150123')
COMMIT TRANSACTION
END TRY
BEGIN CATCH
-- Test XACT_STATE for 0, 1, or -1.
-- If 1, the transaction is committable.
-- If -1, the transaction is uncommittable and should
-- be rolled back.
-- XACT_STATE = 0 means there is no transaction and
-- a commit or rollback operation would generate an error.
-- Test whether the transaction is uncommittable.
IF (XACT_STATE()) = -1
BEGIN
PRINT 'The transaction is in an uncommittable state.' +
' Rolling back transaction.'
ROLLBACK TRANSACTION;
END;
-- Test whether the transaction is active and valid.
IF (XACT_STATE()) = 1
BEGIN
PRINT 'The transaction is committable.' +
' Committing transaction.'
COMMIT TRANSACTION;
END;
END CATCH;
所以SP的工作方式如下:如果事务在任何时候都失败,它应该回滚。因此,当插入位失败时,应该回滚删除位,即表应该与之前的状态相同。
现在,让我们说在运行时dbo.fn_DoSomething()函数不可用(DBA错误地删除了它)。上面写的SP按预期工作,即事务被回滚,表保持不变,SSMS中显示的错误消息如下所示:
" 消息208,级别16,状态1,过程usp_clean_and_re_Insert,第15行 无效的对象名称' dbo.fn_DoSOmething'。"
但由于某种原因,来自CATCH块的PRINT语句似乎没有执行,即我在SSMS中看不到它们?关于TRY ... CATCH的Microsoft文档说如果在TRY块中执行期间发生错误,则执行被传递到CATCH块(https://msdn.microsoft.com/en-us/library/ms175976(v=sql.90).aspx)。
但是,如果我删除XACT_ABORT ON,事情就会变得更加奇怪:
PRINT语句仍未出现在SSMS中
正确显示与上述相同的错误,即
" 消息208,级别16,状态1,过程usp_clean_and_re_Insert,第15行 无效的对象名称' dbo.fn_DoSOmething'。"
"消息266,级别16,状态2,过程usp_clean_and_re_Insert,第52行 EXECUTE之后的事务计数表示缺少COMMIT或ROLLBACK TRANSACTION语句。先前的计数= 0,当前计数= 1."
这导致表被锁定,直到我断开SSMS(运行SP的查询窗口),之后表再次可用,所有结果都保持不变(因此数据库引擎必须隐式回滚不可提交的事务)。 / p>
阅读有关此错误消息的其他帖子(例如:Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0),我知道我需要检查CATCH块中的XACT_STATE并回滚不可提交的事务(这是来自:{{ 3}}),但这正是我在上面的SP中所做的,但是在我断开SSMS之前事务没有被回滚(没有XACT_ABORT ON)?
我很困惑!总结:
为什么我在SSMS中看不到PRINT语句?
为什么在从存储过程中删除XACT_ABORT ON时,不会执行CATCH块中的ROLLBACK TRANSACTION?
如果XACT_ABORT ON似乎独自完成这项任务,为什么要使用TRY ... CACTH?即如果我删除Try..catch并将XACT_ABORT保留为ON它会使事务回滚,那么为什么我需要在catch块中使用隐式ROLLBACK TRANSACTION进行TRY CATCH?
答案 0 :(得分:0)
我认为,我可能错了,XACT_ABORT在这种情况下不起作用,因为你还没有真正开始交易。您的功能不存在,这意味着在您触摸数据库之前,SQL Server会使您的事务失败。阅读XACT_STATE的手册页,以及提供的示例,看起来您必须实际上在读/写上失败。你的查询没有那么做,因为优化器看到你有一个语法错误(调用不存在的函数,你应该在它应该被执行时创建它)。
如果你阅读了这个页面(XACT_STATE的手册页)和可用的例子,它强烈建议只在遇到约束错误时才设置XACT_STATE,只有在实际尝试更改数据时才会发生:
https://msdn.microsoft.com/en-us/library/ms189797.aspx
作为一种测试方法,您可以让分析器监视dbo.table1上的事务。我敢打赌,在查询中使用XACT_ABORT ON,删除不会发生。删除它后,优化器可能会选择一个允许删除运行的不同执行计划。