Sunteți pe pagina 1din 11

SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

ORACLE Application Express


9. CURSORI
Scopul central al Oracle PL/SQL este de ușura și de a eficientiza pe cât posibil interogarea și
modificarea conținutului tabelelor într-o bază de date. În acest sens, Oracle oferă dezvoltatorilor
posibilitatea de a utiliza cursori specifici.

Atunci când se execută o comandă SQL, Oracle Server deschide o zonă de memorie (eng. context
area) în care este executată comanda. Un cursor reprezintă un pointer către acea zonă SQL privată
care stochează informații despre procesarea unei instrucțiuni SELECT sau a altor instrucțiuni folosite
pentru manipularea datelor (INSERT, UPDATE, DELETE). Practic, cursoarele reprezintă structuri care
permit utilizatorului să denumească zona privată de memorie care urmează să stocheze o anumită
instrucțiune, în vederea utilizarii ei ulterioare.

Numărul de linii returnate de o interogare poate fi zero, una sau mai multe, în funcție de condițiile
de căutare ale interogării. În Oracle, cursoarele sunt destinate procesării linie cu linie a seturilor de
rezultate multi-linie. În lipsa cursoarelor, proiectantul de aplicatii Oracle ar trebui sa preia si sa
gestioneze în mod explicit fiecare linie selectată de interogare.

Ne putem imagina un cursor ca reprezentând un fișier care urmează să fie procesat de la început
până la sfârșit, înregistrare cu înregistrare. Cursoarele sunt folosite pentru a procesa linie cu linie
seturi de rezultate multi-linie. De asemenea, cursoarele țin evidența liniei curente accesate, ceea ce
permite procesarea interactivă a setului activ.

În PL/SQL se utilizează două tipuri de cursoare:


 CURSOR IMPLICIT: declarat pentru toate instrucţiunile PL/SQL de tip DML;
 CURSOR EXPLICIT: declarat şi gestionat de programator.

9.1 Cursorul implicit

Cursorul implicit este declarat de PL/SQL pentru toate comenzile de manipulare a datelor. Dacă o
instrucţiune DML nu afectează nici o linie a tabelei nu se generează eroare, însă excepţia trebuie
tratată folosind atributele specifice ale cursorului implicit (a se vedea secțiunea Important! din sub-
capitolul 7.1 – pagina 7):
 %ROWCOUNT, care este un atribut de tip întreg şi reprezintă numărul liniilor încărcate de cursor;
 %FOUND, este un atribut de tip boolean care ia valoarea TRUE dacă ultima operaţie de încărcare
(FETCH) dintr-un cursor a avut succes (în cazul cursoarelor explicite) sau dacă instrucţiunea SQL a
returnat cel puţin o linie ( în cazul cursoarelor implicite );
 %NOTFOUND, care este un atribut de tip boolean şi are semnificaţie opusă faţă de cea a
atributului %FOUND;

Asist.Univ.Drd. Bogoslov Ioana Andreea 1


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

Lucrări aplicative:
 Se încearcă modificarea denumirii produsului cu codul 12, iar în cazul în care acest produs nu
există (comanda update nu realizează nici o modificare) va fi afişat un mesaj corespunzător.

Rezolvare:
BEGIN
UPDATE produse
SET denumire='Vin alb'
WHERE cod_produs=12;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Nu exista produsul cu acest cod');
END IF;
END;

 Să se dubleze prețul pentru toate produsele fabricate înainte de anul 2005 și să se afișeze
numărul de înregistrări actualizate.

Rezolvare:
DECLARE nr_actualizari number (2);
BEGIN
UPDATE produse
SET pret=pret*2
WHERE an_fabricatie<2005;
nr_actualizari:=SQL%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE('Au fost actualizate preturile a ' || nr_actualizari ||' produse');
END;

9.2 Cursorul explicit

Cursorul explicit se foloseşte pentru a procesa individual fiecare linie (înregistrare) a unei instrucţiuni
SELECT ce returnează mai multe înregistrări. Mulţimea înregistrărilor returnate de o instrucțiune
SELECT este numită mulţime rezultat, iar 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 cu TRUE în cazul în care cursorul este deschis;
 nume_cursor %NOTFOUND - evaluat cu 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.

Asist.Univ.Drd. Bogoslov Ioana Andreea 2


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

Prelucrarea cursorului explicit presupune parcurgerea următoarelor etape:

 DECLARAREA VARIABILELOR: se declară variabilele în care vor fi încărcate valorile


corespunzătoare unei linii din cursor;

 DECLARAREA CURSORULUI: se declară cursorul explicit, specificându-se un nume pentru acesta


