2010-05-26 10 views
8

Ho uno script TSQL che esegue molti aggiustamenti della struttura del database ma non è sicuro lasciarlo passare solo quando qualcosa non funziona.Esci e ripristina tutto nello script sull'errore

di fare chiarezza:

  • utilizza MS SQL 2005
  • Non è una stored procedure, solo un file di script (sql)

Quello che ho è qualcosa nel seguente ordine

BEGIN TRANSACTION 
    ALTER Stuff 
    GO 

    CREATE New Stuff 
    GO 

    DROP Old Stuff 
    GO 
IF @@ERROR != 0 
    BEGIN 
    PRINT 'Errors Found ... Rolling back' 
    ROLLBACK TRANSACTION 
    RETURN 
    END 
ELSE 
    PRINT 'No Errors ... Committing changes' 
    COMMIT TRANSACTION 

solo per illustrare ciò con cui sto lavorando ... non posso andare in specifiche ora, il problema ...

Quando introduco un errore (per verificare se le cose vengono ripristinate), ottengo una dichiarazione che la ROLLBACK TRANSACTION non è stata in grado di trovare una corrispondente TRANSAZIONE INIZIALE. Questo mi porta a credere che qualcosa sia DAVVERO sbagliato e che la transazione sia già stata uccisa. quello che ho notato è che lo script non si è completamente chiuso per errore e quindi ho cercato di eseguire ogni istruzione dopo che si è verificato l'errore. (L'ho notato quando si presentavano nuovi tavoli quando non li aspettavo perché avrebbe dovuto eseguire il rollback)

risposta

7

Quando si verifica l'errore, la transazione viene automaticamente ripristinata e il batch corrente viene interrotto.

L'esecuzione continua tuttavia nel lotto successivo. Quindi tutte le cose nei batch dopo l'errore viene eseguito. Quindi, quando si verificano errori in seguito, si tenta di eseguire il rollback di una transazione già annullata.

Inoltre, per fermare l'intero script, non solo il batch corrente, è necessario utilizzare:

raiserror('Error description here', 20, -1) with log 

Vedi my answer here per i dettagli su quello.

Quindi è necessario verificare la presenza di @error dopo ogni partita, penso che qualcosa del genere dovrebbe funzionare:

BEGIN TRANSACTION 
GO 

ALTER Stuff 
GO 

if @@error != 0 raiserror('Script failed', 20, -1) with log 
GO 

CREATE New Stuff 
GO 

if @@error != 0 raiserror('Script failed', 20, -1) with log 
GO 

DROP Old Stuff 
GO 

if @@error != 0 raiserror('Script failed', 20, -1) with log 
GO 

PRINT 'No Errors ... Committing changes' 
COMMIT TRANSACTION 
+1

questo è fondamentalmente quello che ho usato, ma nella forma seguente: se @@ errore <> 0 o @@ TRANCOUNT = 0 iniziare \t \t se @@ TRANCOUNT> 0 rollback della transazione \t \t set noexec su \t fine –

+1

'on' noexec set è un trucco, devono ricordare che. – Blorgbeard

+0

Funziona con transazioni a livello di server come 'DROP LOGIN'? Voglio cambiare il dominio a cui appartiene un login e sto rilasciandolo da tutti i DB, ma ho bisogno di ricrearlo assicurandomi che NULLA venga eliminato se non OGNI transazione ha successo con successo. Ciò comporterebbe autorizzazioni non mappate, ecc. E sarebbe una catastrofe. – Chiramisu

0

si potrebbe provare qualcosa di simile ... Se si utilizza blocco Try ... L'errore il livello 16, (o la maggior parte di errore dell'applicazione), trasferisce immediatamente il controllo al blocco CATCH senza eseguire ulteriori dichiarazioni nel blocco try ...

Begin Transaction 

Begin Try 

        -- Do your Stuff 

     If (@@RowCount <> 1) -- Error condition 
     Begin 
      Raiserror('Error Message',16,1) 
     End 


    Commit 
End Try 
Begin Catch 
    IF @@Trancount > 0 
    begin 
     Rollback Transaction 
    End 

    Declare @ErrMsg varchar(4000), @Errseverity int 

    SELECT @ErrMsg = ERROR_MESSAGE(), 
      @ErrSeverity = ERROR_SEVERITY() 

    RAISERROR(@ErrMsg, @ErrSeverity, 1)  
End Catch 

Spero che questo aiuti ...

+1

Non è possibile avere istruzioni GO in un 'TRY-CATCH': http://msdn.microsoft.com/en-us/library/ms179296.aspx – Blorgbeard

3

Non ho usato la soluzione raiseerror, perché non è riuscita in quanto non disponevo delle autorizzazioni di amministratore. Ho esteso il noexec on/off soluzione che gestisce la transazione come segue:

set noexec off 

begin transaction 
go 

<First batch, do something here> 
go 
if @@error != 0 set noexec on; 

<Second batch, do something here> 
go 
if @@error != 0 set noexec on; 

<... etc> 

declare @finished bit; 
set @finished = 1; 

SET noexec off; 

IF @finished = 1 
BEGIN 
    PRINT 'Committing changes' 
    COMMIT TRANSACTION 
END 
ELSE 
BEGIN 
    PRINT 'Errors occured. Rolling back changes' 
    ROLLBACK TRANSACTION 
END 

A quanto pare il compilatore "capisce" la variabile @finished nel SE, anche se c'è stato un errore e l'esecuzione è stato disattivato. Tuttavia, il valore è impostato su 1 solo se l'esecuzione non è stata disabilitata. Quindi posso tranquillamente commettere o eseguire il rollback della transazione di conseguenza.

4

Provare a utilizzare RETURN. questo uscirà immediatamente dallo script o dalla procedura e non eseguirà nessuna delle seguenti dichiarazioni.È possibile utilizzare questo in combinazione con BEGIN, COMMIT ROLLBACK e dichiarazioni TRANSAZIONE per annullare eventuali danni dati:

BEGIN 
    BEGIN TRANSACTION 

    <first batch> 
    IF @@error <> 0 
     begin 
     RAISERROR ('first batch failed',16,-1) 
     ROLLBACK TRANSACTION 
     RETURN 
     end 

    <second batch> 
    IF @@error <> 0 
     begin 
     RAISERROR ('second batch failed',16,-1) 
     ROLLBACK TRANSACTION 
     RETURN 
     end 

    PRINT 'WIN!' 
    COMMIT TRANSACTION 
    END 
0
SET XACT_ABORT ON 
BEGIN TRAN 

-- Batch 1 

GO 

if @@TRANCOUNT = 0 
SET NOEXEC ON; 
GO 

-- Batch 2 

GO 

if @@TRANCOUNT = 0 
SET NOEXEC ON; 
GO 

-- Batch 3 

GO 

if @@TRANCOUNT > 0 
COMMIT 
GO