Implementując logikę w PL/SQL – jak w każdym innym języku programowania – możemy i z pewnością staniemy przed koniecznością kilkukrotnego przetworzenia jakiegoś bloku kodu. Osiągnąć to możemy za pomocą instrukcji LOOP, która występuje w PL/SQL w trzech odsłonach:
- LOOP,
- FOR-LOOP,
- WHILE-LOOP.
Podstawowa pętla LOOP
Jest to najprostsza struktura pętli dostępna w PL/SQL. W jej przypadku blok wykonawczy rozpoczyna się słowem kluczowym LOOP i kończy słowem kluczowym END LOOP. Są to jedyne dwa wymagane elementy w tym przypadku.
LOOP
INSTRUKCJA 1;
INSTRUKCJA 2;
…
INSTRUKCJA N;
END LOOP;
“INSTRUKCJE” od 1 do N to po prostu sekwencje poleceń, które są wielokrotnie wykonywane.
Tym niemniej – o ile nie jesteśmy zainteresowani pętlą, która będzie wykonywać się w nieskończoność – konieczny jest w tym wypadku jakiś mechanizm wychodzenia z niej. Takim “hasłem bezpieczeństwa” w przypadku PL/SQL jest słowo kluczowe EXIT, który należy użyć w części wykonawczej pętli, rzecz jasna w parze z jakimś warunkiem. Zazwyczaj w tym kontekście widziałem przykłady kodu ze słowem kluczowym WHEN, ale nic nie stoi na przeszkodzie, by użyć do tego instrukcji IF – THEN, o której była mowa w ostatnim wpisie.
W przykładzie poniżej zdecydowałem się jednak na to pierwsze rozwiązanie (WHEN), ponieważ w takim ujęciu kod jest moim zdaniem bardziej przejrzysty.
DECLARE
v_licznik NUMBER := 0;
BEGIN
LOOP
EXIT WHEN v_licznik >= 5;
v_licznik := v_licznik + 1;
dbms_output.put_line('Wartość w pętli: ' || v_licznik );
END LOOP;
dbms_output.put_line('Końcowa wartość: ' || v_licznik );
END;
Jestem przekonany, że widoczny wyżej kod jest na tyle oczywisty, że nie wymaga dodatkowego komentarza, ale dla porządku:
- najpierw deklarowana jest zmienna liczbowa v_licznik, do której od razu przypisywana jest wartość 0,
- w bloku wykonawczym inicjujemy pętlę przy pomocy słowa LOOP, po czym od razu określamy warunek wyjścia przy pomocy pary EXIT-WHEN,
- tym sposobem pętla będzie się wykonywać do momentu aż nasza zmienna nie osiągnie wartości większej lub równej 5, czyli pętla działa do czasu aż testowany warunek nie będzie spełniony (TRUE),
- przy każdej iteracji (do czasu spełnienia warunku, o którym mowa wyżej) wartość zmiennej będzie zwiększana o 1, zaś w konsoli zobaczymy wpis “Wartość w pętli: x”, gdzie x oznacza bieżącą wartość zmiennej,
- natomiast po zakończeniu działania pętli dodatkowo zobaczymy końcową wartość zmiennej v_licznik, jaka się nam ostała po przeprowadzonych iteracjach (w tym wypadku będzie ona się równała 5).
Pętla WHILE-LOOP
Struktura takiej pętli wygląda następująco
WHILE TEST LOOP
INSTRUKCJA 1;
INSTRUKCJA 2;
…
INSTRUKCJA N;
END LOOP;
Słowo kluczowe WHILE oznacza początek inicjowanej pętli. Określenie “TEST” to warunek sprawdzany przy każdej iteracji pętli i jego wynikiem może być PRAWDA (TRUE) lub FAŁSZ (FALSE). Jeśli jest on spełniony (TRUE), wykonywana jest sekwencja instrukcji od 1 do N. W przeciwnym wypadku pętla kończy swoje działanie, a sterowanie przechodzi do następnej instrukcji, które znajduje się bezpośrednio po tej pętli. END LOOP to zarezerwowana fraza, która wskazuje koniec konstrukcji pętli.
To na co warto podkreślić w kontrze do poprzedniej konstrukcji LOOP (oprócz tego, że nie występuje tutaj fraza EXIT, choć można jej użyć, o czym za chwilę), to fakt, iż w przypadku słowa kluczowego WHEN pętla była wykonywana do czasu, gdy założony warunek nie był spełniony (FALSE), tutaj zaś jest odwrotnie, czyli kolejne iteracje są uzależniony, czy nas TEST jest utrzymany w mocy (TRUE).
W przypadku pętli WHILE przekazanie NULL do testowanego warunku jest równoznaczne z wystąpieniem FALSE, czyli pętla nie zostanie wówczas wykonana. |
DECLARE
v_licznik NUMBER := 0;
BEGIN
WHILE v_licznik < 5
LOOP
v_licznik := v_licznik + 1;
dbms_output.put_line('Iteracja w pętli: ' || v_licznik );
--EXIT WHEN v_licznik = 3;
END LOOP;
dbms_output.put_line('Końcowa wartość: ' || v_licznik );
END;
W przykładowym kodzie widocznym wyżej dodana został bodajże w 10 wierszu ekstra instrukcja (EXIT WHEN v_licznik = 3), która jednak została “zakomentowana”, ponieważ chcemy osiągnąć rezultat identyczny jak w przykładzie dla podstawowej konstrukcji LOOP. Jednak jeśli uczynimy ją widoczną (pozbędziemy się myślników z przodu) wówczas działanie pętli skończy się na liczbie 3 jako wartości przypisanej do zmiennej v_licznik. Umieszczenie tej instrukcji w tym miejscu miało po prostu zasygnalizować, że instrukcja słowo kluczowe EXIT jest uniwersalnym mechanizmem, który można zastosować w innych rodzajach pętli.
Pętla FOR LOOP
Ten typ pętli najlepiej sprawdza się, gdy chcemy wykonać kod ściśle określoną liczbę razy, a nie w oparciu o spełnienie jakiegoś mniej lub bardziej wydumanego warunku. W najbardziej ogólnej postaci można ją przedstawić w następujący sposób:
FOR licznik_pętli IN [REVERSE] dolna_granica .. górna_granica
LOOP
INSTRUKCJA 1;
INSTRUKCJA 2;
…
INSTRUKCJA N;
END LOOP;
Słowo kluczowe FOR oznacza w tym przypadku początek pętli. Użyta następnie identyfikator “licznik_pętli” odpowiada zmiennej niejawnej, które nie ma potrzeby wcześ nej definiować w sekcji deklaracji dla naszego bloku kodu (jest to wymagany element konstrukcji pętli FOR). To na co należy zwrócić uwagę przy tej okazji, to zasięg tej zmiennej, ponieważ jest on zawężony do instrukcji FOR, czyli w żaden sposób nie można odwoływać się do niej poza tą konkretną pętlą.
“Dolna_granica” i “górna_granica” to zapisy, które odpowiadają jakimś liczbom całkowitym lub wyrażeniom, których wynikiem jest właśnie liczba całkowita. De facto to one określają liczbę iteracji pętli, ponieważ wskazują one pewien zakres, przez który następnie będzie biegła iteracja tak długo, jak licznik (niejawna zmienna “licznik_pętli”) znajduje się pomiędzy tym zakresem. Mechanizm działania wygląda, więc następująco:
- na początku Oracle przypisuje do licznika (zmiennej niejawnej) liczbę, która odpowiada wartości wyrażonej w dolnej granicy,
- przy każdej iteracji licznik jest automatycznie zwiększany o 1,
- dopóki licznik nie przekracza wartości odpowiadającej górnej granicy, wykonywany jest zestaw instrukcji zawarty między LOOP a END LOOP.
Dodatkowo podczas definiowania pętli możemy użyć słowa zastrzeżonego REVERSE, które odwraca sposób iteracji, czyli licznik pętli będzie iterował od górnej do dolnej granicy. Tym niemniej składnia nie ulega zmianie, czyli dolna granica jest zawsze wskazywana jako pierwsza.
Brzmi skomplikowanie? Może faktycznie tak trochę jest, ale to pewnie wina piszącego, ze nie potrafi być bardziej precyzyjny. Natomiast to bardzo podobna konstrukcja do tych spotykanych w wielu innych językach.Choć mi osobiście brakowało możliwości wskazania wartości kroku pętli, czyli wartości, o jaką będzie powiększany/pomniejszany licznik przy każdej iteracji, co w niektórych przypadkach zmniejsza przydatność tej konstrukcji.
DECLARE
v_licznik NUMBER := 0;
BEGIN
FOR v_index IN 1..5 LOOP
v_licznik := v_licznik+1;
dbms_output.put_line('Iteracja w pętli: ' || v_licznik );
END LOOP;
dbms_output.put_line('Końcowa wartość: ' || v_licznik );
END;
NA ZAKOŃCZENIE
W kontekście używania pętli w PL/SQL przy różnego rodzaju samouczkach czy innego tego typu “pomocach naukowych” zazwyczaj spotkamy się z omówieniem instrukcji CONTINUE. Ponieważ jest to kurs dla hobbystów, czyli tak naprawdę bardzo elementarne wprowadzenie do PL/SQL, wątek ten pozwoliłem sobie pominąć, tym bardziej, że jak zwykle wpis okazał się znacznie obszerniejszy niż sobie to obiecywałem. Wspomnę jedynie w tym miejscu, że instrukcja ta pozwala na pominięcie całości lub część powtarzanego i wrócenie do początku pętli.
Jeśli chodzi o kolejny wpis to będzie on dotyczył obsługi wyjątków, choć tutaj od razu gwiazdka, czyli znowu wracam do charakteru czy też przeznaczenia tego cyklu wpisów. Ponieważ są to podstawy podstaw, to siłą rzeczy bardziej złożone zagadnienia – a niewątpliwie takim jest obsługa wyjątków – będą czasem zaprezentowane bez wchodzenia w analizę detali, zwłaszcza takich, o których piszący te słowa ma bardzo blade pojęcie. Wszak nie chodzi mi o przepisywanie jak leci tego, co udało mi się wyszukać w Internecie.