TRY TRY/CATCH to konstrukcja, która występuje w wielu językach programowania. TRY/CATCH pomaga odseparować daną logikę zapytania która w szczególnych przypadkach może zwrócić błąd.

W bloku TRY wykonujemy niebezpieczne zapytanie .W przypadku wystąpienia błędu łapiemy ten błąd go i obsługujemy w bloku CATCH.

Gdy błąd wystąpi SQL Server automatycznie przeskakuje do bloku CATCH. Niestety kod który do tej pory się wykonał nie zostanie cofnięty. Do tego służą nam transakcję i są one też powiązane z blokami TRY/CATCH. W tym wpisie skoncentruje się tylko na blokach TRY/CATCH i na wywoływaniu własnych błędu.

Istnieją przypadki w których wywoływanie swoich własnych błędów jest naprawdę pomocne.

Przykład TRY i CATCH

Poniżej jest ukazany prosty przykład bloku TRY I CATCH.

BEGIN TRY
	DECLARE @Int INT;
	-- Poniższe wyrażenie wywoła wyjątek: Arithmetic 
	--overflow error converting expression to data type int.
	SET @Int = 999999999999999999999999;
	PRINT 'Udało się'
END TRY
BEGIN CATCH
	SELECT 'Arithmetic overflow error converting expression to data type int' AS Error
END CATCH;

W bloku TRY próbuje nadać zmiennej typu INT o wiele za dużą wartość. Zakres jest przekroczony SQL Server zwraca błąd. Błąd ten wywołuje blok CATCH ,a ten wywołuje prostego SELECT-a. Polecenie PRINT w bloku TRY nie zostało wykonane.Zostało ono pominięte.

Ograniczenia TRY i CATCH

  • Skompilowane błędy nie są przechwytywane
  • Jeżeli proces jest terminowany poprzez jego zabicie lub utratę połączenia wtedy TRY/CATCH nie na wiele się zda.

Warto też wspomnieć o funkcjach, które mogą być użyte w bloku CATCH . Dzięki nim możemy uzyskać więcej informacji na temat przechwyconego błędu.

Funkcję które można użyć w bloku CATCH

  • ERROR_NUMBER: zwraca on numer błędu. Zwracana jest taka sama wartość jak w przypadku parametru @@ERROR.
  • ERROR_SERVERITY: zwraca on poziom wywołanego błędu który wywołał blok CATCH
  • ERROR_STATE: zwraca on numer stanu błędu
  • ERROR_LINE: zwraca on linie w której błąd został wykonany
  • ERROR_PROCEDURE: zwraca on nazwę procedury bądź trigger-a który wywołał błąd. Może być pusty
  • ERROR_MESSAGE: pełna treść błędu.

Oto przykład użycia tych funkcji.

BEGIN TRY
-- Dzielenie przez zero
SELECT 1/0;
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;
END CATCH;

RAISERROR

Jakbyś się zastanawiał po co istnieje możliwość wywołania błędów warto przypomnieć sobie o problemie DEATHLOCK. Wystąpi on wtedy gdy obie transakcje zablokują się nawzajem i będą oczekiwać na wzajemne zwolnienie zamków, które nigdy nie nastąpi. Na poziomie wirtualnym ten problem jest nie do zatrzymania. Chyba ,że wcześniej wyposażymy się w kod zabezpieczający nas przed taki wypadkiem. Ten kod wyrzuci błąd terminując cały proces danego zapytania.

Oto przykład ponownego wywołania błędu gdy ten został już przechwycony.

SQLBEGIN TRY
	DECLARE @Int INT;
	-- Poniższe wyrażenie wywoła wyjątek: Arithmetic 
	--overflow error converting expression to data type int.
	SET @Int = 999999999999999999999999;
END TRY
BEGIN CATCH
	DECLARE @ErrorMessage NVARCHAR(4000);
	SET @ErrorMessage = ERROR_MESSAGE();
	RAISERROR (@ErrorMessage, 16, 1);
END CATCH;

Używając funkcji ERROR_MESSAGE() jestem wstanie przechwycić nazwę błędu i wywołać ten sam komunikat ponownie.

Szesnastka w wywołaniu tej funkcji obrazuje poziom tego błędu. Tylko błędy na poziomie 11-19 są przechwytywane w bloku TRY ,a alternatywnie terminują dane zapytanie.

Jedynka reprezentuje “stan” wykonywanego zapytania. Jego poprawne użycie wymaga bardziej zawansowanego przykładu więc na razie nie zawracajmy nim sobie głowy.

Poniższy kod wyrzuca dwa błędy. Błąd o poziomie 1 zostanie wyświetlony w konsoli oraz zapisany w bazie jednak nie zablokuje on dalszego przepływu. Drugi błąd o poziomie 16 wywoła blok CATCH.

BEGIN TRY
	--Zasygnalizuje błąd ,ale polecenia będą wykonywały się dalej
	RAISERROR ('Low Error', -- Message text.
               1, -- Severity.
               1 -- State.
               );
               
    -- RAISERROR z poziomem 11-19 sprawi ,że wykonanie przeskoczy do 
    -- bloku CATCH
    RAISERROR ('My Name is Error', -- Message text.
               16, -- Severity.
               1 -- State.
               );
END TRY
BEGIN CATCH
    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;
    
	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;

Jak widać wykonał się SELECT w bloku CATCH.

image

W “Messages” można też uzyskać informację na temat pierwszego błędu o poziomie 1.

Low Error
Msg 50000, Level 1, State 1

(1 row(s) affected)

To wszystko.

error

Dalsze użycia TRY/CATCH i RAISERROR wymaga bardziej zawansowanych przykładów. Zostawię je na inny wpis.