Sunteți pe pagina 1din 7

SGBD - Anul 3 Laborator 4 Cursoare

Un cursor este o modalitate de a parcurge (linie cu linie) multimea de linii procesate returnate de o cerere multiple-row. Aceast mulime se numete active set. Cursoarele pot fi: implicite care sunt declarate de PL/SQL in mod implicit pentru toate comenzile LMD si comanda SELECT, inclusiv comenzile care returneaza o singura linie. explicite pentru cereri care returneaza mai mult de o linie, sunt definite cursoare explicite, denumite de programator si manipulate prin intermediul unor comenzi specifice.

Etapele utilizarii unui cursor: a) Declarare (n sectiunea declarativa a blocului PL/SQL): CURSOR c_nume_cursor [ (parametru tip_de_Date, ..)] IS Comanda SELECT; b) Deschidere (comanda OPEN), operatie ce identific mulimea de linii (active set): OPEN c_nume_cursor [ (parametru, )]; c) Incarcare (comanda FETCH ). Numarul de variabile din clauza INTO trebuie sa se potriveasca cu lista SELECT returnata de cursor. FETCH c_nume_cursor INTO variabila, ; d) Verificare dac nu am ajuns cumva la finalul mulimii de linii folosind atributele: C_nume_cursor%NOTFOUND valoare booleana C_nume_cursor%FOUND valoare booleana Daca nu s-a ajuns la final mergi la c). e) Inchidere cursor (operatiune foarte importanta avand in vedere ca daca nu e inchis cursorul ramane deschis si consuma din resursele serverului, MAX_OPEN_CURSORS) CLOSE c_nume_cursor; Atributele cursoarelor Atribut Tip %ISOPEN Boolean %NOTFOUND Boolean %FOUND %ROWCOUNT Boolean Number Descriere TRUE atunci cnd cursorul este deschis TRUE dac cea mai recent operaie FETCH nu a regsit o linie TRUE dac cea mai recent operaie FETCH a ntors o linie ntoarce numrul de linii returnate pn la momentul respectiv.

Etapele de mai sus sunt realizate implicit dac gestionarea cursorului se realizeaz prin intermediul comenzii urmatoare (denumit ciclu cursor): FOR variabila IN cursor LOOP Secventa_instructiuni; END LOOP; Clauza FOR UPDATE Comanda SELECT are urmatoarea extensie PL/SQL pentru blocarea explicita inregistrarilor ce urmeaza a fi prelucrate (modificate sau sterse): SELECT FROM WHERE ORDER BY FOR UPDATE [OF lista_coloane] [NOWAIT | WAIT n]; Daca liniile selectate de cerere nu pot fi blocate din cauza altor blocari atunci

SGBD Laborator 4

- daca se foloseste NOWAIT este ridicata imediat eroarea ORA-00054 - daca nu se foloseste NOWAIT atunci se asteapta pana cand liniile sunt deblocate. - daca se foloseste WAIT n atunci se asteapta un numar determinat de secunde inainte de a da eroare ca liniile ce trebuie selectate pentru modificare sunt blocate. Nu este recomandata anularea (ROLLBACK ) sau permanentizarea schimbarilor inainte de a inchide cursorul ce foloseste FOR UPDATE pentru ca aceasta ar elibera blocarile realizate de acesta. Pentru a modifica o anumit linie returnata de un cursor se poate folosi clauza: WHERE CURRENT OF nume_cursor Aceasta clauza apare la finalul unei comenzi UPDATE si face referinta la un cursor care este deschis si s-a facut cel putin o incarcare din el (FETCH).

Exerciii: [Cursoare implicite]


1. S se actualizeze liniile tabelului EMP_PNU, mrind cu 10% valoarea comisionului pentru salariaii avnd salariul mai mic dect o valoare introdus de utilizator. S se afieze dac au fost actualizate linii sau nu (SQL%FOUND), iar n caz afirmativ s se afieze numrul de linii afectate (SQL%ROWCOUNT). Ce fel de cursor folosim? 2. S se creeze un tabel DEP_EMP_PNU avnd cmpurile cod_dep i cod_ang. S se introduc ntr-o variabil de tip tablou imbricat codurile departamentelor (n care exist angajai), iar apoi, prin intermediul unei comenzi FORALL s se insereze aceste coduri i codurile angajailor corespunztori n tabelul DEP_EMP_PNU. Pentru fiecare departament s se afieze ci angajai au fost introdui. Obs: De revzut observaia din laboratorul 3, referitoare la mecanismul BULK COLLECT i la atributul %BULK_ROWCOUNT al cursorului implicit.

Exerciii: [Introducere cursoare explicite]


