STRUCTURI DE CONTROL
In acest capitol se explica structurile de control ale unui program
PL/SQL. Acestea sint expresiile conditionale, salturile si buclele.
Introducere in expresiile de control ale PL/SQL
PL/SQL ofera facilitati de control al executiei conditionat si
neconditionat.
Aceste sint:
instructiuneaIF
Ofera controlul selectiv al actiunilor, bazat pe indeplinirea
unor conditii.
instructiunea GOTO
Se foloseste la saltul neconditionat la o eticheta din program.
ciclul simplu
Asigura repetarea unor actiuni fara impunerea de conditii
ciclul FOR
Pentru control iterativ al actiunilor, bazat pe un contor.
cicluri WHILE
Pentru control iterativ al actiunilor, bazat pe indeplinrea
unei conditii
instructiunea EXIT
Pentru a iesi dintr-un ciclu
Acolo unde aceste facilitati includ o conditie, aceasta poate fi orice
expresie booleana valida in SQL, cu exceptia query imbricat. Aceasta
include:
* Operatori logici ( ,, -, NOT, etc.)
* Operatori de compunere ( AND, OR )
* Operatori SQL ( LIKE, IN, BETWEEN, IS NULL )
Exemple:
var
Instructiunea IF
Instructiunea IF are o structura similara cu echivalentul din
limbajele procedurale. Ea permite executarea unor actiuni selectiv, bazat
pe indeplinirea unor conditii. Structura sa generala este prezentata mai jos.
Sintaxa:
IF conditie THEN actiuni [ ELSIF conditie THEN actiuni ]
[ ELSE actiuni ]
END IF;
unde "actiuni" pot fi una sau mai multe instructiuni PL/SQL sau SQL,
fiecare terminata cu punct-virgula. Aceste "actiuni" pot include alte
instructiuni IF cu aceiasi structura, ceea ce inseamna ca
instruciunea poate contine IF, ELSE, ELSIF imbricate.
Instructiunea IF simpla
Sa incepem cu un exemplu care implica numai o conditie, fara nici una din
clauzele optionale:
IF v_ename = 'SCOTTY' THEN beam_me_up := 'YES';
COMMIT;
END IF;
Cele doua actiuni sind executate numai daca conditia este adevarata (TRUE).
Daca conditia este falsa (FALSE sau NULL) atunci cele doua instructiuni
sint excluse. In oricare din caz, executia continua cu urmatoarea
instructiune din program de dupa END IF.
Notati ca daca constructia 'END IF' nu este prezenta la sfirsitul
instructiunii, atunci PL/SQL ca cauta in instructiunile urmatoare cautind
'END IF'. Ca si in alte limbaje procedurale aceasta poate provoca
afisarea unor mesaje de eroare confuze.
Urmatorul exemplu arata cum pot fi executate instructiuni alternative
prin intermediul clauzei ELSE. Actiunile specificate prin ELSE sint
executate daca conditiila este FALSE sau NULL, dupa care programul
continua cu instructiunile ce urmeaza dupa END IF.
IF ename = 'SCOTTY' THEN beam_me_up = 'YES' ;
COMMIT;
ELSE beam_me_up = 'NO' ;
ROLLBACK;
END IF;
Desen cu instructiunea IF
Oricare set de actiuni poate include instructiuni IF inlantuite pentru a
executa teste ulterioare inainte ca o actiune specifica sa fie executata.
Fiecare instructiune IF inlantuita trebuie sa fie terminata de END IF-ul
corespunzator.
Exemplu:
IF deptno = 10 THEN accounting := 'YES';
IF job = 'MANAGER' THEN
INSERT INTO status VALUES ( ' Dept10 Manager');
END IF;
COMMIT;
ELSE accounting := 'NO';
IF job = 'MANAGER' THEN
INSERT INTO status VALUES (' Alt Mgr.');
END IF;
COMMIT;
END IF;
diagrama 2 IF THEN ELSIF
Adesea, actiunile de executat in clauza ELSIF pot contine doar un alt IF.
In aceasta situatie, este mult mai convenabil sa folositi clauza ELSIF,
care elimina necesitatea de a inlantui END IF la sfirsitul fiecarui set
ulterior de conditii/actiuni:
IF f_title = 'doamna' THEN status := 'MARITATA';
ELSIF f_title = 'domnisoara' THEN
status := 'NEM.'
ELSE status:= 'NECUNOSCUTA';
END IF;
Datorita posibilitatii de a imbrica actiuni, clauza ELSIF poate fi
utilizata recursiv, daca doriti:
IF choise = 1 THEN actiune := 'Run Payroll' ;
ELSIF choice = 2 THEN actiune := 'Run Accounts';
ELSIF choice = 3 THEN actiune := 'Backups';
ELSIF choice = 4 THEN actiune := 'Restore';
ELSE actiune := 'Invalid';
END IF;
__________________________________________________________________________
Cicluri de baza si instructiunea EXIT
Structura:
LOOP
-- actiuni de executat in
-- cadrul ciclului
END LOOP;
* EXIT termina ciclul
Sintaxa:
EXIT [eticheta-ciclu] [WHEN conditie];
__________________________________________________________________________
Cicluri si salturi in PL/SQL
PL/SQL ofera un numar de facilitati pentru cicluri si pentru a iesi din
cicluri, cind se doreste, spre alta parte a programului. Cel mai simplu
caz contine instructiunile ce se doresc repetate incluse intre delimitatorii
LOOP si END LOOP:
LOOP
-- actiuni de executat
-- in cadrul ciclului
END LOOP;
De fiecare data cind programul ajunge la constructia END LOOP, controlul
este redat expresiei LOOP corespunzatoare. Acest ciclu necontrolat va fi,
bineinteles, infinit daca nu se folosesc instructiuni de control in
interiorul ciclului, care sa provoace saltul in afara ciclului.
Instructiunea EXIT
Un ciclu poate fi terminat din interior, daca este utilizata
instructiunea EXIT. EXIT permite transferul controlului instructiunii
urmatoare de dupa END LOOP, permitind astfel incheierea imediata a ciclului.
Sintaxa:
EXIT [loop-label] [WHEN conditie];
EXIT poate fi executat fie ca o actiune dintr-un IF, sau ca o
instructiune individuala in cadrul unui ciclu. In acest caz, o clauza
WHEN poate fi atasata la instructiunea IF, permitind terminarea
conditionata a ciclului.
Exemplul 1:
LOOP
counter := counter + 1;
INSERT INTO numbered_rows VALUES (counter);
...
IF counter = 10 THEN COMMIT;
EXIT;
END IF;
END LOOP;
Exemplul 2:
LOOP
...
EXIT WHEN total_sals = 60000;
...
END LOOP;
O alta posibilitate de a iesi dintr-un ciclu ar fi sa sariti la o
instructiune etichetata in afara ciclului, folosind instructiunea GOTO.
Aceasta este in general considerata ca o metoda mai putin structurata.
GOTO si etichetele vor fi discutate mai tirziu, in acest capitol.
Utilizarea ciclurilor FOR pentru controlul iteratiilor
Ciclurile FOR au aceeasi structura generala ca si celee pe care deja
le-ati vazut, dar adauga instructiuni de control la inceputul ciclului,
care vor determina numarul de iteratii ce vor fi efectuate.
Sintaxa:
FOR variabila_control IN [REVERSE] valoare_inferioare .. valoare_superi
oara
Unde 'variabila_control' este numele unei variabile intregi ale carei
valori vor fi incrementate/decrementate automat la fiecare iteratie a
ciclului. Aceasta variabila este creata de catre ciclu, si nu poate fi
scrisa de nici o instructiune din cadrul ciclului. Durata de viata se
termina la terminarea ciclului.
'valoare_inferioara' si 'valoare_superioar' sint expresii intregi,
care determina valorile pe care le va lua variabila de control.
Implicit, variabila de control este initializata cu o valoare mica, si
este incrementata cu +1 la fiecare iteratie pina cind valoarea superioara
este atinsa.
Exemplu:
FOR i IN 1 .. 2000
LOOP
INSEERT INTO numbered_rows VALUES (i);
preserve_i := i;
...
END LOOP;
Observati ca daca valoarea lui 'i' este necesara dupa terminarea
ciclului, atunci ea trebuie copiata intr-o variabila declarata inainte de
terminarea ciclului.
Ciclul FOR poate si sa decrementeze variabila de control, incepind de
la valoarea superioara in prima iteratie:
FOR n IN REVERSE 50 .. myvar+50
LOOP
-- n are valoarea myvar+50 la prima iteratie.
-- si valoarea 50 la ultima
END LOOP;
Utilizarea ciclurilor WHILE pentru controlul iteratiilor
Aceasta este o structura de control alternativa, care permite
iteratiilor sa fie executate conditionat.
Sintaxa:
WHILE conditie
Conditia este evaluata la inceputul fiecarei iteratii, si ciclul este
terminat cind conditia este FALSE. Daca conditia este FALSE atunci cind
ciclul incepe, atunci nici o interatie nu se executa.
Exemplul 1:
WHILE bill
Exemplul 2:
WHILE bool_var
LOOP
-- actiuni
END LOOP;
Normal, daca variabila implicata in conditii nu se schimba in timpul
executiei ciclului, atunci ciclul nu se va termina niciodata. Totusi
instructiunea EXIT poate fi inclusa atit in ciclurile FOR si WHILE.
Controlul ciclurilor imbricate
Ciclurile pot fi imbricate pe nivele multiple. Puteti imbrica cicluri
FOR in cicluri WHILE si invers. In mod normal, terminarea unui ciclu
intern nu termina ciclul care il include, exceptind cazul cind este
generata o exceptie.
Etichetele in PL/SQL sint definite astfel:
>
Numele etichetei urmeaza aceleasi reguli ca si orice identificatori.
O eticheta este plasata inaintea unei instructiuni, fie pe aceasi linie,
fie pe o linie separata.
Ciclurile pot fi etichetate prin plasatea unei etichete inainte de
cuvintul LOOP, si dupa END LOOP, conform:
> LOOP
-- actiuni
END LOOP outer_limits;
Daca un ciclu subordonat executa un EXIT, atunci el poate specifica
care ciclu sa fie incheiat prin specificarea etichetei.
Exemplu:
> LOOP
...
LOOP
...
EXIT main WHEN total_done = 'YES';
-- paraseste ambele cicluri
EXIT WHEN inner_done = 'YES';
-- paraseste doar ciclul interior
END LOOP main;
Folosirea GOTO si a etichetelor
Dupa cum ati vazut, etichetele pot fi utilizate pentru a identifica
cicluri atunci cind apar in structuri inlantuite. Aceasta este si cazul
blocurilor inlantuite. O eticheta poate fi plasata inaintea inceputului
si dupa sfirsitul unui bloc PL/SQL, astfel incit identitatea sa declarate
sa poate fi deosebita de celelalte blocuri din structura inlantuita. Sa
luam exemplul urmator:
DECLARE
var1 NUMBER;
BEGIN
.
DECLARE
var1 NUMBER := 400;
BEGIN
.
var1 := var1 + 1 ; -- var1 din subbloc se mareste cu 1
.
END;
.
END;
Variabila var1 care a fost declarata in blocul exterior nu se "vede"
in cadrul blocului interior, deoarece acesta a declarat o variabila cu
acelasi nume. Toate referirile la 'var1' din sub-bloc vor fi deci
folosite pentru variabila local declarata.
Totusi, daca blocurile sint etichetate, identificatorii pot fi
referiti prefixati de numele blocului dorit. (Observatie: blocul exterior
nu trebuie neaparat etichetat). Un exemplu:
BEGIN
> DECLARE
var1 NUMBER;
.
BEGIN
.
> DECLARE
var1 NUMBER := 400;
BEGIN
.
block1.var1 := block1.var + 1;
-- variabila din block 1
-- este incrementata
END block2;
END block1;
END;
Asa cum s-a aratat, END poate include numele blocului care trebuie
terminat.
Instructiunea GOTO
Aceasta instructiune ofera salturi neconditionate la o eticheta in
cadrul unui program PL/SQL. Utilizarea instructiunii trebuie minimizata,
deoarece folosirea excesiva poate duce la programe foarte dezorganizate.
Valabilitatea unei etichete este in intregul bloc in care a fost
definita. GOTO poate, deci, transmite controlul catre puncte etichetate
din blocul curent sau catre un punct etichetat dintr-un alt bloc care
contine blocul curent.
Sintaxa:
GOTO eticheta;
Unde 'eticheta' poate fi orice eticheta care marcheaza o pozitie in
cadrul blocului curent sau al altuia ce il cuprinde. GOTO nu poate
transmite controlul in interiorul unui sub-bloc.
Exercitii pentru capitolul 22
1. Scrieti un bloc PL/SQL care sa insereze un rind in tabela MESSAGES
cu coloana NUMCOL1 continind 1 daca este primul rind, 2 daca este
al 2-lea s.a.m.d. Nu inserati rinduri etichetate 6 sau 8 si iesiti
din ciclu cind valoarea 10 a fost inserata. Actualizati baza cind
se termina ciclul. (atentie - COUNT este cuvint rezervat!)
2. (daca aveti timp) Selectati coloanele ENAME, HIREDATE si SAL din
tabela EMP unde EMPNO este egal cu un numar introdus in timpul
executiei. Dupa ce ati selectat coloanele in variabile, inserai un
rind in MESSAGES bazat pe unul din urmatoarele criterii:
Criteriu / Mesaj
Salariu mai mare de 1200
Salariu mai mare de 1200
ENAME contine 'T'
Numele contine "T"
HIREDATE este Decembrie
Decembrie
Nici unul din cazurile precedente
** Nimic **
Testatti pentru angajatii cu numerele 7654, 7369, 7900, 7876.
3. Urmatorul cod executa un ciclu, cu diferite valori pentru V la
fiecare interatie (in domeniul 1 la 10).
UPDATE messages SET numcol2 = 100
WHERE numcol1 = V ;
Daca rularea UPDATE produce altceva decit un singur rind, atunci se va
iesi din ciclu.( Puteti testa atributul SQL%ROWCOUNT, asa cum este
discutat in capitolul 23).
Solutii la capitolul 22
1.
BEGIN
FOR v_count IN 1..10
LOOP
IF v_count NOT IN (6,8) THEN
INSERT INTO messages (numcol1) VALUES (v_count);
END IF;
END LOOP;
COMMIT;
END;
2.
DECLARE
v_ename emp.ename%TYPE;
v_hiredate emp.hiredate%TYPE;
v_sal emp.sal%TYPE;
v_message CHAR(30);
BEGIN
SELECT ename, hiredate, sal
INTO v_ename, v_hiredate, v_sal
FROM emp
WHERE empno = &EMPLOYEE_NO ;
IF v_sal > 1200 THEN
v_message := 'Salariu mai mare de 1200';
ELSIF v_ename LIKE '%T%' THEN
v_message := 'Numele contine "T"';
ELSIF TO_CHAR(v_hiredate, 'MON') = 'DEC' THEN
v_message := 'Decembrie';
ELSE v_message := '** Nimic **';
END IF;
INSERT INTO messages (charcol1) values (v_message);
END;
3.
BEGIN
FOR v IN 1 .. 10
LOOP
UPDATE messages SET numcol2 = 100
WHERE numcol1 = v;
EXIT WHEN SQL%ROWCOUNT 1;
END LOOP;
END;
BogSoft 1999