Documente Academic
Documente Profesional
Documente Cultură
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.
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;
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;
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.
Î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;
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;
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
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);
LOOP
FETCH pret into v_produse;
Asist.Univ.Drd. Bogoslov Ioana Andreea 5
SISTEME INFORMATICE DE GESTIUNE | CIG, ANUL III
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;
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.
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…]
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:
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;
Î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.
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;
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.
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;
Prin instrucţiunea RAISE se invocă, în mod explicit, în cadrul secţiunii executabile, excepția
declarată anterior:
RAISE nume_exceptie;
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
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;
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;