Pergunta

Eu tenho um procedimento armazenado que necessita de definir um ponto de salvamento, de modo que ele pode, em determinadas circunstâncias, desfazer tudo que fez e retornar um código de erro para o chamador, ou aceitar/confirmar e retornar sucesso para o chamador.Mas eu preciso dele para trabalhar se o chamador já começou uma transação ou não.O doc é extremamente confuso sobre este assunto.Aqui está o que eu acho que vai funcionar, mas eu não estou certo de todas as ramificações.

A coisa é - este Stored Procedure (SP) é chamado por outros.Então, eu não sei se eles já iniciado uma transação ou não...Mesmo se eu exigir que os usuários iniciar uma transação de usar o meu de SP, eu ainda tiver dúvidas sobre o uso adequado de Save Points ...

Meus SP vai testar se uma transação está em andamento, e se não, comece com um BEGIN TRANSACTION.Se uma transação já está em progresso, em vez disso, criará um ponto de save com SAVE TRANSACTION MySavePointName, e salve o fato foi isso que eu fiz.

Então, se eu tenho que reverter as minhas alterações, se eu fiz uma BEGIN TRANSACTION mais cedo, então eu vou ROLLBACK TRANSACTION.Se eu fiz o save point, então eu vou ROLLBACK TRANSACTION MySavePointName.Este cenário parece um grande trabalho.

Aqui é onde eu fico um pouco confuso - se eu quiser manter o trabalho que eu fiz, se eu começar uma transação executarei COMMIT TRANSACTION.Mas se eu criei um save point?Eu tentei COMMIT TRANSACTION MySavePointName, mas, em seguida, o interlocutor tenta consolidar sua transação e recebe uma mensagem de erro:

A TRANSAÇÃO de confirmação de pedido tem não correspondente BEGIN TRANSACTION.

Então, eu estou querendo saber então um save point pode ser revertida (que funciona: ROLLBACK TRANSACTION MySavePointName NÃO vai reverter a chamada transação).Mas talvez nunca precisa "commit" ele?Ele simplesmente fica lá, no caso de você precisar reverter para ele, mas vai embora depois que o original transação é confirmada (ou revertida)?

Se não há um "melhor" caminho para o "ninho" de uma transação, por favor, lançar um pouco de luz também.Eu ainda não descobri como ninho com BEGIN TRANSACTION mas só de reversão ou de comprometer a minha transação interna.Parece ROLLBACK sempre vai voltar para o topo de transação, enquanto COMMIT simplesmente diminui @@trancount.

Foi útil?

Solução

Eu acredito que eu percebi tudo isso fora agora, então eu vou responder a minha própria pergunta...

Eu mesmo escrevi minhas conclusões se você quiser mais detalhes http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

Então, meus SP começa com algo como isso, para iniciar uma nova transação se não houver nenhum, mas usar um Save Point, se um já está em andamento:

DECLARE @startingTranCount int
SET @startingTranCount = @@TRANCOUNT

IF @startingTranCount > 0
    SAVE TRANSACTION mySavePointName
ELSE
    BEGIN TRANSACTION
-- …

Então, quando estiver pronto para aplicar as alterações, você só precisa comprometer-se começamos a transação nós mesmos:

IF @startingTranCount = 0
    COMMIT TRANSACTION

E, finalmente, para reverter apenas as alterações até agora:

-- Roll back changes...
IF @startingTranCount > 0
    ROLLBACK TRANSACTION MySavePointName
ELSE
    ROLLBACK TRANSACTION

Outras dicas

Estendendo-se Brian B resposta.

Isso garante que o ponto de salvar o nome é único e usa o novo TRY/CATCH/JOGAR funcionalidades do SQL Server 2012.

DECLARE @mark CHAR(32) = replace(newid(), '-', '');
DECLARE @trans INT = @@TRANCOUNT;

IF @trans = 0
    BEGIN TRANSACTION @mark;
ELSE
    SAVE TRANSACTION @mark;

BEGIN TRY
    -- do work here

    IF @trans = 0
        COMMIT TRANSACTION @mark;
END TRY
BEGIN CATCH
    IF xact_state() = 1 OR (@trans = 0 AND xact_state() <> 0) ROLLBACK TRANSACTION @mark;
    THROW;
END CATCH

Eu tenho usado este tipo de gerenciador de transações no meu Procedimentos Armazenados :

    CREATE PROCEDURE Ardi_Sample_Test  
        @InputCandidateID INT  
    AS  
        DECLARE @TranCounter INT;  
        SET @TranCounter = @@TRANCOUNT;  
        IF @TranCounter > 0  
            SAVE TRANSACTION ProcedureSave;  
        ELSE  
            BEGIN TRANSACTION;  
        BEGIN TRY  

            /*
            <Your Code>
            */

            IF @TranCounter = 0  
                COMMIT TRANSACTION;  
        END TRY  
        BEGIN CATCH  
            IF @TranCounter = 0  
                ROLLBACK TRANSACTION;  
            ELSE  
                IF XACT_STATE() <> -1  
                    ROLLBACK TRANSACTION ProcedureSave;  

            DECLARE @ErrorMessage NVARCHAR(4000);  
            DECLARE @ErrorSeverity INT;  
            DECLARE @ErrorState INT;  
            SELECT @ErrorMessage = ERROR_MESSAGE();  
            SELECT @ErrorSeverity = ERROR_SEVERITY();  
            SELECT @ErrorState = ERROR_STATE();  

            RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);  
        END CATCH  
    GO  
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top