Сервер 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.