Откат внутренней транзакции вложенных транзакций
Предположим, у меня есть следующая инструкция sql в SQL Server 2008:
BEGIN TRANSACTION
SqlStatement1
EXEC sp1
SqlStatement3
COMMIT TRANSACTION
Код sp1
BEGIN TRANSACTION
SqlStatement2
ROLLBACK TRANSACTION
Мой вопрос: Выполняется ли SqlStatement3
?
Ответы
Ответ 1
Вы можете использовать точки сохранения транзакций. sp1 может использовать шаблон, подобный описанному в Обработка ошибок и вложенные транзакции:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
end catch
end
Такой шаблон позволяет выполнить проделанную операцию sp1 для отката, но сохранить активную транзакцию.
Ответ 2
SQL Server не поддерживает вложенные транзакции. Одновременно выполняется только одна транзакция.
Эта транзакция имеет базовый вложенный счетчик транзакций, @@TRANCOUNT
. Каждый последовательный begin transaction
увеличивает счетчик на единицу, каждый commit transaction
уменьшает его на единицу. Только commit
, который уменьшает счетчик до 0, действительно совершает одну транзакцию.
A rollback transaction
отменяет одну транзакцию и очищает @@TRANCOUNT
.
В вашем случае смешным результатом является то, что SqlStatement3 запускается вне транзакции! Ваш последний commit
будет вызывать "Запрос COMMIT TRANSACTION не имеет соответствующего исключения BEGIN TRANSACTION", но эффекты SqlStatement3 являются постоянными.
Например:
create table #t (col1 int)
insert #t (col1) values (1)
BEGIN TRANSACTION
update #t set col1 = 2 -- This gets rolled back
BEGIN TRANSACTION
update #t set col1 = 3 -- This gets rolled back too
ROLLBACK TRANSACTION
update #t set col1 = 4 -- This is run OUTSIDE a transaction!
COMMIT TRANSACTION -- Throws error
select col1 from #t
Печать 4
. В самом деле.:)
Ответ 3
Rollback transaction
самостоятельно сворачивает все транзакции.
http://msdn.microsoft.com/en-us/library/ms181299(v=sql.100).aspx
Оператор все равно будет выполнен - попробуйте
create table #t (i int)
insert #t values (1) -- t contains (1)
begin tran
update #t set i = i +1
select * from #t -- t contains (2)
begin tran
update #t set i = i +1
select * from #t -- t contains (3)
rollback tran -- transaction is rolled back
select * from #t -- t contains (1)
update #t set i = i +1
select * from #t -- t contains (2)
commit -- error occurs
select * from #t -- t contains (2)
drop table #t
Ответ 4
Собственные внутренние транзакции игнорируются SQL Server Database Engine. Сделка либо совершена, либо отменена на основании действий, предпринятых в конце самой внешней транзакции. Если внешняя транзакция совершена, внутренние вложенные транзакции также совершаются. Если внешняя транзакция откатывается назад, то все внутренние транзакции также откатываются, независимо от того, были ли внутренние транзакции осуществлены индивидуально.
Операции вложения в Microsoft TechNet
Ответ 5
Вложенные транзакции могут использоваться. Чтобы откат только внутренней транзакции, используйте точку сохранения и откат к точке сохранения. В случае, если внутренняя транзакция не является вложенной или нет, операторы IF
могут использоваться, чтобы определить, следует ли устанавливать точку сохранения и откат или откат к точке сохранения:
BEGIN TRAN
DECLARE @WILL_BE_NESTED_TRANSACTION BIT = CASE WHEN (@@TRANCOUNT > 0) THEN 1 ELSE 0 END
IF @WILL_BE_NESTED_TRANSACTION = 1
SAVE TRAN tran_save
BEGIN TRAN
-- do stuff
IF @WILL_BE_NESTED_TRANSACTION = 1
ROLLBACK TRAN tran_save
ELSE
ROLLBACK
ROLLBACK