【发布时间】:2015-03-22 15:52:20
【问题描述】:
当 TRY 块中出现错误时,我对使用 XACT_ABORT ON 和 TRY...CATCH 构造来尝试回滚 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_DoSOthing'。"
但是由于某种原因,来自 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_DoSOthing'。"
- 最后一个错误提示:
“消息 266,级别 16,状态 2,程序 usp_clean_and_re_Insert,第 52 行 EXECUTE 之后的事务计数表明缺少 COMMIT 或 ROLLBACK TRANSACTION 语句。先前计数 = 0,当前计数 = 1。”
这会导致表被锁定,直到我断开 SSMS(SP 运行的查询窗口),然后表再次可用,所有结果都完好无损(因此数据库引擎必须隐式回滚不可提交的事务)。
阅读有关此错误消息的其他帖子(例如此:Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0),我了解我需要检查 CATCH 块中的 XACT_STATE 并回滚不可提交的事务(这与来自:https://msdn.microsoft.com/en-us/library/ms189797.aspx 的建议相同) ,但这正是我在上述 SP 中所做的,但在我断开 SSMS 之前,事务不会回滚(没有 XACT_ABORT ON)?
我很困惑!总结:
为什么我在 SSMS 中看不到 PRINT 语句?
为什么当 XACT_ABORT ON 从存储过程中移除时,CATCH 块中的 ROLLBACK TRANSACTION 没有得到执行?
如果 XACT_ABORT ON 似乎可以自己完成这项工作,为什么还要使用 TRY...CACTH? IE。如果我删除 Try..catch 并将 XACT_ABORT 保留为 ON,它会回滚事务,那么为什么我需要在 catch 块中使用隐式 ROLLBACK TRANSACTION 的 TRY CATCH?
【问题讨论】:
标签: sql sql-server sql-server-2005 try-catch xact-abort