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;

Inapoi la cuprins.

BogSoft 1999