事务不回滚所有更改
Transaction does not rollback all changes
我已经 运行 进入 SQL Server 2017 中的一个过程,该过程在 try-catch
块中有一个 t运行 动作。它不是嵌套的,只是使用游标填充和循环了一个标识 table。所以 try-catch
在一个循环中,调用了其他一些过程。有时该过程会因违反约束错误而失败,并且在内部异常之前保存任何成功的东西是完全可以的。然后我遇到了 catch 子句中的提交。这让我想知道,我写了这段代码:
DECLARE @Table TABLE (ID INT NOT NULL PRIMARY KEY)
DECLARE @Input TABLE (ID INT)
INSERT INTO @Input
VALUES (1), (1), (2), (NULL), (3)
DECLARE @Output TABLE (ID INT)
--SET XACT_ABORT OFF
DECLARE @ID int
DECLARE [Sequence] CURSOR LOCAL FAST_FORWARD FOR
SELECT ID FROM @Input
OPEN [Sequence]
FETCH NEXT FROM [Sequence] INTO @ID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
BEGIN TRAN
DECLARE @Msg nvarchar(max) = 'Inserting ''' + TRY_CAST(@ID as varchar(11)) + ''''
RAISERROR (@Msg, 0, 0) WITH NOWAIT
-- Order is important
--INSERT INTO @Table VALUES (@ID)
INSERT INTO @Output VALUES (@ID)
INSERT INTO @Table VALUES (@ID)
COMMIT TRAN
END TRY
BEGIN CATCH
SET @Msg = 'Caught ' + CAST(ERROR_NUMBER() as varchar(11)) + ' : ' + ERROR_MESSAGE()
RAISERROR (@Msg, 1, 1) WITH NOWAIT
IF XACT_STATE() = -1
BEGIN
SET @Msg = 'Uncommitable transaction [-1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
ROLLBACK TRAN
END
IF XACT_STATE() = 1
BEGIN
SET @Msg = 'Commitable transaction [1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
COMMIT TRAN
END
END CATCH
FETCH NEXT FROM [Sequence] INTO @ID
END
SELECT * FROM @Table
SELECT * FROM @Output
因此,当我尝试交换 @Output
和 @Table
插入的顺序时,无论 XACT_ABORT
是什么,我都得到了 不同的 结果设置为或者我是否在 catch 块中提交或回滚 t运行saction。我一直确信,一切都会回滚,@Output
和 @Table
tables 都将相等....
我在这里做错了什么?这是默认的 t运行saction 行为吗?
这很有趣,但您的代码符合我的预期。 Table 变量不遵守事务语义。临时 tables 做不过!因此,如果您需要能够将突变回滚到临时 "thing",请使用 table 而不是变量。
请注意,您的序列仍会从中提取值。即使它你也把它放在交易中。
正如提醒的那样,这里只能使用临时的或正常的table。因此,当捕获到异常并且 XACT_STATE() = 1
(Commitable 事务)时,COMMIT
将保留成功的任何内容,而 ROLLBACK
将撤消整个操作。
IF XACT_STATE() = 1
BEGIN
SET @Msg = 'Commitable transaction [1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
COMMIT TRAN -- Keep changes or undo everything (ROLLBACK)
END
输出table结果:
ROLLBACK
: [1,2,3]
COMMIT
: [1,1,2,NULL,3]
我已经 运行 进入 SQL Server 2017 中的一个过程,该过程在 try-catch
块中有一个 t运行 动作。它不是嵌套的,只是使用游标填充和循环了一个标识 table。所以 try-catch
在一个循环中,调用了其他一些过程。有时该过程会因违反约束错误而失败,并且在内部异常之前保存任何成功的东西是完全可以的。然后我遇到了 catch 子句中的提交。这让我想知道,我写了这段代码:
DECLARE @Table TABLE (ID INT NOT NULL PRIMARY KEY)
DECLARE @Input TABLE (ID INT)
INSERT INTO @Input
VALUES (1), (1), (2), (NULL), (3)
DECLARE @Output TABLE (ID INT)
--SET XACT_ABORT OFF
DECLARE @ID int
DECLARE [Sequence] CURSOR LOCAL FAST_FORWARD FOR
SELECT ID FROM @Input
OPEN [Sequence]
FETCH NEXT FROM [Sequence] INTO @ID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
BEGIN TRAN
DECLARE @Msg nvarchar(max) = 'Inserting ''' + TRY_CAST(@ID as varchar(11)) + ''''
RAISERROR (@Msg, 0, 0) WITH NOWAIT
-- Order is important
--INSERT INTO @Table VALUES (@ID)
INSERT INTO @Output VALUES (@ID)
INSERT INTO @Table VALUES (@ID)
COMMIT TRAN
END TRY
BEGIN CATCH
SET @Msg = 'Caught ' + CAST(ERROR_NUMBER() as varchar(11)) + ' : ' + ERROR_MESSAGE()
RAISERROR (@Msg, 1, 1) WITH NOWAIT
IF XACT_STATE() = -1
BEGIN
SET @Msg = 'Uncommitable transaction [-1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
ROLLBACK TRAN
END
IF XACT_STATE() = 1
BEGIN
SET @Msg = 'Commitable transaction [1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
COMMIT TRAN
END
END CATCH
FETCH NEXT FROM [Sequence] INTO @ID
END
SELECT * FROM @Table
SELECT * FROM @Output
因此,当我尝试交换 @Output
和 @Table
插入的顺序时,无论 XACT_ABORT
是什么,我都得到了 不同的 结果设置为或者我是否在 catch 块中提交或回滚 t运行saction。我一直确信,一切都会回滚,@Output
和 @Table
tables 都将相等....
我在这里做错了什么?这是默认的 t运行saction 行为吗?
这很有趣,但您的代码符合我的预期。 Table 变量不遵守事务语义。临时 tables 做不过!因此,如果您需要能够将突变回滚到临时 "thing",请使用 table 而不是变量。
请注意,您的序列仍会从中提取值。即使它你也把它放在交易中。
正如XACT_STATE() = 1
(Commitable 事务)时,COMMIT
将保留成功的任何内容,而 ROLLBACK
将撤消整个操作。
IF XACT_STATE() = 1
BEGIN
SET @Msg = 'Commitable transaction [1]'
RAISERROR (@Msg, 1, 1) WITH NOWAIT
COMMIT TRAN -- Keep changes or undo everything (ROLLBACK)
END
输出table结果:
ROLLBACK
: [1,2,3]
COMMIT
: [1,1,2,NULL,3]