3. S se obin cte o linie de forma <nume_angajat> are salariul anual <salariu_anual> pentru fiecare angajat din departamentul 50. Se cer 4 soluii (WHILE, LOOP, ciclu cursor cu varianta de scriere a cursorului n interiorul su). Soluia 1: DECLARE CURSOR c_emp IS SELECT last_name, salary*12 sal_an FROM emp_pnu WHERE department_id = 50; V_emp c_emp%ROWTYPE; BEGIN OPEN c_emp; FETCH c_emp INTO v_emp; WHILE (c_emp%FOUND) LOOP DBMS_OUTPUT.PUT_LINE ( v_emp.last_name || are salariul anual : || v_emp.sal_an); FETCH c_emp INTO v_emp; END LOOP; CLOSE c_emp; END; / Soluia 2: DECLARE CURSOR c_emp IS SELECT last_name, salary*12 sal_an

SGBD Laborator 4

FROM emp_pnu WHERE department_id = 50; V_emp c_emp%ROWTYPE; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO v_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE ( v_emp.last_name || are salariul anual : || v_emp.sal_an); END LOOP; CLOSE c_emp;

END; / Soluia 3: // nu mai este nevoie explicit de OPEN, FETCH, CLOSE !!! DECLARE CURSOR c_emp IS SELECT last_name, salary*12 sal_an FROM emp_pnu WHERE department_id = 50; BEGIN FOR v_emp IN c_emp LOOP DBMS_OUTPUT.PUT_LINE (v_emp.last_name || are salariul anual : || v_emp.sal_an); END LOOP; END; / Soluia 4: BEGIN FOR v_rec IN (SELECT last_name, salary*12 sal_an FROM employees WHERE department_id = 50) LOOP DBMS_OUTPUT.PUT_LINE ( v_rec.last_name || are salariul anual : || v_rec.sal_an); END LOOP; END; / 4. Sa se afiseze salariatii care au salariul mai mic de 7000$, in urmatoarea forma: -Salariatul <nume> castiga <salariu>-. 5. Creai un bloc PL/SQL care determin cele mai mari n salarii, urmnd paii descrii n continuare: a) creai un tabel TOP_SALARII_PNU, avnd o coloan salary. b) Numrul n (al celor mai bine pltii salariai) se va introduce de ctre utilizator (se va folosi comanda SQL*Plus ACCEPT i o variabil de substituie p_num). c) n seciunea declarativ a blocului PL/SQL se vor declara 2 variabile: v_num de tip NUMBER (corespunztoare lui p_num) i v_sal de tipul coloanei salary. Se va declara un cursor emp_cursor pentru regsirea salariilor n ordine descresctoare (se presupune c nu avem valori duplicate). d) Se vor introduce cel mai bine pltii n angajai n tabelul TOP_SALARII_PNU; e) Afiai coninurtul tabelului TOP_SALARII_PNU. f) Testai cazuri speciale, de genul n = 0 sau n mai mare dect numrul de angajai. Se vor elimina nregistrrile din tabelul TOP_SALARII_PNU dup fiecare test. Exerciii: [Cursoare cu parametru]

SGBD Laborator 4

6. S se declare un cursor cu un parametru de tipul codului departamentului, care regsete numele i salariul angajailor din departamentul respectiv, pentru care nu s-a specificat comisionul. S se declare o variabil v_nume de tipul unei linii a cursorului. S se declare dou tablouri de nume (v_tab_nume), respectiv salarii (v_tab_sal). S se parcurg liniile cursorului n dou moduri: regsindu-le n v_nume sau n cele dou variabile de tip tablou. DECLARE CURSOR c_nume (p_idDep employees.department_id%TYPE) IS SELECT last_name, salary FROM employees WHERE commission_pct IS NULL AND department_id = p_idDep; v_nume c_nume%ROWTYPE; -- sau /* TYPE t_nume IS RECORD (last_name employees.employee_id%TYPE, salary employees.salary%TYPE v_nume t_nume; */ TYPE t_tab_nume IS TABLE OF employees.last_name%TYPE; TYPE t_tab_sal IS TABLE OF employees.salary%TYPE; v_tab_nume t_tab_nume; v_tab_sal t_tab_sal; BEGIN IF c_nume%ISOPEN THEN CLOSE c_nume; END IF; OPEN c_nume (20); FETCH c_nume INTO v_nume; WHILE c_nume%FOUND LOOP DBMS_OUTPUT.PUT_LINE ( Nume: || v_nume.last_name || salariu : || v_nume.salary); FETCH c_nume INTO v_nume; END LOOP; CLOSE c_nume; -- eroare INVALID CURSOR -- FETCH c_nume INTO v_nume; DBMS_OUTPUT.PUT_LINE ( SI o varianta mai eficienta); OPEN c_nume (30); FETCH c_nume BULK COLLECT INTO v_tab_nume, v_tab_sal; CLOSE c_nume; FOR i IN v_tab_nume.FIRST..v_tab_nume.LAST LOOP DBMS_OUTPUT.PUT_LINE (i || Nume: || v_tab_nume(i) || salariu : || v_tab_sal(i)); END LOOP; END; / 7. S se rezolve exerciiul 6 utiliznd comanda LOOP. 8. S se rezolve exerciiul 6 folosind comanda FOR specific lucrului cu cursoare (ciclu cursor). Obs: La cursoare, comanda FOR realizeaz deschidere, ncrcare i nchidere automat. 9. Creai un tabel MESAJE_PNU avnd o coloan rezultat de tip VARCHAR2(75). Utiliznd un cursor parametrizat s se obin codurile angajailor din fiecare departament i pentru fiecare job. Rezultatele s fie inserate n tabelul MESAJE_PNU, sub forma cte unui ir de caractere obinut prin concatenarea valorilor celor 3 coloane.

