Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Il costrutto Try Catch in T-SQL

Gestire gli errori in Transact SQL con il noto costrutto Try Catch
Gestire gli errori in Transact SQL con il noto costrutto Try Catch
Link copiato negli appunti

Tra le nuove funzionalità di programmazione T-SQL in SQL Server 2005 spicca la cattura degli errori tramite il blocco Try Catch. Questo costrutto è ben noto agli sviluppatori java, c#, c++, in quanto ricalca il pattern tipo utilizzato in questi linguaggi per intercettare le eccezioni sollevate nelle routines.

Microsoft ha comunque potenziato il classico Try/Catch in SQL Server 2005 aggiungendo un nutirito gruppo di funzioni di sistema in grado restituire codici e livelli severità degli errori T-SQL intercettati. In questo articolo andremo ad esporre modalità e limiti di utilizzo di Try/Catch.

Sintassi di Try/Catch

Usare il costrutto Try/Catch all'interno di uno script T-SQL è semplicissimo. Ci sono due blocchi di codice, il "blocco Try" con le istruzioni T-SQL che devono essere eseguite dalla procedura e il "blocco Catch" con le istruzioni T-SQL che da eseguire nel caso si verifichino errori nel blocco precedente.

Un blocco Try inizia sempre con l'istruzione BEGIN TRY e termina con l'istruzione END TRY, idem per il blocco Catch (BEGIN CATCH, END CATCH). Ad ogni blocco Try corrisponde un blocco Catch ed ogni blocco Try può contenere una o più istruzioni T-SQL.

Listato 1. Sintassi di Try Catch

BEGIN TRY
  { sql_statement | statement_block }
END TRY
BEGIN CATCH
  { sql_statement | statement_block }
END CATCH

Quando si verifica un errore nel blocco Try il controllo del codice viene passato al blocco Catch all'interno del quale è codificata la logica di gestione dell'errore (loggin degli errori, rollback di transazioni, invio di messaggi agli utenti, ecc...). Al contrario, quando non si verificano errori, il controllo passa direttamente all'istruzione End Catch associata.

Le istruzioni all'interno di un blocco Catch vengono sempre eseguite a meno che si faccia esplicitamente ricorso a un'istruzione che cambi il flusso di esecuzione come RETURN o GOTO.

Listato 2. Esempio di blocco Try/Catch in azione

CREATE TABLE test (id int IDENTITY(1,1) PRIMARY KEY, data NVARCHAR(64) not null);
BEGIN TRY
  INSERT INTO test (id,data) VALUES (1,'test');
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER() as ErrorNumber, ERROR_MESSAGE() as ErrorMessage;
END CATCH

L'istruzione INSERT genera un errore perchè forza l'inserimento di un valore all'interno di una colonna identity. L'errore sollevato viene catturato dal blocco Try e passato al blocco Catch. Quest'ultimo formatta un messaggio di errore utilizzando un'istruzione SELECT e le funzioni ERROR_NUMBER() e ERROR_MESSAGE().

L'output restituito da ERROR_MESSAGE

Quando IDENTITY_INSERT è OFF non è possibile inserire 
un valore esplicito per la colonna Identity nella tabella 'test'

Il codice ERROR_NUMBER() restituito è 544.

Recuperare informazioni sugli errori

Nell'ambito di un blocco CACTH SQL Server permette di utilizzare delle funzioni di sistema in grado di recuperare informazioni sullo stato e sulla natura degli errori, ecco un elenco:

Funzione Descrizione
ERROR_NUMBER() restituisce il numero dell'errore
ERROR_SEVERITY() restituisce la gravità dell'errore
ERROR_STATE() restituisce il numero di contesto dell'errore
ERROR_PROCEDURE() restituisce il nome della stored procedure o del trigger in cui si è verificato l'errore
ERROR_LINE() restituisce il numero di riga all'interno della routine che ha causato l'errore
ERROR_MESSAGE() restituisce il contenuto del messaggio di errore

Queste funzioni restituiscono un valore NULL se vengono chiamate all'esterno di un blocco Try/Cacth e sono utilizzabili all'interno del blocco Catch, o in una stored procedure richiamata dal suo interno.

Utilizzando queste funzioni all'interno di una stored procedure e richiamando la stessa da un blocco Catch è possibile centralizzare la raccolta degli errori nelle applicazioni.

Listato 3. Centralizzare la raccolta degli errori di una applicazione

-- Creazione della procedura
CREATE PROCEDURE prcCatchErrors
AS
SELECT
  ERROR_NUMBER() AS ErrorNumber,
  ERROR_SEVERITY() AS ErrorSeverity,
  ERROR_STATE() as ErrorState,
  ERROR_LINE () as ErrorLine,
  ERROR_PROCEDURE() as ErrorProcedure,
  ERROR_MESSAGE() as ErrorMessage;
GO
-- Uso la procedura in un blocco CACTH
BEGIN TRY
  ...
END TRY
BEGIN CATCH
  exec dbo.prcCatchErrors
END CATCH

Try/Catch e le transazioni

Per controllare lo stato delle transazioni esplicite all'interno del blocco Try/Catch è possibile usare due funzioni: @@TRANCOUNT e XACT_STATE().

La prima funzione (già presente in SQL Server 2000) restituisce un numero intero che rappresenta il numero di transazioni aperte nella sessione, mentre la seconda (introdotta in SQL Server 2005) restituisce un range di tre valori (1,-1,0) in grado di identificare lo stato delle transazioni attualmente in corso.

Interrogando il valore restituito da XACT_STATE() all'interno di un blocco Try/Catch possiamo sapere se fare o no il COMMIT (o il ROLLBACK) di una transazione. I valori di ritorno della funzione XACT_STATE() hanno il seguente significato:

Funzione Descrizione
1 se è attiva una transazione la sessione può compiere qualsiasi azione (COMMIT o ROLLBACK)
0 non ci sono transazioni attive
-1 se esiste una transazione aperta, non è possibile eseguire il COMMIT

Vediamo un possibile pattern di utilizzo di XACT_STATE() in un blocco Try/Cacth.

Listato 4. Usare XACT_STATE() per valutare lo stato di una transazione in un blocco Catch

SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN;
  INSERT INTO test (data) VALUES ('test')
  SELECT 1/0; -- divisione per zero

END TRY
BEGIN CATCH
--Catturo errori

  exec dbo.prcCatchErrors
  IF (XACT_STATE()) = -1
    ROLLBACK TRAN
  IF (XACT_STATE()) = 1
    COMMIT TRAN
END CATCH

Nella prima riga del listato 4 l'opzione XACT_ABORT è settata su "ON", così facendo la transazione viene bloccata (congelata) quando si verifica l'errore della divisione per 0. Quando l'errore passa al blocco Catch è sufficiente usare la funzione XACT_STATE() per valutare le azioni da intraprendere (ROLLBACK o COMMIT).

Dove Try Catch fallisce

Il costrutto Try/Catch non è sempre efficace nell'intercettazione degli errori. Esistono situazioni particolari in cui non ha alcun effetto, ad esempio quando si verificano:

  1. Avvisi o messaggi con un livello di gravità minore o uguale a 10.
  2. Errori con un livello di gravità superiore o uguale a 20 che implicano un blocco del SQL Server Database Engine, cioè l'interruzione della connessione verso il database, per la sessione in corso.
  3. Errori di compilazione o ricompilazione a livello di singola istruzione del batch di comandi.

Ti consigliamo anche