事务不会回滚所有更改

时间:2019-01-03 11:10:03

标签: sql-server transactions try-catch rollback

我遇到了一个SQL Server 2017中的过程,该过程在try-catch块中具有事务。它不是嵌套的,只是使用游标填充并循环了一个身份表。因此try-catch在一个循环中,因此调用了其他一些过程。有时,该过程因约束违规错误而失败,并且在内部异常之前保存成功的所有内容是完全可以的。然后我碰到了catch子句中的commit。这让我想知道,我编写了这段代码:

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块中提交或回滚事务。我始终确信,所有内容都会回滚,并且@Output@Table表将相等。...

我在这里做错了什么?我这是默认的交易行为吗?

2 个答案:

答案 0 :(得分:1)

这是一个有趣的过程,但是您的代码可以实现我所期望的。表变量不遵循事务语义。临时表呢!因此,如果您需要将突变回滚到临时“事物”的能力,请使用表而不是变量。

请注意,尽管您的序列仍然会从中提取值。即使您也将其放入交易中。

答案 1 :(得分:0)

正如Ben Thul所提醒的,此处仅应使用临时表或普通表。因此,当捕获到异常并XACT_STATE() = 1(可交易)时,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

输出表结果:
ROLLBACK:[1,2,3]
COMMIT:[1,1,2,NULL,3]