Domanda

Ho una procedura memorizzata che deve impostare un punto di salvataggio in modo che possa, in determinate circostanze, annullare tutto ciò che ha fatto e restituire un codice di errore al chiamante, oppure accettarlo/impegnarlo e restituire l'esito positivo al chiamante.Ma ho bisogno che funzioni indipendentemente dal fatto che il chiamante abbia già avviato una transazione o meno.Il documento è estremamente confuso su questo argomento.Ecco cosa penso che funzionerà, ma non sono sicuro di tutte le ramificazioni.

Il fatto è... questo Stored Procedure (SP) viene chiamato dagli altri.Quindi non so se hanno avviato una transazione oppure no...Anche se richiedo agli utenti di avviare una transazione per utilizzare il mio SP, ho ancora domande sul corretto utilizzo dello stesso Save Points ...

Il mio SP verificherà se una transazione è in corso e, in caso negativo, ne avvierà una BEGIN TRANSACTION.Se una transazione è già in corso, verrà invece creato un punto di salvataggio con SAVE TRANSACTION MySavePointName, e salvo il fatto che questo è quello che ho fatto.

Quindi, se devo ripristinare le modifiche apportate, se ho fatto un file BEGIN TRANSACTION prima, allora lo farò ROLLBACK TRANSACTION.Se ho fatto il punto di salvataggio, lo farò ROLLBACK TRANSACTION MySavePointName.Questo scenario sembra funzionare alla grande.

Qui è dove mi sento un po' confuso: se voglio mantenere il lavoro che ho svolto, se ho avviato una transazione la eseguirò COMMIT TRANSACTION.Ma se creassi un punto di salvataggio?ho provato COMMIT TRANSACTION MySavePointName, ma poi il chiamante tenta di confermare la transazione e riceve un errore:

La richiesta COMMIT TRANSACTION non ha BEGIN TRANSACTION corrispondente.

Quindi mi chiedo: è possibile ripristinare un punto di salvataggio (funziona: ROLLBACK TRANSACTION MySavePointName NON ripristinerà la transazione del chiamante).Ma forse non è mai necessario “impegnarselo”?Rimane lì, nel caso in cui sia necessario ripristinarlo, ma scompare una volta eseguita (o ripristinata) la transazione originale?

Se esiste un modo "migliore" per "nidificare" una transazione, si prega di fare luce anche su questo.Non ho capito come nidificare BEGIN TRANSACTION ma solo eseguire il rollback o il commit della mia transazione interna.Sembra ROLLBACK tornerà sempre alla transazione principale, mentre COMMIT semplicemente diminuisce @@trancount.

È stato utile?

Soluzione

Credo di aver capito tutto ora, quindi risponderò alla mia domanda...

Ho anche pubblicato sul blog le mie scoperte, se desideri maggiori dettagli, all'indirizzo http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

Quindi il mio SP inizia con qualcosa del genere, per iniziare una nuova transazione se non ce n'è, ma usa un punto di salvataggio se ne è già in corso:

DECLARE @startingTranCount int
SET @startingTranCount = @@TRANCOUNT

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

Quindi, quando sei pronto per confermare le modifiche, devi confermare solo se abbiamo avviato noi stessi la transazione:

IF @startingTranCount = 0
    COMMIT TRANSACTION

E infine, per ripristinare solo le modifiche apportate finora:

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

Altri suggerimenti

Estendendo La risposta di Brian B.

Ciò garantisce che il nome del punto di salvataggio sia univoco e utilizzi le nuove funzionalità TRY/CATCH/THROW di 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

Ho utilizzato questo tipo di gestore delle transazioni nelle mie Stored Procedure:

    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  
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top