2014-04-14 17 views
12

Sto scrivendo uno script che cancellerà i record da un certo numero di tabelle, ma prima che lo cancellino deve restituire un conteggio che un utente deve confermare prima di impegnarsi.TSQL Try/Catch in Transaction o viceversa?

Questo è un riepilogo della sceneggiatura.

BEGIN TRANSACTION SCHEDULEDELETE 
    BEGIN TRY 
     DELETE -- delete commands full SQL cut out 
     DELETE -- delete commands full SQL cut out 
     DELETE -- delete commands full SQL cut out 
     PRINT 'X rows deleted. Please commit or rollback.' --calculation cut out. 
    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 

      ROLLBACK TRANSACTION SCHEDULEDELETE 
      PRINT 'Error detected, all changes reversed.' 
    END CATCH 

--COMMIT TRANSACTION SCHEDULEDELETE --Run this if count correct. 

--ROLLBACK TRANSACTION SCHEDULEDELETE --Run this if there is any doubt whatsoever. 

Questa transazione è la mia prima volta che scrivo, è corretto/pratica migliore per avere il blocco try/catch all'interno della transazione o dovrebbe essere la transazione all'interno del blocco try?

Il fattore importante in questo script è che l'utente deve eseguire manualmente il commit della transazione.

+1

Yup al di fuori del blocco try/catch. – cyan

+1

Non attendere mai che un utente finale esegua il commit della transazione, a meno che non si tratti di un database in modalità utente singolo. – dean

+0

@dean C'è un problema con questo? Fondamentalmente, verrà detto all'utente che dovrebbero aspettarsi il numero X di record cancellati e di impegnarsi subito se il numero corrisponde. Queste tabelle non verranno scritte mentre la transazione è in esecuzione (anche se altre tabelle saranno). – Devasta

risposta

26

Aprire una transazione solo quando ci si trova all'interno del blocco TRY e subito prima dell'effettiva affermazione e impegnarsi subito. Non aspettare che il tuo controllo arrivi alla fine del batch per eseguire il commit delle tue transazioni.

Se qualcosa va storto mentre ci si trova nel blocco TRY e si è aperta una transazione, il controllo passerà al blocco CATCH. È sufficiente eseguire il rollback della transazione e eseguire altre operazioni di gestione degli errori come richiesto.

Ho aggiunto un piccolo controllo per qualsiasi transazione aperta utilizzando la funzione @@TRANCOUNT prima di eseguire effettivamente il rollback della transazione. Non ha molto senso in questo scenario. È più utile quando si eseguono alcuni controlli di convalida 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 convalida non riesce. In tal caso, il controllo passerà al blocco CATCH senza nemmeno aprire una transazione. Qui puoi controllare eventuali transazioni aperte e il rollback se ce ne sono di aperti. Nel tuo caso, non hai davvero bisogno di verificare la presenza di alcuna transazione aperta in quanto non inserirai il blocco CATCH a meno che qualcosa non vada a finire nella tua transazione.

Non chiedere dopo aver eseguito l'operazione DELETE se è necessario eseguire il commit o il rollback; fare tutte queste convalide prima di aprire la transazione. Una volta aperta una transazione, commettila subito e in caso di errori, gestisci gli errori (stai facendo un buon lavoro ottenendo informazioni dettagliate usando quasi tutte le funzioni di errore).

BEGIN TRY 

    BEGIN TRANSACTION SCHEDULEDELETE 
    DELETE -- delete commands full SQL cut out 
    DELETE -- delete commands full SQL cut out 
    DELETE -- delete commands full SQL cut out 
COMMIT TRANSACTION SCHEDULEDELETE 
    PRINT 'X rows deleted. Operation Successful Tara.' --calculation cut out. 
END TRY 

BEGIN CATCH 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
     ROLLBACK TRANSACTION SCHEDULEDELETE 
     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 
2

Non attendere mai che un utente finale esegua il commit della transazione, a meno che non si tratti di un database in modalità utente singolo.

In breve, si tratta di bloccare. La transazione richiederà alcuni blocchi esclusivi sulle risorse in fase di aggiornamento e rimarrà bloccata fino alla chiusura della transazione (commit o rollback). Nessuno sarà in grado di toccare quelle file. Ci sono alcuni problemi diversi se l'isolamento dello snapshot viene utilizzato con la pulizia dell'archivio delle versioni.

Meglio emettere prima una query di selezione per determinare un numero di righe qualificanti, presentarlo all'utente finale e dopo conferma confermare l'eliminazione effettiva.

0

Oltre al buon consiglio di M.Ali e Dean sopra, un po 'di aiuto per coloro che vogliono utilizzare il nuovo (er) Prova CATTURA LANCIO paradigma in SQL SERVER:

(non ho potuto facilmente trovare la sintassi completa, in modo da aggiungere qui)

GIST: HERE

Esempio codice di stored procedure qui (dal mio GIST):

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 

CREATE PROC [dbo].[pr_ins_test] 
@CompanyID INT 
AS 

SET NOCOUNT ON 

BEGIN 

    DECLARE @PreviousConfigID INT 

    BEGIN TRY 
     BEGIN TRANSACTION MYTRAN; -- Give the transaction a name 
     SELECT 1/0 -- Generates divide by zero error causing control to jump into catch 

     PRINT '>> COMMITING' 
     COMMIT TRANSACTION MYTRAN; 
    END TRY 
    BEGIN CATCH 
     IF @@TRANCOUNT > 0 
     BEGIN 
      PRINT '>> ROLLING BACK' 
      ROLLBACK TRANSACTION MYTRAN; -- The semi-colon is required (at least in SQL 2012) 

      THROW 
     END 
    END CATCH 
END