SQL Server: il rollback delle transazioni avviene in caso di errore?


193

Abbiamo un'app client che esegue SQL su un SQL Server 2005 come il seguente:

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

Viene inviato da un comando di stringa lunga.

Se uno degli inserimenti non riesce o una parte del comando non riesce, SQL Server ripristina la transazione? Se non esegue il rollback, devo inviare un secondo comando per ripristinarlo?

Sono in grado di fornire dettagli sull'API e sulla lingua che sto usando, ma penso che SQL Server dovrebbe rispondere allo stesso modo per qualsiasi lingua.


Risposte:


205

Puoi mettere set xact_abort onprima della tua transazione per assicurarti che sql torni automaticamente in caso di errore.


1
Funzionerà su MS SQL 2K e versioni successive? Questa sembra la soluzione più semplice.
Jonathanpeppers,

1
Appare nei documenti per il 2000, 2005 e 2008, quindi presumo di sì. Lo stiamo usando nel 2008.

8
Devo spegnerlo o è per sessione?
Marc

5
@Marc l'ambito di xact_abortè a livello di connessione.
Keith,

2
@AlexMcMillan L'istruzione DROP PROCEDURE modifica la struttura del database, a differenza di INSERT, che funziona solo con i dati. Quindi non può essere inserito in una transazione. Sto semplificando troppo, ma fondamentalmente è così.
eksortso,

195

È corretto in quanto verrà eseguito il rollback dell'intera transazione. È necessario emettere il comando per ripristinarlo.

Puoi avvolgerlo in un TRY CATCHblocco come segue

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
    RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH

2
Mi piace di più la soluzione di DyingCactus, la sua è una riga di codice da cambiare. Se il tuo se per qualche motivo migliore (o più affidabile) fammi sapere.
Jonathanpeppers,

14
Try catch ti dà la possibilità di catturare (e possibilmente correggere) l'errore e, se necessario, generare un messaggio di errore personalizzato.
Raj More,

11
"Cattura e registra" più frequentemente di "cattura e correggi", penso.
Quillbreaker,

24
La sintassi di RAISERROR non è corretta almeno in SQL Server 2008R2 e versioni successive. Vedere msdn.microsoft.com/en-us/library/ms178592.aspx per la sintassi corretta.
Eric J.

2
@BornToCode Per assicurarsi che la transazione esista. Supponiamo che tu abbia eseguito il rollback della transazione in una determinata condizione (nella try), ma dopo il codice fallisce. Non ci sono più transazioni, ma stai ancora andando in catch.
Gabriel GM,

42

Ecco il codice con il funzionamento del messaggio di errore con MSSQL Server 2016:

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

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

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

1
Ho dovuto usare DECLARE @Var TYPE; SET @Var = ERROR;per aumentare gli errori nel sql server 2005. Altrimenti il ​​codice sopra per aumentare gli errori funziona anche per i DB più vecchi. Cercare di assegnare un valore predefinito a una variabile locale è ciò che ha causato il problema.
jtlindsey,

Puoi usare un semplice PASSAGGIO; invece delle dichiarazioni RAISERROR e ERROR_ *.
Rodzmkii,

21

Dall'articolo MDSN, Controllo delle transazioni (Motore di database) .

Se si verifica un errore dell'istruzione di runtime (come una violazione del vincolo) in un batch, il comportamento predefinito nel Motore di database consiste nel ripristinare solo l'istruzione che ha generato l'errore. È possibile modificare questo comportamento utilizzando l'istruzione SET XACT_ABORT. Dopo l'esecuzione di SET XACT_ABORT ON, qualsiasi errore dell'istruzione di runtime provoca un rollback automatico della transazione corrente. Gli errori di compilazione, come gli errori di sintassi, non sono interessati da SET XACT_ABORT. Per ulteriori informazioni, vedere SET XACT_ABORT (Transact-SQL).

Nel tuo caso eseguirà il rollback della transazione completa quando uno degli inserimenti fallisce.


3
di cosa abbiamo bisogno per gestire gli errori di sintassi? o compilare errori? se qualcuno di loro accade, l'intera transazione dovrebbe essere annullata
MonsterMMORPG il

La cattura di errori di compilazione / sintassi è la finalità dei progetti SSDT. :-)
Joe the Coder

10

Se uno degli inserimenti non riesce o una parte del comando fallisce, il server SQL esegue il rollback della transazione?

No non lo fa.

Se non esegue il rollback, devo inviare un secondo comando per ripristinarlo?

Certo, dovresti emettere ROLLBACKinvece di COMMIT.

Se si desidera decidere se eseguire il commit o il rollback della transazione, è necessario rimuovere la COMMITfrase dall'estratto conto, controllare i risultati degli inserti e quindi emettere uno COMMITo in ROLLBACKbase ai risultati del controllo.


Quindi, se viene visualizzato un errore, dire "Conflitto chiave primaria", è necessario inviare una seconda chiamata al rollback? Immagino abbia senso. Cosa succede se si verifica un errore relativo alla rete, ad esempio la connessione viene interrotta durante un'istruzione SQL molto lunga?
Jonathanpeppers,

2
Quando una connessione scade, il protocollo di rete sottostante (es. Named PipesO TCP) interrompe la connessione. Quando una connessione viene interrotta, SQL Serverarresta tutti i comandi attualmente in esecuzione e ripristina la transazione.
Quassnoi,

1
Quindi la soluzione di DyingCactus sembra risolvere il mio problema, grazie per l'aiuto.
jonathanpeppers,

Se devi interrompere qualsiasi errore, quindi sì, questa è l'opzione migliore.
Quassnoi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.