2012-03-15 18 views
12

Ho una procedura memorizzata che deve impostare un punto di salvataggio in modo che, in determinate circostanze, possa annullare tutto ciò che ha fatto e restituire un codice di errore al chiamante , o accettarlo/confermarlo e restituire il successo al chiamante. Ma ho bisogno che funzioni se il chiamante ha già avviato una transazione o meno. Il documento è estremamente confuso su questo argomento. Ecco quello che penso funzionerà, ma non sono certo di tutte le implicazioni.SAVE TRANSACTION vs BEGIN TRANSACTION (SQL Server) come nidificare correttamente le transazioni

Il fatto è che questo Stored Procedure (SP) viene chiamato da altri. Quindi non so se hanno avviato una transazione o no ... Anche se richiedo agli utenti di avviare una transazione per utilizzare il mio SP, ho ancora domande sull'uso corretto di Save Points ...

My SP verificherà se una transazione è in corso e, in caso contrario, ne inizi una con BEGIN TRANSACTION. Se una transazione è già in corso, creerà invece un punto di salvataggio con SAVE TRANSACTION MySavePointName e salverà il fatto che questo è ciò che ho fatto.

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

Ecco dove mi sento un po 'confuso - se voglio mantenere il lavoro che ho fatto, se avviò una transazione eseguirò COMMIT TRANSACTION. Ma se ho creato un punto di salvataggio? Ho provato COMMIT TRANSACTION MySavePointName, ma poi il chiamante cerca di impegnare il proprio transazione e ottiene un errore:

The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

Quindi mi chiedo allora - un punto di salvataggio possibile eseguire il rollback (che funziona: ROLLBACK TRANSACTION MySavePointName non rotolerà il chiamante del transazione). Ma forse non è mai necessario "commetterlo"? Rimane solo lì, nel caso in cui è necessario eseguire il rollback, ma si allontana una volta che la transazione originale è stata commessa (o ripristinata)?

Se c'è un modo "migliore" per "annidare" una transazione, per favore getta luce anche tu. Non ho capito come nidificare con BEGIN TRANSACTION ma solo il rollback o il commit della mia transazione interna. Sembra che ROLLBACK ricominci sempre alla transazione principale, mentre COMMIT diminuisce semplicemente @@trancount.

+0

vostro ritrovamento potrebbe valere la pubblicazione come una risposta. –

+0

@Andriy Ok, questo è quello che ho fatto: rimosso la mia modifica e usata come risposta. Grazie. –

risposta

19

Credo di aver capito tutto questo fuori ora, quindi mi rispondere alla mia domanda ...

Ho anche bloggato miei risultati se volete maggiori dettagli a 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 di simile, per iniziare una nuova transazione se non c'è nessuno, ma utilizzare un punto di salvataggio se uno è già in corso:

DECLARE @startingTranCount int 
SET @startingTranCount = @@TRANCOUNT 

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

Poi, quando si è pronti a commettere le modifiche, è solo bisogno di impegnarsi, se abbiamo avviato la transazione da soli:

IF @startingTranCount = 0 
    COMMIT TRANSACTION 

E, infine, di ripristinare solo le modifiche finora:

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

alcun motivo per non iniziare una nuova operazione nidificata dopo il salvataggio della transazione? – sotn

+0

Di seguito è riportato un collegamento a un articolo sulle transazioni di nidificazione. Se viene eseguito il commit di una transazione nidificata, riduce il conteggio delle transazioni nidificate ma NON esegue alcun commit. Quindi, se la transazione esterna è stata commessa, è tutto, se viene eseguito il rollback, tutto viene ripristinato, incluso quello "impegnato". Se la transazione nidificata esegue un rollback, esegue il rollback di ENTRAMBI stesso E delle transazioni esterne. Se desideri eseguire il rollback della tua transazione senza influire sulle transazioni in cui ti trovi già, utilizza la tecnica descritta qui. –

+0

Ecco l'articolo sulle transazioni nidificate: https://technet.microsoft.com/en-us/library/ms189336%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396 –

10

Estensione Brian B's answer.

Ciò garantisce che il nome del punto di salvataggio sia univoco e utilizza 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 
+0

Non ero tornato a questo post per un po ', ma adoro il template che puoi riutilizzare letteralmente nella prossima query. Bel miglioramento. –

+0

Ho visto sui suggerimenti MSDN per inserire l'istruzione di transazione iniziale nel blocco try. Fare questo in questo modello causa alcuni problemi o è del tutto sicuro farlo? –

+0

Sono confuso sull'istruzione if nel blocco catch. Perché ripristinare la transazione se xact stato = -1. Non dovrebbe essere <> - 1? –

2

Ho usato questo tipo di gestore delle transazioni nei miei 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 
Problemi correlati