SGBD Laborator 4

Exerciii: [FOR UPDATE, WHERE CURRENT OF]


10. S se dubleze valoarea salariilor celor angajai nainte de 1 ianuarie 1995, care nu ctig comision. DECLARE CURSOR before95 IS SELECT * FROM emp_pnu WHERE commission_pct IS NULL AND hire_date <= TO_DATE(01-JAN-1995,DD-MON-YYYY) FOR UPDATE OF salary NOWAIT; BEGIN FOR x IN before95 LOOP UPDATE emp_pnu SET salary = salary*2 WHERE CURRENT OF before95; END LOOP; -- ce efect ar avea urmatoarea comanda? Explicati. -- DBMS_OUTPUT.PUT_LINE(Au fost actualizate || before95%ROWCOUNT || linii); COMMIT; -- se permanentizeaza actiunea si se elibereaza blocarea END; / 11. S se declare un cursor cu un parametru de tipul coloanei location_id, care determin departamentele din locaia respectiv i blocheaz liniile pe perioada prelucrrii acestora. S se deschid cursorul folosind o variabil de substituie pentru furnizarea parametrului. S se actualizeze tabelul DEP_PNU, dnd valoarea 100 locaiei corespunztoare liniei curente a cursorului. 12. Modificati exemplul de mai sus astfel incat noua valoare a numelui departamentelor afectate de bloc sa fie vechea valoare la care se adauga numele locatiei date ca parametru. n PL/SQL a fost introdus variabila cursor, care este de tip referin. Variabilele cursor sunt similare tipului pointer din limbajele C sau Pascal. Prin urmare, un cursor este un obiect static, iar un cursor dinamic este un pointer la un cursor.

Pentru a crea o variabil cursor este necesar definirea unui tip REF CURSOR, urmnd apoi declararea unei variabile de tipul respectiv. Dup ce variabila cursor a fost declarat, ea poate fi deschis pentru orice cerere SQL care returneaz date de tipul declarat. Sintaxa pentru declararea variabilei cursor este urmtoarea: TYPE tip_ref_cursor IS REF CURSOR [RETURN tip_returnat]; var_cursor tip_ref_cursor;

Exerciii [Cursoare dinamice]


13. S se declare un cursor dinamic care ntoarce linii de tipul celor din tabelul EMP_PNU. S se citeasc o opiune de la utilizator, care va putea lua valorile 1, 2 sau 3. Pentru opiunea 1 deschidei cursorul astfel nct s regseasc toate informaiile din tabelul EMP_PNU, pentru opiunea 2, cursorul va regsi doar angajaii avnd salariul cuprins ntre 10000 i 20000, iar pentru opiunea 3 se vor regsi salariaii angajai n anul 1990. ACCEPT p_optiune PROMPT Introduceti optiunea (1,2 sau 3) DECLARE TYPE emp_tip IS REF CURSOR RETURN emp_pnu%ROWTYPE; V_emp emp_tip; V_optiune NUMBER := &p_optiune; BEGIN IF v_optiune = 1 THEN

SGBD Laborator 4