şi definindu-se interogarea de procesat în cadrul lui:
DECLARE nume_cursor IS SELECT........................;

 DESCHIDEREA CURSORULUI: se deschide cursorul prin intermediul instrucţiunii OPEN, care


execută interogarea şi legarea tuturor variabilelor referite. Înregistrările returnate de interogare
sunt desemnate drept set activ de date, care pot fi de acum încărcate.
OPEN nume_cursor;

 ÎNCĂRCAREA LINIEI: utilizându-se instrucţiunea FETCH, se încarcă linia curentă din cursor în
variabile. Fiecare încărcare determină mutarea pointerului cursorului la linia următoare din setul
activ de date.
FETCH nume_cursor INTO var1, var2,..............;

 ÎNCHIDEREA CURSORULUI: este închis cursorul prin instructiunea CLOSE, care dezactivează setul
activ de linii. Cursorul poate fi din nou deschis pentru a stabili un nou set activ de linii.
CLOSE nume_cursor;

Lucrări aplicative:
 Să se afişeze lista cu numele şi salariul angajaţilor din Sibiu, prin utilizarea unui cursor explicit.

Rezolvare:
DECLARE
cursor ang_cursor is select cod_angajat, nume, salariu from angajati where localitate='Sibiu';
cod_ang angajati.cod_angajat%type;
v_nume angajati.nume%type;
v_sal angajati.salariu%type;
BEGIN
dbms_output.put_line('Lista cu salariariile angajatilor din Sibiu:');
OPEN ang_cursor;
LOOP
FETCH ang_cursor INTO cod_ang, v_nume, v_sal;
EXIT WHEN ang_cursor%notfound;
dbms_output.put_line('Salariatul '||v_nume||' are salariul: '||v_sal);
END LOOP;
CLOSE ang_cursor;
END;

Asist.Univ.Drd. Bogoslov Ioana Andreea 3


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

 Să se diminueze cu 50% prețul pentru toate vinurile fabricate înainte de anul 2005.
Rezolvare:
DECLARE CURSOR dublare IS SELECT * FROM produse WHERE an_fabricatie<2005
FOR UPDATE OF pret NOWAIT;
BEGIN
FOR x IN dublare LOOP
UPDATE produse
SET pret = pret*0.5
WHERE CURRENT OF dublare;
END LOOP;
COMMIT;
END;

Important! – SELECT FOR UPDATE/WHERE CURRENT OF


Instrucțiunea SELECT FOR UPDATE vă permite să blocați înregistrările din setul de rezultate al
cursorului. Nu vi se cere să faceți modificări în înregistrări pentru a utiliza această declarație.
Înregistrările blocate se eliberează la emiterea următoarei instrucțiuni de comitere sau revocare.

Sintaxă generală:
CURSOR denumire_cursor
IS
instrucțiune_SELECT
FOR UPDATE [of listă_de_coloane] [NOWAIT];
Unde:
- denumire_cursor: numele cursorului.
- instrucțiune_SELECT: o instrucțiune SELECT care va umple setul de rezultate al cursorului.
- listă_de_coloane: coloanele din rezultatul cursorului pe care doriți să le actualizați.
- NOWAIT: opțional - cursorul nu așteaptă resurse.

Dacă intenționați să actualizați sau să ștergeți înregistrări care au fost menționate printr-o
instrucțiune SELECT FOR UPDATE, puteți utiliza instrucțiunea WHERE CURRENT OF. Instrucțiunea
WHERE CURRENT OF vă permite să actualizați sau să ștergeți înregistrarea încărcată ultima dată de
cursor.

Sintaxă generală:
UPDATE denumire_tabelă
SET condiție
WHERE CURRENT OF denumire_cursor;

SAU

DELETE FROM denumire_tabelă


WHERE CURRENT OF denumire_cursor;

Asist.Univ.Drd. Bogoslov Ioana Andreea 4


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

 Să se realizeze un cursor cu ajutorul căruia să se afișeze numele, prenumele, funcția si


localitatea pentru fiecare angajat al cărui salariu este mai mare ca 2000 RON sub formă de
propoziție specifică.

