kam*_*ilk 5 sql sql-server stored-procedures transactions
的文档SET XACT_ABORT仅说明了启用此选项的效果。
当 SET XACT_ABORT 为 ON 时,如果 Transact-SQL 语句引发运行时错误,则整个事务将终止并回滚。
我不相信这就是全部真相。读完本文后,我担心如果在外部进程创建的事务中执行启用此选项的存储过程,则最终可能会回滚外部事务。幸运的是,我的担心被证明是没有根据的。然而,这意味着我现在并不真正理解如何XACT_ABORT运作。SQL Server 检查事务是否应该回滚的条件是什么?
我进行了以下实验:(此代码的摘要如下,因为代码块之前有一个编号列表会破坏 StackOverflow 的格式,呃)
CREATE TABLE Dummy
(
ID INT NOT NULL IDENTITY CONSTRAINT PK_Dummy PRIMARY KEY,
Text NVARCHAR(128) NOT NULL
)
CREATE UNIQUE NONCLUSTERED INDEX IX_Dummy_Text ON dbo.Dummy(Text)
GO
CREATE OR ALTER PROCEDURE InsertDummy
@Text NVARCHAR(128)
AS
BEGIN
SET NOCOUNT OFF
SET XACT_ABORT ON
INSERT dbo.Dummy (Text) VALUES (@Text)
END
GO
SET XACT_ABORT ON
BEGIN TRANSACTION
BEGIN TRY
EXEC dbo.InsertDummy @Text = N'Dummy'
EXEC dbo.InsertDummy @Text = N'Dummy' --DUPLICATE!
END TRY
BEGIN CATCH
PRINT 'ERROR! @@TRANCOUNT is ' + CONVERT(NVARCHAR, @@TRANCOUNT)
-- Echo the error
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
PRINT 'At the end @@TRANCOUNT is ' + CONVERT(NVARCHAR, @@TRANCOUNT)
IF @@TRANCOUNT>0
ROLLBACK
Run Code Online (Sandbox Code Playgroud)
XACT_ABORT.@@TRANCOUNT值以显示我们是否仍在交易中。它还使XACT_ABORT.该测试的输出是:
(1 row affected)
(0 rows affected)
ERROR! @@TRANCOUNT is 1
Msg 50000, Level 14, State 1, Line 74
Cannot insert duplicate key row in object 'dbo.Dummy' with unique index 'IX_Dummy_Text'. The duplicate key value is (Dummy).
At the end @@TRANCOUNT is 1
Run Code Online (Sandbox Code Playgroud)
发生错误但事务未回滚。这个设置的工作方式显然并不像文档让我相信的那么简单。为什么交易没有回滚?
这个答案提到,XACT_ABORT仅在错误严重性至少为 16 时才回滚事务。本示例中的错误仅为 14 级。但是,即使我将INSERT过程中的 替换为RAISERROR (N'Custom error', 16, 0),事务仍然不会回滚。
更新:我发现虽然在我的测试中事务没有回滚,但它是注定的!@@TRANCOUNT是1当我执行此示例时,无论设置如何XACT_ABORT:但如果设置是ON,XACT_STATE()则-1表示不可提交事务。什么时候XACT_ABORT是OFF,XACT_STATE()是1。
问题 “出现了错误,但事务没有回滚。这些设置的工作方式显然不像文档让我相信的那么简单。为什么事务没有回滚”
答案是 RAISERROR 不会导致 XACT_ABORT 触发!这意味着我们可能处于非常混乱的状态,事务明智 中止,中止,我们是 XACT_ABORT:ing,还是我们?!
根据 MSDN 的说法,
THROW 语句遵循 SET XACT_ABORT。RAISERROR 没有。新应用程序应使用 THROW 而不是 RAISERROR。
我们可以使用 THROW 语句来代替 RAISERROR。所以我们可以使用下面的语句来触发XACT_ABORT
TRUNCATE TABLE Dummy
GO
SET XACT_ABORT ON
BEGIN TRANSACTION
BEGIN TRY
EXEC dbo.InsertDummy @Text = N'Dummy'
EXEC dbo.InsertDummy @Text = N'Dummy' --DUPLICATE!
END TRY
BEGIN CATCH
THROW
END CATCH
PRINT 'At the end @@TRANCOUNT is ' + CONVERT(NVARCHAR, @@TRANCOUNT)
IF @@TRANCOUNT>0
ROLLBACK
Run Code Online (Sandbox Code Playgroud)
输出将是;
(1 row affected)
(0 rows affected)
Msg 2601, Level 14, State 1, Procedure dbo.InsertDummy, Line 7 [Batch Start Line 5]
Cannot insert duplicate key row in object 'dbo.Dummy' with unique index 'IX_Dummy_Text'. The duplicate key value is (Dummy).
Run Code Online (Sandbox Code Playgroud)
对于更新的问题,您可以看到set xact_abort on 和 try-catch 一起
| 归档时间: |
|
| 查看次数: |
1807 次 |
| 最近记录: |