Откат внутренней транзакции вложенных транзакций

Предположим, у меня есть следующая инструкция 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