OPEN v_emp FOR SELECT * FROM emp_pnu; --!!! Introducei cod pentru afiare ELSIF v_optiune = 2 THEN OPEN v_emp FOR SELECT * FROM emp_pnu WHERE salary BETWEEN 10000 AND 20000; --!!! Introducei cod pentru afiare ELSIF v_optiune = 3 THEN OPEN v_emp FOR SELECT * FROM emp_pnu WHERE TO_CHAR(hire_date, YYYY) = 1990; --!!! Introducei cod pentru afiare ELSE DBMS_OUTPUT.PUT_LINE(Optiune incorecta); END IF; END; / 14. S se citeasc o valoare n de la tastatura. Prin intermediul unui cursor deschis cu ajutorul unui ir dinamic s se regseasc angajaii avnd salariul mai mare dect n. Pentru fiecare linie regsit de cursor, dac angajatul are comision, s se afieze numele su i salariul. n Oracle9i a fost introdus conceptul de expresie cursor (cursor expression), care returneaz un cursor imbricat (nested cursor). Expresia cursor are urmtoarea sintax: CURSOR (subcerere) Fiecare linie din mulimea rezultat poate conine valori uzuale i cursoare generate de subcereri. PL/SQL accept cereri care au expresii cursor n cadrul unei declaraii cursor, declaraii REF CURSOR i a variabilelor cursor.

Exerciii: [Expresii cursor]


15. S se listeze numele regiunilor i pentru fiecare regiune s se afieze numele rilor. Se cer 2 metode de rezolvare (secvenial i cu expresii cursor). Varianta cu expresii cursor: DECLARE CURSOR c_regiune IS SELECT region_name, CURSOR (SELECT country_name FROM countries c WHERE c.region_id = r.region_id) FROM regions r; v_regiune regions.region_name%TYPE; v_tara SYS_REFCURSOR; TYPE tara_nume IS TABLE OF countries.country_name%TYPE INDEX BY BINARY_INTEGER; v_nume_tara tara_nume; BEGIN OPEN c_regiune; LOOP FETCH c_regiune INTO v_regiune, v_tara; EXIT WHEN c_regiune%NOTFOUND; DBMS_OUTPUT.PUT_LINE (v_regiune); FETCH v_tara BULK COLLECT INTO v_nume_tara; FOR ind IN v_nume_tara.FIRST..v_nume_tara.LAST LOOP

SGBD Laborator 4

DBMS_OUTPUT.PUT_LINE (v_nume_tara (ind)); END LOOP; END LOOP; CLOSE c_regiune; END; /

Exerciii propuse:
1. Sa se afiseze departamentele si media salariilor pe departamente, in urmatoarea forma: - In departamentul <nume departament> media salariilor este <media>. 2. Sa se afiseze primii 5 salariati considerati in ordine alfabetica. 3. S se determine primii n cel mai bine pltii salariai, n ipoteza c avem valori duplicat. Exemplu : Dac primii angajati au salariile A 1000 B 700 C 700 D 800 Iar utilizatorul introduce n=2, se vor regsi A 1000 B 700 C 700 4. Scrieti o cerere pentru a regasi toate job-urile si salariatii practicnd fiecare job. Rezultatele se vor scrie in tabelul MESAJE. Se va folosi un cursor pentru a regasi codul job-ului si se va transmite acest cod unui cursor care regaseste salariatii avnd job-ul respectiv. 5. S se scrie un bloc PL/SQL care declar i utilizeaz un cursor cu parametri, astfel:ntr-o instruciune de ciclare, utilizai un cursor care regsete codul i numele departamentelor avnd codul mai mic dect 100. Transmitei ca parametru codul departamentului ctre alt cursor pentru regsirea, n tabelul employees, a numelui job-ului, datei angajrii i salariului angajailor avnd codul mai mic dect 20 i care lucreaz n acel departament. 6. a) Utilizatorul va putea specifica un cod de departament prin intermediul unei variabile de substituie p_dep. b) S se creeze un bloc PL/SQL care declar o variabil corespunztoare variabilei de substituie i un cursor, emp_cursor, pentru regsirea numelui, salariului i codului efului pentru angajaii din departamentul specificat n p_dep. Utilizai cursorul pentru a realiza urmtoarele: dac salariul angajatului este mai mic dect 5000 i codul efului su este 101 sau 124, s se afieze <nume> va fi propus pentru marire; altfel, se va afia <nume> nu va fi propus pentru mrire. 7. a) Creai un cursor care regsete salariul, prenumele i numele angajailor dintr-un departament al crui cod este introdus de utilizator. Utilizai clauza FOR UPDATE. Ce efect are aceasta? b) Se presupune c doar angajaii din departamentele 20, 60, 80, sau 100 pot fi propui pentru o mrire de salariu. S se verifice dac utilizatorul a introdus unul dintre aceste departamente. In caz afirmativ, se va deschide cursorul, altfel se va afia un mesaj corespunztor. c) utiliznd o expresie CASE , s se stabileasc urmtoarele: - salariile <6500 vor fi mrite cu 20% ; - salariile >6500 i <9500 vor fi mrite cu 15%; - salariile >9500 i <12000 vor fi mrite cu 8%; - salariile >12000 vor fi mrite cu 3%.

S-ar putea să vă placă și