Rezolvare:
DECLARE CURSOR c_angajati IS SELECT nume, prenume, functie, localitate FROM angajati WHERE
salariu>2000;
v_nume angajati.nume%TYPE;
v_prenume angajati.prenume%TYPE;
v_functie angajati.functie%TYPE;
v_loc angajati.localitate%TYPE;
BEGIN
dbms_output.put_line('Lista cu functiile angajatilor care au salariul mai mare decat 2000 RON:');
OPEN c_angajati;
LOOP
FETCH c_angajati INTO v_nume, v_prenume, v_functie, v_loc;
EXIT WHEN c_angajati%notfound;
dbms_output.put_line('Salariatul '||v_nume||' ' ||v_prenume|| ' are functia de: '|| v_functie
|| ' si este din localitatea: ' || v_loc);
END LOOP;
close c_angajati;
END;

 Realizați un cursor cu ajutorul căruia să fie afișate produsele cu prețurile mai mici ca prețul
mediu.

Rezolvare:
DECLARE CURSOR pret (i NUMBER) IS SELECT cod_produs, pret FROM produse;
pret_mediu produse.pret%TYPE;
v_pret NUMBER (8);
v_produse pret%rowtype;

BEGIN
select avg(pret) INTO pret_mediu FROM produse;
v_pret:=pret_mediu;
DBMS_OUTPUT.PUT_LINE('Produsele al caror pret este mai mic decat '|| v_pret);

IF NOT pret%ISOPEN THEN


OPEN pret (v_pret);
END IF;

LOOP
FETCH pret into v_produse;
Asist.Univ.Drd. Bogoslov Ioana Andreea 5
SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

EXIT WHEN pret%notfound;

If v_produse.pret<v_pret then
DBMS_OUTPUT.PUT_LINE('Produsul '||v_produse.cod_produs||' are pretul '|| v_produse.pret);
END if;
END LOOP;
CLOSE pret;
END;

10. TRATAREA EXCEPTIILOR


În Oracle, o excepţie reprezintă un identificator PL/SQL asociat unei condiţii anormale apărute în
timpul execuţiei unui bloc PL/SQL. Invocarea unei excepţii are ca efect terminarea blocului, prin
ieşirea din blocul PL/SQL. Pentru evitarea unor situaţii de întrerupere anormală, excepţia poate fi
captată, existând posibilitatea de a fi specificată o rutină de tratare a acesteia.

Există două metode prin care este determinată apariția unei excepții:

 În momentul în care se produce o eroare Oracle, excepția asociată este declanșată automat.
 Putem declansa o excepție explicită prin folosirea declarației RAISE în cadrul unui bloc.

Tratarea tuturor tipurilor de excepții, se realizează în secțiunea EXCEPTION (opțională) a blocurilor


PL/SQL, în conformitate cu sintaxa de mai jos.

Sintaxă generală:
EXCEPTION WHEN exceptie1 [OR exceptie2 …]
THEN instructiuni_de_executat;
[WHEN exceptie3 [OR exceptie4 …] THEN instructiuni_de_executat …]
[WHEN OTHERS THEN instructiuni_de_executat…]

10.1 Tipuri de exceptii

Tip exceptie Exemplu Mod de manipulare


Excepțiii predefinite NO_DATA_ FOUND, Nu trebuie declarate, serverul
asociate erorilor care apar cel TOO_MANY_ROWS, Oracle le invocă în mod
mai frecvent în blocurile INVALID_CURSOR, automat, dar trebuie tratate în
PL/SQL ZERO_DIVIDE secţiunea EXCEPTION.
Excepţii non-predefinite
Trebuie declarate în secţiunea
recunoscute de Oracle, dar
ORA- 01400 declarativă. Serverul Oracle le
tratate de utilizator cu ajutorul
invocă în mod automat, dar
codului de eroare returnat
Asist.Univ.Drd. Bogoslov Ioana Andreea 6
SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

trebuie tratate în secţiune


EXCEPTION.
Trebuie declarate în secţiunea
Excepţii definite de utilizator De exemplu cazul în care
declarativă, invocate de către
asociate unor condiţii specifice valoarea stocului unui anumit
utilizator şi tratate în secţiunea
de prelucrare produs este zero.
EXCEPTION.

10.2 Tratarea exceptiilor predefinite

PL/SQL declară excepții predefinite la nivel global în pachetul STANDARD, care definește mediul
PL/SQL. Prin urmare, acestea nu trebuie declarate de către dezvoltatori. Printre cele mai des întâlnite
excepții predefinite, amintim următoarele:

Denumire predefinită Explicatie


