Documente Academic
Documente Profesional
Documente Cultură
CURSORUL ÎN PL/SQL
Atunci când se execută o comandă SQL, Oracle Server deschide o zonă de memorie (context
area) în care comanda este executată. Cursorul este un pointer către această zonă.
În PL/SQL se utilizează două tipuri de cursoare:
implicit: declarat pentru toate instrucţiunile PL/SQL de tip LMD
explicit: declarat şi gestionat de programator.
CURSORUL IMPLICIT
Este declarat de PL/SQL implicit pentru toate comenzile de manipulare a datelor (INSERT,
UPDATE, DELETE, SELECT);
Dacă o instrucțiune LMD nu afectează nicio linie a tabelei, NU se generează automat o
excepție;
Atributele cursorului implicit:
SQL%ROWCOUNT
SQL%FOUND
SQL%NOTFOUND
SQL%ISOPEN este mereu FALSE
Atributele se referă la cea mai recentă instrucțiune LMD. Înaintea primei instrucțiuni LMD
din bloc, toate atributele au valoarea NULL;
După o instrucțiune COMMIT sau ROLLBACK, SQL%ROWCOUNT are valoarea 0.
Atenție - Cursorul implicit NU este util pentru instrucțiunea SELECT folosită cu INTO. Aceasta
va rula cu succes doar dacă interogarea returnează un singur rând și atunci SQL%ROWCOUNT
va fi 1, SQL%FOUND va fi TRUE iar SQL%NOTFOUND va fi FALSE. Dacă interogarea nu
returnează niciun rând sau returnează două sau mai multe, se va ridica o excepție
(NO_DATA_FOUND sau TOO_MANY_ROWS – vezi seminarul 4).
SET SERVEROUTPUT ON
BEGIN
DELETE FROM produse
WHERE categorie='hardware3' AND
id_produs NOT IN (SELECT id_produs FROM rand_comenzi);
DBMS_OUTPUT.PUT_LINE (SQL%ROWCOUNT || ' randuri sterse');
ROLLBACK;
DBMS_OUTPUT.PUT_LINE (SQL%ROWCOUNT || ' randuri afectate');
END;
/
După ROLLBACK, atributul SQL%ROWCOUNT devine 0. Rezultatul rulării este:
10 randuri sterse
0 randuri afectate
1
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
CURSORUL EXPLICIT
se foloseşte pentru a procesa individual fiecare linie (înregistrare) returnată de o instrucţiune
SELECT ce returnează mai multe înregistrări.
mulţimea înregistrărilor returnate de o instructiune SELECT este numită mulţime rezultat.
cursorul păstrează un pointer către linia curentă în cadrul unei mulţimi rezultat.
Verificarea stării unui cursor explicit se realizează prin intermediul următoarelor atribute:
NUME_CURSOR%ISOPEN - evaluat la TRUE în cazul în care cursorul este
deschis;
NUME_CURSOR%NOTFOUND - evaluat la TRUE în cazul în care cel mai
recent FETCH nu a returnat nici o linie;
NUME_CURSOR%FOUND - complementul lui %NOTFOUND;
NUME_CURSOR%ROWCOUNT - are ca valoare numărul liniilor returnate
până în momentul curent.
3
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
END;
DECLARE
v_id angajati.id_angajat%type;
v_nume angajati.nume%type;
CURSOR c1 IS SELECT id_angajat, nume FROM angajati;
BEGIN
OPEN c1;
FOR i IN 1..5 LOOP
FETCH c1 INTO v_id, v_nume;
DBMS_OUTPUT.PUT_LINE('Salariatul '||v_id||' are numele: '||v_nume);
END LOOP;
CLOSE c1;
END;
/
DECLARE
v_id angajati.id_angajat%type;
v_nume angajati.nume%type;
CURSOR c1 IS SELECT id_angajat, nume FROM angajati;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_id, v_nume;
EXIT WHEN c1%ROWCOUNT>5 OR c1%NOTFOUND;
INSERT INTO mesaje VALUES (v_id, v_nume);
END LOOP;
CLOSE c1;
END;
/
4
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
În acest caz, tipul record nu trebuie declarat. Se realizează în mod implicit deschiderea,
încărcarea și închiderea cursorului.
DECLARE
CURSOR ang_cursor IS SELECT id_angajat, nume, salariul
FROM angajati WHERE id_departament=60;
BEGIN
DBMS_OUTPUT.PUT_LINE('Lista cu salariariile angajatilor din departamentul 60');
FOR ang_rec IN ang_cursor LOOP
DBMS_OUTPUT.PUT_LINE('Salariatul '||ang_rec.nume||' are salariul: '||
ang_rec.salariul);
END LOOP;
END;
/
DECLARE
CURSOR c_com IS SELECT c.nr_comanda, c.data, COUNT(r.id_produs) numar
FROM comenzi c, rand_comenzi r
WHERE c.nr_comanda=r.nr_comanda
GROUP BY c.nr_comanda, c.data
ORDER BY COUNT(r.id_produs) DESC;
BEGIN
DBMS_OUTPUT.PUT_LINE('Numarul de produse pentru fiecare comanda:');
FOR rec_com IN c_com LOOP
EXIT WHEN c_com%ROWCOUNT>3;
DBMS_OUTPUT.PUT_LINE('Comanda '||rec_com.nr_comanda||' data pe '||
rec_com.data||' are: '||rec_com.numar||' produse');
END LOOP;
END;
/
TRATAREA EXCEPŢIILOR
O excepţie este un identificator PL/SQL asociat unei condiţii anormale apărute în timpul
execuţiei unui bloc PL/SQL. Apariția unei excepții are ca efect terminarea blocului, deci ieşirea
din blocul PL/SQL. Pentru evitarea unor situaţii de întrerupere anormală, excepţia poate fi
captată și poate fi specificată o rutină de tratare a acesteia. Atenție: a nu se confunda o excepție
cu o eroare de sintaxă. Erorile de sintaxă nu pot fi captate și tratate în partea de EXCEPTION.
O excepție poate fi invocată (ridicată) în doua moduri:
5
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
Tipuri de excepţii
Sunt trei tipuri de excepţii:
EXCEPTION
WHEN exception1 [OR exception2 …] THEN
statement1 ;
statement2 ;
…
[WHEN exception3 [OR exception4 …] THEN
statement1 ;
statement2 ;
…]
[WHEN OTHERS THEN
statement1 ;
statement2 ;
…]
6
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
Acestea nu trebuie declarate, fiind definite de către Oracle Server si invocate implicit. Lista
completă a excepţiilor predefinite Oracle va fi consultată din PL/SQL Reference, capitolul 10,
pagina 10-4 (http://bd.ase.ro/uploads/sgbd_activitate/PL_Reference.pdf ) cât și la finalul acestui
seminar. Câteva exemple de excepţii predefinite sunt prezentate mai jos:
Exemple:
Să se afişeze angajatul cu codul 10. Să se trateze eroarea apărută în cazul în care nu există nici
un angajat cu acest cod.
SET SERVEROUTPUT ON
DECLARE
v_nume VARCHAR2(20);
BEGIN
SELECT nume INTO v_nume
FROM angajati
WHERE id_angajat=10;
dbms_output.put_line(v_nume);
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Nu exista angajatul cu acest ID!');
END;
/
Să se afişeze salariul angajatului cu prenumele John. Să se trateze eroare apărută în cazul în care
există mai mulţi angajaţi cu acelaşi nume (interogarea SQL din bloc întoarce mai multe
înregistrări).
SET SERVEROUTPUT ON
DECLARE
sal angajati.salariul%type;
BEGIN
select salariul into sal from angajati where prenume='John';
DBMS_OUTPUT.PUT_LINE('John are salariul de: '||sal);
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Exista mai multi salariati cu numele John! Utilizati un
cursor pentru selectie!');
7
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
END;
/
Exemplu:
Să se insereze în tabela departamente un nou departament cu ID-ul 200, fără a preciza denumirea
acestuia. În acest caz va apare o eroarea cu codul ORA-01400 prin care programatorul este
avertizat de încălcarea unei restricţii de integritate. Această excepţie poate fi tratată astfel:
SET SERVEROUTPUT ON
DECLARE
-- se asociază un nume codului de eroare apărut
INSERT_EXCEPT EXCEPTION;
PRAGMA EXCEPTION_INIT(INSERT_EXCEPT, -01400);
BEGIN
insert into departments (department_id, department_name) values (200, NULL);
EXCEPTION
--se tratează eroarea prin numele său
WHEN insert_except THEN
DBMS_OUTPUT.PUT_LINE('Nu ati precizat informatii suficiente pentru
departament');
--se afişează mesajul erorii
DBMS_OUTPUT.PUT_LINE(SQLERRM);
8
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
END;
/
Să se şteargă toate înregistrările din tabela PRODUSE. Acest lucru va duce la apariţia erorii cu
codul –2292, reprezentând încălcarea restricţiei referenţiale. Valorile SQLCODE şi SQLERRM
vor fi inserate în tabela ERORI. ATENTIE! Aceste variabile nu se pot utiliza direct într-o
comandă SQL (cum ar fi SELECT, INSERT, UPDATE sau DELETE), drept pentru care vor fi
încărcate mai întâi in variabilele PL/SQL COD și MESAJ și apoi utilizate în instrucţiuni SQL.
DECLARE
cod NUMBER;
mesaj VARCHAR2(255);
del_exception EXCEPTION;
PRAGMA EXCEPTION_INIT(del_exception, -2292);
BEGIN
DELETE FROM produse;
EXCEPTION
WHEN del_exception THEN
dbms_output.put_line('Nu puteti sterge produsul');
dbms_output.put_line('Exista comenzi asignate lui');
cod:=SQLCODE;
mesaj:=SQLERRM;
INSERT INTO erori VALUES(USER, SYSDATE, cod, mesaj);
END;
/
SELECT * FROM erori;
In PL/SQL se pot defini excepții de către utilizator. Ele pot fi declarate în secțiunea
declarativa a blocului şi invocate explicit prin instrucțiunea RAISE sau
RAISE_APPLICATION_ERROR.
Etape:
1. Se declara excepţia în secţiunea declarativă:
nume_exceptie EXCEPTION;
9
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
Exemple:
Să se invoce o excepție în cazul în care utilizatorul încearcă să execute blocul PL/SQL după ora
17.
SET SERVEROUTPUT ON
DECLARE
e_exc1 EXCEPTION;
BEGIN
IF TO_NUMBER(TO_CHAR(SYSDATE, 'HH24'))>=17 THEN
RAISE e_exc1;
END IF;
EXCEPTION
WHEN e_exc1 THEN
dbms_output.put_line('Este ora '||TO_CHAR(SYSDATE, 'HH24'));
dbms_output.put_line('Operatiune permisa doar '||' in timpul programului');
END;
/
DECLARE
invalid_prod EXCEPTION;
BEGIN
UPDATE produse
SET denumire_produs='Laptop ABC'
WHERE id_produs=3;
IF SQL%NOTFOUND THEN
RAISE invalid_prod;
END IF;
EXCEPTION
WHEN invalid_prod THEN
DBMS_OUTPUT.PUT_LINE('Nu exista produsul cu acest ID');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('A aparut o eroare! Nu se poate actualiza denumirea
produsului!');
END;
/
10
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
Putem invoca în mod explicit și excepții pre-definite. In exemplul următor este invocată excepția
NO_DATA_FOUND:
DECLARE
invalid_prod EXCEPTION;
BEGIN
UPDATE produse
SET denumire_produs='Laptop ABC'
WHERE id_produs=3;
IF SQL%NOTFOUND THEN
RAISE NO_DATA_FOUND;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/
Clauza OTHERS este similară clauzei ELSE dintr-o instrucțiune CASE. Se folosește mereu
ultima în partea EXCEPTIONS a unui bloc PL/SQL și captează toate excepțiile, fie ele
predefinite, non-predefinite sau definite de utilizator care nu sunt explicit tratate anterior.
Propagarea excepţiilor
11
Facultatea de Cibernetică, Statistică şi Informatică Economică
SGBD Oracle, opțional – seminarul 4
TEMĂ
Comenzile se vor testa în SQL Developer și apoi vor fi salvate într-un fișier Word, alături de
capturi de ecran justificative (care să evidențieze rezultatele blocurilor):
Folosind tabelele bazei de date de la proiect, să se creeze 4 blocuri PL/SQL care să utilizeze
cursori și excepții (de toate tipurile)
12