Сервер SQL: управление транзакциями (начать, сохранить, зафиксировать, откат) через несколько хранимых процедур

В T-Sql, rollback transaction Откат всех транзакций, кроме указанного имени точки сохранения. Для отката только части модификаций мы используем rollback transaction @save_point_name, Это save transaction @save_point_name должно быть указано ранее. Если указанная точка сохранения уже откатана (удалена из журнала транзакций), возникает ошибка. Аналогично, если нет активной транзакции, begin transaction @transaction_name необходимо указать и откатить таким же образом. Это позволяет быстро обнаруживать ошибки или использовать try...catch механизм.

В отличие от rollback, commit transaction @transaction_name игнорирует его @transaction_name часть полностью и просто выполняет коммит, либо уменьшая @@trancount или прекращение транзакции. Таким образом, невозможно узнать или указать, какая (вложенная) транзакция или, в этом отношении - точка сохранения, (псевдо) совершается. Я знаю, что транзакции изначально не предназначались для вложенности, поэтому существуют точки сохранения.

Типичный подход состоит в том, чтобы в каждой процедуре проверять @@trancount определить, следует ли создать точку сохранения или начать новую транзакцию. Затем позже подтвердите определение, проверьте состояние транзакции и подтвердите или откатите (или ничего не сделайте) соответственно.

Эта проверка очень важна, особенно если у вас много процедур, вызывающих (несколько) процедур, которые выполняют откат только своих собственных действий, если что-то пошло не так. Поэтому я попытался абстрагировать транзакцию, чтобы можно было просто написать что-то вроде этого.

create procedure RollBackOnlyMyActions
as
declare @SavePointName nvarchar(32) = N'RollBackToHere';
begin try
    exec CreateSavePoint @SavePointName;    

    --do stuff

    --call other procedures that may roll back only its actions

    --do other stuff

    exec CommitSavePoint @SavePointName;
end try
begin catch
    exec RollBackSavePoint @SavePointName;
end catch

где (избавь меня от распечаток)

create procedure dbo.CreateSavePoint
    @SavePointName nvarchar(32)
as
declare @Msg nvarchar(255);
print N'Preparing to create save point ['+@SavePointName+N'].';
print N'Checking not in an uncommitable transaction.';
if xact_state() != -1 
begin 
    print N'Not in an uncommitable transaction. Checking for transaction existence.';
    if @@TranCount = 0
    begin
        print N'No active transaction. Starting a new one.';
        begin transaction @SavePointName;
    end
    else
    begin
        print N'In active transaction. Saving transaction point.';
        save transaction @SavePointName;
    end
end
else
begin
    print N'In an uncommitable transaction. No use saving. Throwing exeption.';
    set @Msg = N'Uncommitable transaction.';
    throw 50000,@Msg,1;
end
go

create procedure dbo.CommitSavePoint
    @SavePointName nvarchar(32)
as
declare @Msg nvarchar(255);
print N'Preparing to commit save point ['+@SavePointName+N'].';
print N'Checking not in an uncommitable transaction.';
if xact_state() != -1
begin
    print N'Not in an uncommitable transaction. Checking transaction count.';
    if @@trancount > 1
    begin
        print N'In nested transaction of '+convert(nvarchar(255),@@trancount)+N'. Committing once.';
    end
    else if @@trancount = 1
    begin
        print N'In outter transaction. Committing.';        
    end
    else
    begin
        print N'No active transaction. Throw exception.';
        set @Msg = N'No transaction to commit.';
        throw 50000,@Msg,1;
    end
    commit transaction;
end
else
begin
    print N'In an uncommitable transaction. Cannot commit. Throwing exeption.';
    set @Msg = N'Uncommitable transaction.';
    throw 50000,@Msg,1;
end
go

create procedure dbo.RollbackSavePoint
    @SavePointName nvarchar(32)
as
declare @Msg nvarchar(255);
print N'Prepare to rollback savepoint ['+@SavePointName+N']';
print N'Checking not in an uncommitable transaction.';
if xact_state() != -1 
begin 
    print N'Not in an uncommitable transaction. Trying rollback';
    begin try
        rollback transaction @SavePointName;
    end try
    begin catch
        print N'Something went wrong. Rethrowing exception.';
        throw;
    end catch
end
else
begin
    print N'In an uncommitable transaction. No use rolling back. Throwing exeption.';
    set @Msg = N'Uncommitable transaction.';
    throw 50000,@Msg,1;
end
go

Ну, это не сработало. Как я получаю

Msg 266, Level 16, State 2, Procedure CreateSavePoint, Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.

после первого вызова CreateSavePoint. То есть, похоже, что серверу sql не нравится управлять транзакциями в нескольких процедурах.

Так. Есть ли обходной путь для этого, например, способ подавления этой ошибки? Или я здесь упускаю важную концепцию?

1 ответ

Проблема отката может быть решена с помощью SET XACT_ABORT ON, который является "автоматическим откатом" (просто) и подавляет ошибку 266.

Другие вопросы по тегам