Asignarea unei valori unui atribut al unui obiect
ACCESS_INTO_NULL
neinițializat.
Instrucțiune CASE fără ELSE, pentru care nici una dintre
CASE_NOT_FOUND
etichete nu este egală cu valoarea căutată.
Aplicarea unei alte metode decat EXISTS unui TABLE sau
COLLECTION_IS_NULL
VARRAY neinițializat.
CURSOR_ALLREADY_OPEN Încercare de deschidere a unui cursor deja deschis.
Inserarea unei valori duplicat pe o coloană unde acest
DUP_VAL_ON_INDEX
lucru nu este permis (PRIMARY KEY, UNIQUE, etc).
INVALID_CURSOR Operație cursor invalidă.
INVALID_NUMBER Eroare conversie de la șir de caractere la număr.
LOGIN_DENIED Autentificare Oracle cu un username/parolă invalide.
Cererea SELECT care ar fi trebuit să întoarcă cel puțin o
NO_DATA_FOUND
linie, nu întoarce nici o linie.
Programul PL/SQL execută un acces la server fără să fie
NOT_LOGGED_ON
realizată în prealabil conexiunea cu acesta.
PROGRAM_ERROR Eroare internă PL/SQL.
Diferență de tip între două cursoare (unul gazdă, altul
ROWTYPE_MISMATCH
PL/SQL) implicate într-o asignare
Programul apelează o metodă MEMBER pe o instanță
SELF_IS_NULL (*) nulă. În acest caz, parametrul implicit SELF - primul
transmis unei metode MEMBER - este nul.
Depășire memorie pentru PL/SQL sau memoria este
STORAGE_ERROR
coruptă.
Referință la o tabelă imbricată sau un obiect de tip
SUBSCRIPT_BEYOND_COUNT VARRAY printr-un index mai mare decât numărul de
elemente al colecției.

Asist.Univ.Drd. Bogoslov Ioana Andreea 7


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

Referință la o tabelă imbricată sau un obiect de tip


SUBSCRIPT_BEYOND_LIMIT
VARRAY printr-un index ilegal (de exemplu -1).
Conversia unui șir de caractere la tipul UROWID a întors
SYS_INVALID_ROWID (*)
o eroare deoarece șirul nu este un UROWID valid.
Time-out la încercarea de accesare a unei resurse de
TIMEOUT_ON_RESOURCE
către Oracle.
Cererea SELECT care ar fi trebuit să întoarcă o singură
TOO_MANY_ROWS
linie, întoarce mai multe linii.
Erori aritmetice, de conversie, de trunchiere sau de
VALUE_ERROR
violare a unei constrângeri privind o valoare.
ZERO_DIVIDE Impărțirea se face la 0.

Lucrări aplicative:

 Să se afișeze angajatul cu numele Zamfirescu. În cazul în care nu există nici un angajat cu acest
nume să se afișeze mesajul: Ne pare rău, nu există angajatul cu acest nume!.

Rezolvare:
DECLARE v_nume angajati.nume%TYPE;
BEGIN
SELECT nume INTO v_nume FROM angajati
WHERE nume='Zamfirescu';
dbms_output.put_line(v_nume);
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Ne pare rau, nu exista angajatul cu acest nume !');
END;

 Să se afișeze produsele din sortimentul Sec. În cazul în care există mai multe produse din acest
sortiment să se afișeze mesajul: Există mai multe produse din sortimentul ales.

Rezolvare:
DECLARE v_denumire VARCHAR2(30);
BEGIN
SELECT denumire INTO v_denumire FROM produse WHERE sortiment='Sec';
DBMS_OUTPUT.PUT_LINE('Vinul: '|| v_denumire );
EXCEPTION WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Exista mai multe produse din sortimentul ales.');
END;

Notă: Încercați exemplul anterior pentru produsele din sortimentul Demidulce.

Asist.Univ.Drd. Bogoslov Ioana Andreea 8


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

10.3 Tratarea exceptiilor non-predefinite

În cazul în care o eroare a serverului Oracle nu are asociată o excepție predefinită, aceasta poate fi
captată prin asocierea unui nume codului de eroare returnat sau folosind clauza WHEN OTHERS. În
cadrul PL/SQL, directiva EXCEPTION_INIT determină compilatorul să asocieze un nume de excepţie
unui număr (cod) de eroare standard a serverului Oracle. Aceasta permite referirea erorii standard
prin nume şi scrierea unei rutine de tratare a ei.

Important! – Pasii necesari în tratarea erorilor non-predefinite

Tratarea erorilor non-predefinite se realizează în trei paşi:

 Declararea excepţiei: se face în zona DECLARE a blocului, prin sintaxa:


NUME_EXCEPTIE EXCEPTION;

 Asocierea codului erorii cu excepţia declarată anterior: se realizează tot în zona DECLARE
