W dotychczasowych przykładach wykonanie programu przebiegało zawsze po kolei, od pierwszej do ostatniej linii. W przykładach tego wykładu sytuacja nie będzie już taka prosta. Instrukcje sterujące są bardzo ważnym elementem każdego języka programowania. Pozwalają wykonać różne fragmenty kodu w zależności od zajścia określonych warunków. Umożliwiają też tworzenie pętli, a więc wielokrotne wykonanie pewnego fragmentu kodu.
IF
jest podstawową instrukcją sterującą w większości języków programowania. Umożliwia rozdzielenie wykonania programu w zależności od tego, czy spełnione są określone warunki. Składnia instrukcji IF
wygląda następująco:
IF warunek ciąg instrukcji 1 ELSE ciąg instrukcji 2
Gdy warunek określony w IF
będzie spełniony, wykona się pierwszy ciąg instrukcji. W przeciwnym przypadku drugi. Instrukcja IF
nie musi zawierać ELSE
. W takim przypadku ciąg instrukcji wykona się, gdy spełniony jest warunek, a w przeciwnym przypadku ciąg instrukcji zostanie pominięty i wykonanie programu od razu przejdzie dalej.
Warunek logiczny umieszczony po słowie IF
może używać tych samych operatorów oraz funkcji, których używaliśmy, gdy omawialiśmy warunek WHERE
instrukcji SELECT
. Możemy więc łączyć kilka warunków spójnikami logicznymi (AND
, OR
, NOT
), używać nawiasów, czy innych operatorów (np. LIKE
, BETWEEN
, IN
). Podobnie jak w przypadku klauzuli WHERE
, warunek jest spełniony wyłącznie wtedy, gdy zwraca logiczną wartość „prawda”. Pierwszy ciąg instrukcji wykona się więc wyłącznie w takim przypadku. Gdy wynikiem jest „fałsz” lub NULL, wykona się blok znajdujący się po słowie ELSE
.
Ciąg instrukcji powinien znajdować się między słowami BEGIN
i END
. Słowa te można pominąć, gdy warunkiem ma być objęta pojedyncza instrukcja. Przykład:
DECLARE @Ile INT SELECT @Ile = COUNT(*) FROM emp IF @Ile < 16 BEGIN INSERT INTO emp (empno, ename, sal) VALUES (1234, 'Kowalski', 1000) PRINT 'Wstawiono Kowalskiego' END ELSE PRINT 'Nie wstawiono danych'
Jak widać na powyższym przykładzie, jeśli pensja jest większa niż 0 wykonają się dwie instrukcje (INSERT
oraz PRINT
). Konieczne jest więc użycie słów BEGIN
i END
. Nie trzeba jednak ich stosować po ELSE
, gdyż znajduje się tam tylko jedna instrukcja.
Używając instrukcji sterujących warto robić wcięcia korzystając z przycisku tabulacji. Dzięki temu łatwiej będzie zorientować się, który fragment kodu wykona się przy zajściu określonego warunku. Pamiętajmy, że w bardziej zaawansowanych programach instrukcji sterujących może pojawić się bardzo wiele, mogą być zagnieżdżone jedna w drugiej itd.
W języku Transact-SQL istnieje możliwość umieszczania podzapytań wewnątrz warunku logicznego. Niejednokrotnie pozwala to znacznie uprościć kod. Oto kod ostatniego przykładu z podzapytaniem w instrukcji IF
. Jak widać, nie ma w tym przypadku konieczności deklarowania zmiennej:
IF (SELECT COUNT(*) FROM emp) < 16 BEGIN INSERT INTO emp (empno, ename, sal) VALUES (1234, 'Kowalski', 1000) PRINT 'Wstawiono Kowalskiego' END ELSE PRINT 'Nie wstawiono danych'
Może zajść konieczność rozdzielenia sterowania programu na więcej niż dwie ścieżki. Niektóre języki programowania oferują konstrukcje typu ELSEIF
. Konstrukcja taka nie jest bezpośrednio dostępna w Transact-SQL. Możemy ją jednak zastąpić kilkoma konstrukcjami IF ... ELSE
. Przykład:
DECLARE @sal INT SELECT @sal = sal FROM emp WHERE empno = 1234 IF @sal < 1000 BEGIN UPDATE emp SET sal = sal + 100 WHERE empno = 1234 PRINT 'Zwiększono pensję' END ELSE IF @sal > 2000 BEGIN UPDATE emp SET sal = sal - 100 WHERE empno = 1234 PRINT 'Zmniejszono pensję' END ELSE PRINT 'Pensja niezmieniona'
Pętla jest konstrukcją programistyczną pozwalającą na wielokrotne wykonanie fragmentu kodu. Języki programowania ogólnego przeznaczenia oferują zwykle kilka konstrukcji pętli. W Transact-SQL nie mamy wyboru. Istnieje wyłącznie pętla WHILE
czyli taka, w której fragment kodu będzie wykonywany wielokrotnie do czasu, gdy przestanie być spełniony warunek logiczny. Nie jest to duża wada, gdyż przy pomocy pętli WHILE
jesteśmy w stanie zrealizować każdy inny rodzaj pętli. Pętla WHILE
ma następującą postać:
WHILE warunek ciąg instrukcji
Pisząc tego typu pętlę powinniśmy upewnić się, że warunek logiczny kiedyś przestanie być spełniony. W przeciwnym przypadku nasz program wejdzie w nieskończoną pętlę.
Przykład użycia pętli w celu wypisania liczb od 1 do 10:
DECLARE @licznik INT SET @licznik = 1 WHILE @licznik <= 10 BEGIN PRINT @licznik SET @licznik = @licznik + 1 END
Wewnątrz pętli WHILE
mamy możliwość umieszczenia instrukcji natychmiastowego przerwania pętli (BREAK
) oraz przejścia do kolejnej iteracji bez wykonywania reszty kodu (CONTINUE
). Przykład:
WHILE (SELECT AVG(Sal) FROM Emp) < 2000 BEGIN UPDATE Emp SET Sal = Sal * 1.1 IF (SELECT MAX(Sal) FROM Emp) > 4000 BREAK ELSE CONTINUE END
Program ten będzie zwiększał pensję pracownikom o 10% do czasu, gdy średnia przekroczy 2000. Jeśli jednak wcześniej maksymalna pensja przekroczy 4000 program zakończy się natychmiast. Użycie instrukcji CONTINUE
w tym przypadku nie jest konieczne, gdyż po niej i tak nie znajduje się żaden kod, a więc wykonałby się kolejny obrót pętli.
Wewnątrz kodu Transact-SQL może zaistnieć konieczność wywołania błędu, który będzie obsłużony przez aplikację kliencką. Błędy takie będą przez aplikację kliencką traktowane na równi z błędami zgłaszanymi przez system zarządzania bazą danych.
RAISERROR (komunikat|numer, severity, state)
Gdzie:
Wartości parametrów Severity i State są przekazywane do aplikacji klienta, dzięki czemu różne błędy mogą być obsługiwane w różny sposób.
Umieszczona w kodzie procedury instrukcja zwraca komunikat o błędzie (zadeklarowany w parametrze Message) oraz Severity i State. Dla wartości Severity większej od 10 następuje przerwanie realizacji kodu procedury i (ewentualnie) przekazanie sterowania do bloku CATCH
.
Błędy, zarówno te zgłaszane przez programistę, jak też zgłaszane przez system, mogą być obsłużone, a więc w momencie wystąpienia błędu sterowanie zostanie przekazane do odpowiedniego bloku kodu. Obsługa błędów może odbywać się w deklarowanych bezpośrednio po sobie blokach:
BEGIN TRY Kod END TRY BEGIN CATCH Kod wykonywany, gdy wystąpi błąd END CATCH
Zasadnicze instrukcje naszego programu umieszczane są w bloku TRY
i wykonywane, dopóki nie zostanie podniesiony błąd. W przypadku wystąpienia błędu o wartości severity większej niż 10 sterowanie zostaje przekazane do bloku CATCH
i wykonywane są dalej instrukcje zawarte w tym bloku.
Bloków TRY
i CATCH
może być w procedurze kilka , mogą być w sobie zagnieżdżane.
Przekazanie sterowania do bloku CATCH
może nastąpić zarówno po podniesieniu błędu instrukcją RAISERROR
, jak też po podniesieniu przez system bazy danych błędu o severity większym niż 10.
Poniższy przykład ilustruje zgłaszanie i obsługę błędów w Transact-SQL:
DECLARE @Level Int SET @Level = 1 -- tutaj podstaw wartość 1 lub 2 DECLARE @StrInfo Varchar(30) BEGIN TRY IF @Level = 1 BEGIN SET @StrInfo = 'Błąd niekrytyczny' RAISERROR (@StrInfo, 1, 10) PRINT 'Przeszliśmy błąd o numerze 10' END IF @Level = 2 BEGIN SET @StrInfo = 'Błąd krytyczny' RAISERROR (@StrInfo, 11, 20) PRINT 'Czy przeszliśmy błąd o numerze 20 ?' END END TRY BEGIN CATCH DECLARE @ErrMsg NVARCHAR(100), @ErrSev INT, @ErrSt INT; SELECT @ErrMsg = ERROR_MESSAGE(), @ErrSev = ERROR_SEVERITY(), @ErrSt = ERROR_STATE(); PRINT @ErrMsg + ' Severity = ' + Cast(@ErrSev As Varchar(3)) + ' State = ' + Cast(@ErrSt As Varchar(3)) END CATCH
Instrukcja warunkowa IF
w PL/SQL ma następującą postać:
IF warunek THEN ciąg instrukcji ELSIF warunek THEN ciąg instrukcji ELSE ciąg instrukcji END IF;
ELSE
oraz ELSIF
nie muszą wystąpić. ELEIF
może pojawić się wielokrotnie.
Oracle oferuje trzy konstrukcje pętli:
LOOP ciąg instrukcji (w tym EXIT lub EXIT WHEN warunek) END LOOP;
FOR zmienna IN wartość1 .. wartość2 LOOP ciąg instrukcji END LOOP;
WHILE warunek LOOP ciąg instrukcji END LOOP;
Przykład użycia instrukcji warunkowej w PL/SQL:
DECLARE pensja INTEGER; BEGIN SELECT sal INTO pensja FROM emp WHERE empno = 1234; IF pensja < 1000 THEN UPDATE emp SET sal = sal + 100 WHERE empno = 1234; dbms_output.put_line ('Zwiększono pensję'); ELSIF pensja > 2000 THEN UPDATE emp SET sal = sal - 100 WHERE empno = 1234; dbms_output.put_line 'Zmniejszono pensję'; ELSE PRINT 'Pensja niezmieniona'; END IF; END;