2016-05-06 29 views
6

Sono uno sviluppatore C# che apprende più TSQL. Ho scritto uno script come questo:Una transazione richiede un tentativo di cattura?

begin transaction 
--Insert into several tables 
end transaction 

Ma mi è stato detto che non era una buona idea e usare qualcosa di simile:

BEGIN TRANSACTION; 

BEGIN TRY 
    -- Generate a constraint violation error. 
    DELETE FROM Production.Product 
    WHERE ProductID = 980; 
END TRY 
BEGIN CATCH 
    SELECT 
     ERROR_NUMBER() AS ErrorNumber 
     ,ERROR_SEVERITY() AS ErrorSeverity 
     ,ERROR_STATE() AS ErrorState 
     ,ERROR_PROCEDURE() AS ErrorProcedure 
     ,ERROR_LINE() AS ErrorLine 
     ,ERROR_MESSAGE() AS ErrorMessage; 

    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 
END CATCH; 

IF @@TRANCOUNT > 0 
    COMMIT TRANSACTION; 
GO 

non vedo il motivo per cui il secondo esempio è più corretto . Il primo non funzionerebbe allo stesso modo? Sembra che il primo aggiornerebbe tutti i tavoli o non lo farà affatto? Non vedo perché controllare lo @@TRANCOUNT sia necessario prima del commit.

+1

Vorrei fare lo stesso argomento che tu sei. Inoltre il pattern try/catch raccomandato è un anti-pattern che chiamo try/squelch. Cattura ed errore e quindi procede silenziosamente. Questo NON è gestire gli errori, li sta sopprimendo. Detto questo, un blocco try/catch non è richiesto per una transazione. Soprattutto se si è in un trigger, l'utilizzo di un try/catch probabilmente causerà molti più problemi di quanti ne risolverà mai. –

+2

Se qualcosa nel secondo esempio, il commit dovrebbe essere nel blocco try, non dopo il catch ... penso – Kritner

risposta

4

Aprire una transazione solo quando ci si trova all'interno del blocco di prova e appena prima dell'effettiva affermazione, e impegnarsi subito, non attendere che il controllo raggiunga la fine del batch per il commit delle transazioni.

Una volta che ci si trova in Try Block e si è aperta una transazione, Se qualcosa va storto Il controllo passerà al blocco CATCH, eseguirà semplicemente il rollback della transazione e eseguirà altre operazioni di gestione degli errori come richiesto.

Ho aggiunto un piccolo controllo prima di eseguire effettivamente il rollback del controllo della transazione per qualsiasi transazione aperta utilizzando la funzione @@ ROWCOUNT, in realtà non ha molta importanza in questo scenario. È più utile quando si eseguono alcuni controlli di validazione nel blocco try prima di aprire una transazione come controllare i valori param e altre cose e generare errori nel blocco try se uno dei controlli di validazione fallisce, In tal caso il controllo salterà per catturare il blocco senza nemmeno aprire una transazione, è possibile controllare qualsiasi transazione aperta e rollback se ce ne sono di aperti. Nel tuo caso così com'è, non hai davvero bisogno di verificare la presenza di nessuna transazione aperta, in quanto non entrerai nel blocco catch a meno che qualcosa non vada nella tua transazione.

BEGIN TRY 

    BEGIN TRANSACTION 
    -- Multiple Inserts 
    INSERT INTO.... 
    INSERT INTO.... 
    INSERT INTO.... 
COMMIT TRANSACTION 
    PRINT 'Rows inserted successfully...' 

END TRY 

BEGIN CATCH 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
     ROLLBACK TRANSACTION 
     PRINT 'Error detected, all changes reversed' 
    END 
    SELECT 
     ERROR_NUMBER() AS ErrorNumber, 
     ERROR_SEVERITY() AS ErrorSeverity, 
     ERROR_STATE() AS ErrorState, 
     ERROR_PROCEDURE() AS ErrorProcedure, 
     ERROR_LINE() AS ErrorLine, 
     ERROR_MESSAGE() AS ErrorMessage 
END CATCH 
+0

Penso che intendessi '@@ TranCount' nella tua spiegazione, non' @@ RowCount'. Esiterei anche a includere in un messaggio generale _ "tutte le modifiche invertite" _ poiché potrebbero esserci valori di identità "persi", effetti collaterali causati da trigger, .... – HABO

0

Sono in due a proposito di PROVA ... CATCH in T-SQL.

Mentre è un'aggiunta potenzialmente utile alla lingua, il fatto che sia disponibile non è sempre un motivo per usarlo.

Prendendo il vostro

DELETE FROM Table WHERE... 

esempio (che mi rendo conto che è solo un esempio). L'unico modo che fallirà sempre con un errore è se il codice è stato seriamente eliminato da whack dallo schema. (ad esempio, se qualcuno crea una chiave esterna con Table sul lato PK di esso).

Dato il test corretto, questo tipo di discrepanza tra il codice e lo schema non deve mai essere messo in produzione. Supponendo che lo faccia, IMHO il messaggio di errore "non valido", non mediato che risulterà, è una migliore indicazione di ciò che è andato storto di un involucro "educato" di esso in un'istruzione SELECT per tornare al client. (Il che può equivalere al try/squelch antipattern SeanLange menziona).

Per scenari più complicati, posso vedere un utilizzo per TRY ... CATCH. Anche se IMHO non può sostituire un'attenta validazione dei parametri di input.

1

voglio dare il mio punto di vista qui come uno sviluppatore C#:

Nel semplice scenario di cui sopra (basta inserire in un paio di tavoli da uno script), non v'è alcun motivo per aggiungere un try/catch, come non aggiunge alcun beneficio alla transazione. Entrambi gli esempi produrranno esattamente lo stesso risultato: o verranno inserite tutte le tabelle, altrimenti non lo saranno. Lo stato del database rimane coerente.(Poiché COMMIT TRANSACTION non viene mai chiamato, il rollback viene implicitamente chiamato da Sql Server alla fine dello script.)

Tuttavia, ci sono momenti in cui è possibile fare cose nel try/catch che non sono possibili con l'integrato gestione degli errori. Ad esempio, registrare l'errore in una tabella errori.

Nella mia esperienza C#, l'unica volta in cui utilizzare un Try/Catch è quando ci sono cose che sono al di fuori del controllo dello sviluppatore, come ad esempio il tentativo di aprire un file. In tal caso, l'unico modo per gestire un'eccezione generata dal framework .Net è tramite Try/Catch.

Se stavo facendo una stored procedure e volevo controllare manualmente lo stato dei dati e chiamare manualmente ROLLBACK TRANSACTION, ho potuto vederlo. Ma ancora non richiederebbe un tentativo/cattura.