使用多个保存点的正确方法是什么

What is the right way to use multiple savepoints

我是第一次使用交易,所以我可能会问一个愚蠢的问题。

我想在 3 个表中插入数据:

Table1(p1,p2,p3)
Table2(q1,q2)
Table3(t3,fk1,fk2)

例如,如果出现问题,数据无法插入Table2Table1的数据不会丢失,Table3保持不变(反之亦然)。

到目前为止,我已经尝试了两个版本,但 none 令人满意。

版本 1:

CREATE PROCEDURE InsertInto(@p1,@p2,@p3,@q1,@q2,@t3))
AS BEGIN

BEGIN TRAN
SET XACT_ABORT OFF

SAVE TRANSACTION point1
BEGIN TRY
    DECLARE @fk1 INT
    INSERT INTO Table1 VALUES (@p1,@p2,@p3)
    SELECT @fk1 = Table1.Id FROM Table1 WHERE Table1.p1 = @p1

    SAVE TRANSACTION point2
    BEGIN TRY
        DECLARE @fk2 INT
        INSERT INTO Table2 VALUES (@q1,@q2)
        SELECT @fk2 = Table2.Id FROM Table2 WHERE Table2.q1 = @q1

        SAVE TRANSACTION point3
        BEGIN TRY
            INSERT INTO Table3 VALUES (@t3, @fk1, @fk2)
            COMMIT TRAN
            END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION point3
            COMMIT TRAN
        END CATCH
        
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION point2
        COMMIT TRAN
    END CATCH

END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION point1
    COMMIT TRAN
END CATCH
END

但是如果无法在 Table1 中插入数据,那么 Table2 的可能数据就会丢失,我不想丢失任何东西。所以,我尝试拆分它。

版本 2:

CREATE PROCEDURE InsertInto(@p1,@p2,@p3,@q1,@q2,@t3)
AS
BEGIN

BEGIN TRAN
SET XACT_ABORT OFF

SAVE TRANSACTION point1
BEGIN TRY
    DECLARE @fk1 INT
    INSERT INTO Table1 VALUES (@p1,@p2,@p3)
    SELECT @fk1 = Table1.Id FROM Table1 WHERE Table1.p1 = @p1
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION point1
    COMMIT TRAN
END CATCH

SAVE TRANSACTION point2
BEGIN TRY
    DECLARE @fk2 INT
    INSERT INTO Table2 VALUES (@q1,@q2)
    SELECT @fk2 = Table2.Id FROM Table2 WHERE Table2.q1 = @q1
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION point2
    COMMIT TRAN
END CATCH

SAVE TRANSACTION point3
BEGIN TRY
    INSERT INTO Table3 VALUES (@t3,@fk1,@fk2)
    COMMIT TRAN
 END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION point3
    COMMIT TRAN
 END CATCH
END

但是如果 Insert into Table2 失败,我得到这个:

(1 row(s) affected)

(0 row(s) affected)
Msg 628, Level 16, State 0, Procedure InsertInto, Line 26 (second BEGIN CATCH)
Cannot issue SAVE TRANSACTION when there is no active transaction.

我该怎样做才对?

SAVE TRAN 要求事务计数 > 0,因此您必须在前一个 CATCH 块中提交事务。您有多种选择:

1) 将您的 SAVE TRAN 语句替换为以下内容(您可以使用相同的保存点名称,但是回滚将仅回滚到最后一个保存点):

IF @@TRANCOUNT = 0
    BEGIN TRAN;
ELSE
    SAVE TRAN tran1;

2) 在CATCH块中,在COMMIT TRAN之后添加BEGIN TRAN

BEGIN CATCH
    ROLLBACK TRANSACTION point1
    COMMIT TRAN
    BEGIN TRAN
END CATCH

3) 删除 CATCH 块中的所有 COMMIT TRAN,并在最后执行单个 COMMIT。