prin utilizarea directivei de compilare EXCEPTION_INIT, prin sintaxa:
PRAGMA EXCEPTION_INIT(NUME_EXCEPTIE, COD_EROARE);
Unde COD_EROARE reprezintă un cod de eroare standard Oracle;

 Tratarea excepţiei: se face în zona EXCEPTION a blocului, prin sintaxa:


EXCEPTION WHEN NUME_EXCEPTIE THEN .........;

Important! – Atribute utilizate pentru gestionarea erorilor

În Oracle, pot fi utilizate două atribute pentru a gestiona erorile apărute:

 SQLCODE – returnează codul numeric al erorii și poate avea următoarele valori:

- 0: nu a apărut nici o excepţie;


- 1: este o excepţie definită de utilizator;
- +100: excepţia NO_DATA_FOUND;
- un număr negativ: o eroare Oracle Server;

 SQLERRM – returnează mesajul asociat erorii. Aceste atribute pot fi încărcate în variabile şi
inserate într-o tabelă de erori pentru vizualizare şi verificare ulterioară.

Lucrări aplicative:

 Să se insereze în tabela furnizori un nou furnizor cu codul 28, fără a preciza denumirea acestuia.
În acest caz va apărea o eroarea cu codul ORA-01400, prin care utilizatorul este avertizat de
încălcarea unei restricţii de integritate. Se cere tratarea excepției apărute prin afișarea unui
mesaj corespunzător.

Asist.Univ.Drd. Bogoslov Ioana Andreea 9


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

Rezolvare:
DECLARE
-- se asociază un nume (exceptie_inserare) codului de eroare apărut
exceptie_inserare EXCEPTION;
PRAGMA EXCEPTION_INIT(exceptie_inserare, -01400);
BEGIN
INSERT INTO furnizori (cod_furnizor, denumire)
VALUES (28, NULL);
EXCEPTION
--se tratează eroarea prin numele său
WHEN exceptie_inserare THEN
DBMS_OUTPUT.PUT_LINE('Nu ati precizat informatii suficiente pentru furnizorul adaugat.');
--se afişează mesajul erorii
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;

10.4 Tratarea exceptiilor definite de către utilizator

În PL/SQL, utilizatorii au posibilitatea de a defini propriile erori/excepții. Acestea trebuie declarate în


secţiunea declarativă a blocului şi invocate explicit prin instrucţiunea RAISE.

Important! – Etape în tratarea exceptiilor definite de către utilizator

 Declararea excepției în secțiunea declarativă, prin sintaxa:


nume_exceptie EXCEPTION;

 Prin instrucţiunea RAISE se invocă, în mod explicit, în cadrul secţiunii executabile, excepția
declarată anterior:
RAISE nume_exceptie;

 Se tratează excepția în rutina corespunzătoare din secţiunea de tratare a excepţiilor, prin


sintaxa:
WHEN nume_exceptie THEN......

Lucrări aplicative:

 Să se calculeze numărul furnizorilor din Sibiu, iar în cazul în care acesta este mai mic ca 5 să se
afișeze mesajul: Există prea puțini furnizori din Sibiu. Aceștia sunt în numar de… (numărul
furnizorilor).

Rezolvare:
DECLARE exceptie_furnizori EXCEPTION;
numar_furnizori NUMBER;
BEGIN

Asist.Univ.Drd. Bogoslov Ioana Andreea 10


SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III

SELECT count(*) INTO numar_furnizori FROM furnizori WHERE localitate LIKE '%Sibiu%';
IF (numar_furnizori < 5) THEN
RAISE exceptie_furnizori;
END IF;
EXCEPTION
WHEN exceptie_furnizori THEN
DBMS_OUTPUT.PUT_LINE('Exista prea putini furnizori din Sibiu. Acestia sunt in numar de '||
numar_furnizori);
END;

 Să se modifice denumirea produsului cu codul 32. Dacă nu se produce nici o actualizare


(valoarea atributului SQL%ROWCOUNT este 0) sau dacă apare o altă eroare (OTHERS) atunci
trebuie să se declanşeze o excepţie prin care să fie avertizat utilizatorul.

Rezolvare:
DECLARE
produs_invalid EXCEPTION;
BEGIN
UPDATE produse
SET denumire='Vin Rosu'
WHERE cod_produs=32;
IF SQL%NOTFOUND THEN
RAISE produs_invalid;
END IF;
EXCEPTION
WHEN produs_invalid THEN
DBMS_OUTPUT.PUT_LINE('Nu exista produsul cu acest cod');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('A aparut o eroare! Nu se poate actualiza denumirea produsului!');
END;

Asist.Univ.Drd. Bogoslov Ioana Andreea 11

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