0% au considerat acest document util (0 voturi)
84 vizualizări105 pagini

SGBD An3 Idd Sem2-1

Documentul prezintă concepte generale despre limbajul PL/SQL și modul în care acesta extinde SQL prin adăugarea de constructii specifice limbajelor procedurale. De asemenea, se explică rolul important al PL/SQL la nivelul serverului Oracle și al utilitarelor Oracle în dezvoltarea diverselor aplicații de baze de date.

Încărcat de

bogdan
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
84 vizualizări105 pagini

SGBD An3 Idd Sem2-1

Documentul prezintă concepte generale despre limbajul PL/SQL și modul în care acesta extinde SQL prin adăugarea de constructii specifice limbajelor procedurale. De asemenea, se explică rolul important al PL/SQL la nivelul serverului Oracle și al utilitarelor Oracle în dezvoltarea diverselor aplicații de baze de date.

Încărcat de

bogdan
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
Sunteți pe pagina 1/ 105

1

1. Concepte generale


Procedural Language/Structured Query Language (PL/SQL) este extensia procedurala a limbajului
SQL. PL/SQL este un limbaj de programare complex care asigura accesarea datelor unei baze de date relationale
orientate pe obiecte si permite gruparea unei multimi de comenzi ntr-un bloc unic de tratare a datelor. Un
program este format din unul sau mai multe blocuri care pot contine blocuri imbricate. Fiecare bloc logic
corespunde unei probleme sau subprobleme de rezolvat.
PL/SQL include att instructiuni SQL pentru prelucrarea datelor si gestiunea tranzactiilor, ct si
instructiuni proprii. Limbajul mbina constructiile procedurale ale unui limbaj LG3 cu puterea si flexibilitatea lui
SQL (LG4). Aceasta combinatie a generat un limbaj puternic pentru modelarea aplicatiilor complexe.
PL/SQL extinde SQL prin constructii specifice limbajelor procedurale (definirea variabilelor, declararea
tipurilor, utilizarea structurilor de control, implementarea subprogramelor, introducerea tipurilor obiect si a
metodelor etc.). PL/SQL ofera posibilitati moderne de tratare a informatiei: ncapsularea datelor, analiza speciala
a erorilor, mascarea informatiei, orientarea pe obiecte. Posibilitatile lui SQL sunt folosite pentru un acces rafinat
la date, iar facilitatile oferite de PL/SQL, pentru fluxul controlului procesarii datelor.
Dintre functionalitatile care determina utilizarea frecventa a limbajului PL/SQL, se remarca urmatoarele:
integrarea comenzilor SQL de baza;
integrarea cu server-ul Oracle si cu utilitarele Oracle;
oferirea unui suport pentru programarea orientata pe obiecte;
asigurarea securitatii informatiei;
definirea si gestiunea blocurilor de instructiuni;
gestiunea variabilelor, constantelor si cursoarelor;
modularizarea programelor (subprograme, pachete);
implementarea si utilizarea declansatorilor;
utilizarea structurilor de control fundamentale;
detectarea si gestiunea erorilor de executie, a situatiilor exceptionale;
dezvoltarea de aplicatii Web.
PL/SQL este o tehnologie utilizata de server-ul Oracle si de anumite utilitare Oracle. Blocurile PL/SQL
sunt transmise unui motor PL/SQL si procesate (compilate si executate) de acesta. Motorul PL/SQL se poate afla
pe server-ul Oracle sau ntr-un utilitar, iar utilizarea sa depinde de locul din care se invoca PL/SQL. Multe
utilitare Oracle au propriul lor motor PL/SQL care este independent de motorul prezent pe server-ul Oracle.
Blocurile PL/SQL pot fi executate pe statia client fara interactiune cu server-ul sau n ntregime pe
server. Atunci cnd blocurile PL/SQL sunt referite dintr-un program PRO*, din SQL*Plus sau de catre Server
Manager, motorul PL/SQL de pe server-ul Oracle proceseaza aceste blocuri. Acesta descompune blocul n
instructiuni SQL si le trimite executorului de instructiuni SQL (SQL Statement Executor) de pe server-ul Oracle.
Fara PL/SQL, instructiunile SQL ar fi procesate separat, fiecare la un moment dat, fiecare implicnd un apel la

PROGRAMARE N PL/SQL


2

server-ul Oracle. Restul comenzilor (procedurale) sunt procesate de catre executorul instructiunilor procedurale
(PSE Procedural Statement Executor) aflat n motorul PL/SQL. PSE proceseaza datele locale aplicatiei,
reducndu-se astfel activitatea de transfer spre server-ul Oracle si numarul de cursoare deschise. n felul acesta,
este necesar un singur transfer pentru a trimite blocul, din aplicatie catre server.
O aplicatie de baze de date poate fi structurata n trei parti:
interfata utilizator (utilizatorul introduce anumite informatii si obtine niste rezultate n urma executarii
aplicatiei);
aplicatia logica efectiva;
baza de date.
Exista doua arhitecturi pentru proiectarea unei aplicatii de baze de date: modelul clasic client/server (two-
tier) si modelul multitier (three-tier).
Multe dintre aplicatiile de baze de date sunt construite folosind arhitectura traditionala client/server
descrisa anterior pentru PL/SQL. Modelul este caracterizat de cele doua componente, client si server. Client-ul
asigura interfata cu utilizatorul, logica aplicatiilor si prelucrarii datelor. Server-ul se ocupa cu validarea datelor si
accesarea bazei de date. De remarcat caracteristica fundamentala a modelului, si anume faptul ca aplicatia
comunica direct cu server-ul. Exista un motor PL/SQL pe server, iar n anumite cazuri si pe client.
Daca motorul PL/SQL este pe server, atunci aplicatia (ce poate fi scrisa n Pro*C/C++, JDBC, OCI sau
n alte limbaje) care rezida pe client trimite cereri la un server de date. Cererile sunt rezolvate utiliznd SQL.
Diferite cereri SQL pot fi grupate ntr-un bloc PL/SQL si trimise server-ului ca o singura entitate, genernd
diminuarea traficului si cresterea rapiditatii aplicatiei.
Se considera un scenariu n care exista doua motoare PL/SQL, unul pe statia client (local) si unul pe
server (de exemplu, un declansator se executa pe statia client si apeleaza un subprogram stocat n baza de date).
n acest caz, blocurile anonime sunt trimise motorului PL/SQL de pe statia client, care proceseaza local
comenzile procedurale. Comenzile neprocedurale din interiorul blocului sunt trimise executorului de instructiuni
SQL de pe server. De asemenea, apelurile procedurilor care sunt stocate pe server sunt trimise pentru procesare
tot motorului de pe server.
Un mecanism similar este folosit pentru comunicarea dintre motoare PL/SQL aflate pe server-e diferite.
n acest caz, obiectele PL/SQL din diferite motoare pot, nsa, sa depinda unul de altul.
n modelul three-tier, aplicatia de baze de date este scindata n trei parti separate ce corespund interfetei
utilizator, logicii aplicatiei si bazei de date. Acestor trei straturi le corespund: Client Browser, Application Server
(Oracle Internet Application Server iAS) si Oracle Database Server. Client-ul (cel mai frecvent un browser
Internet) apeleaza subprograme care genereaza ca rezultat pagini HTML, iar Application Server proceseaza
solicitarile, manevrnd logica aplicatiilor si prelucrarii datelor. n versiunea Oracle9i, aplicatiile Forms si Reports
se executa numai ca parte a unui model three-tier (Web - Server). De exemplu, Oracle Web Forms permite
desfasurarea unei aplicatii client/server ntr-un mediu multitier Internet, fara a modifica nici o linie de cod.
n general, comenzile SQL si PL/SQL sunt trimise server-ului pentru a fi executate. Pentru a realiza acest
lucru, trebuie stabilita conectarea la baza de date. Aceasta presupune ca baza de date sa autentifice utilizatorul
(identificator si parola). PL/SQL nu contine o sintaxa speciala pentru realizarea conectarii la baza de date.
Conectarea poate fi realizata de alte componente ale sistemului (de exemplu, SQL*Plus).
PL/SQL are un rol important att la nivelul server-ului Oracle (prin subprograme stocate, declansatori si
pachete), ct si la nivelul utilitarelor Oracle (de exemplu, Developer Suite componenta declansatori). Partea
procedurala este att la nivel de client, ct si la nivel de server. Cererile, nsa, nu se executa pe statii client, ci
numai pe server. Cu toate ca au aceeasi structura generala, procedurile si functiile declarate ca parte a unei
aplicatii Developer Suite sunt diferite de cele stocate n baza de date. Subprogramele stocate sunt obiecte depuse
n dictionarul datelor, putnd fi accesate de un numar arbitrar de aplicatii, inclusiv de aplicatii Developer Suite.
PL/SQL constituie fundamentul pentru realizarea diverselor aplicatii de baze de date:
fisiere SQL*Plus (script file);


3

proceduri si functii stocate (stored procedure si stored function) - o procedura sau o functie stocata
este un subprogram PL/SQL care poate fi invocat de o aplicatie client, un declansator baza de date
sau un declansator aplicatie;
pachete (package) - un pachet este un bloc PL/SQL care ncapsuleaza ntr-o unitate logica, n baza
de date, o multime de proceduri, functii, variabile, constante, tipuri, cursoare si exceptii;
declansatori baza de date (database trigger) - un declansator la nivel de baza de date este un bloc
PL/SQL care se executa la aparitia unui eveniment (modificarea unui tabel al bazei, modificarea unei
vizualizari, anumite actiuni ale sistemului sau ale utilizatorului);
declansatori aplicatie (application trigger) - un declansator la nivel de aplicatie este un bloc PL/SQL
care se executa n urma unui eveniment provocat de sistem. Aplicatiile Oracle (Forms Developer si
Reports Developer) sunt dotate cu motoare PL/SQL care permit construirea acestor declansatori.
Referitor la portabilitatea limbajului, pot fi remarcate doua aspecte.
Aplicatiile PL/SQL se pot executa pe orice platforma sau sistem de operare pe care poate fi executat
Oracle.
PL/SQL permite transfer de cod ntre server-ul Oracle si aplicatii. Este posibila scrierea de pachete
program portabile si crearea de biblioteci utilizabile n diferite situatii, de diverse aplicatii.



2. Ce aduce nou Oracle9i?


Pentru Oracle8, cele mai spectaculoase realizari erau legate de introducerea tipurilor obiect si a
metodelor, definirea tipului colectie (tablouri imbricate si vectori), definirea si accesarea rutinelor externe,
utilizarea tipurilor LOB.
Dintre performantele versiunii Oracle8i se pot aminti: rutine externe Java, parametri NOCOPY, tranzactii
autonome, operatii bulk bind, SQL dinamic nativ.
Dintre noutatile (referitoare la PL/SQL) oferite de Oracle9i se remarca:
posibilitatea ca PL/SQL sa suporte sintaxa completa a comenzilor SQL;
introducerea unor noi tipuri de date n SQL si PL/SQL (TIMESTAMP, INTERVAL, colectii pe mai
multe niveluri);
introducerea de comenzi si expresii CASE;
definirea expresiilor cursor;
introducerea ierarhiilor de tipuri si subtipuri;
definirea functiilor tabel;
introducerea de pachete predefinite care ofera functionalitati speciale legate de noile caracteristici ale
versiunii;
compilarea nativa a codului PL/SQL;
extinderea posibilitatilor oferite de structura bulk bind si mbinarea acestei structuri cu facilitatile
oferite de SQL dinamic;
scrierea rapida de aplicatii C++ care au acces la un server Oracle, folosind o interfata API
(Application Programming Interface), numita Oracle C++ Call Interface (OCCI);
depunerea si executarea claselor Java n baza de date;
dezvoltarea de aplicatii Web.
Procedurile PL/SQL se executa mai rapid prin compilarea lor ntr-un cod nativ. Ele sunt translatate n cod
C, compilate cu ajutorul unui compilator C si apoi preluate automat n procese Oracle. Aceasta tehnica, care nu
impune restaurarea bazei de date, poate fi utilizata pentru procedurile si pachetele Oracle.


4

Performantele oferite de sistem pentru dezvoltarea aplicatiilor sunt legate de implementarea urmatoarelor
facilitati: SQL dinamic, structura bulk bind, argumentul NOCOPY, clauza RETURNING, rutine externe, tipuri
obiect si colectii, compilarea codului PL/SQL pentru executie nativa. Aceste posibilitati vor fi analizate n
urmatoarele capitole.
Sistemul Oracle furnizeaza solutii (interfete la nivel de client si server, utilitare, JVM integrata cu server-
ul Oracle9i) pentru dezvoltatorii de aplicatii n vederea crearii, gestionarii si exploatarii aplicatiilor Java.
Metodele Java stocate pot fi apelate din pachete PL/SQL, iar procedurile PL/SQL existente pot fi invocate din
proceduri Java. Datele SQL pot fi accesate prin doua interfete (API): JDBC si SQLJ. Astfel:
pentru a invoca o metoda Java din SQL este nevoie de interfata Java Stored Procedures;
pentru a invoca, n mod dinamic, comenzi SQL complexe este folosita interfata JDBC;
pentru a utiliza comenzi SQL statice, simple (referitoare la un tabel ale carui coloane sunt cunoscute)
referitor la o clasa Java este folosit SQLJ.
PL/SQL ofera facilitati care permit utilizarea Web-ului de catre baza de date si da posibilitatea ca datele
din baza de date sa fie accesibile interactiv diferitilor utilizatori Intranet. Aplicatiile Web scrise n PL/SQL sunt de
fapt proceduri stocate care interactioneaza, printr-un protocol HTTP, cu un browser Web.
n dezvoltarea de aplicatii Web cu PL/SQL se impun cteva probleme.
Cum se genereaza un rezultat HTML din PL/SQL?
Cum se pot trimite parametri unei aplicatii Web?
Cum se pot executa operatii Web (trimiterea unui e-mail, gasirea adresei unei gazde etc.) cu ajutorul
procedurilor stocate PL/SQL?
Cum se poate include cod PL/SQL n pagini Web?
Sistemul furnizeaza o clasa de pachete predefinite care usureaza dezvoltarea aplicatiilor Web. De
exemplu, pentru a trimite un e-mail dintr-un program PL/SQL sau dintr-o procedura stocata se utilizeaza
pachetul UTL_SMTP. De asemenea, pentru a gasi numele unei gazde locale sau distante a carei adresa este
cunoscuta sau pentru a gasi adresa unei gazde careia i se cunoaste numele poate fi utilizat pachetul
UTL_INADDR. Pachetele pot fi folosite mpreuna cu Oracle Internet Application Server si WebDB. n felul
acesta se poate formata rezultatul unei cereri ntr-un tabel HTML, se pot combina operatii Web tipice ntr-un
program PL/SQL etc.
PL/SQL Server Pages (PSP) permite crearea de pagini Web dinamice, prin care se pot executa operatii
asupra bazei de date si include rezultatele unei interogari n pagini Web, n mod dinamic. Folosind elemente
speciale, numite tag-uri, se pot insera script-uri PL/SQL n codul sursa HTML. Aceste script-uri se executa
atunci cnd paginile sunt solicitate de clienti Web. Script-ul permite accesarea unor parametri, filtrarea sau
reactualizarea bazei de date.
Pentru a include codul PL/SQL n pagini Web trebuie:
aleasa o configuratie software;
scris cod pentru PSP (prin care se creeaza pagini Web ce permit operatii asupra bazei de date);
ncarcate fisierele PSP n baza de date (ca proceduri stocate) si executate.
O aplicatie Web PL/SQL poate sa accepte date n format XML sau sa furnizeze rezultate n acest format.
Documentul XML poate fi procesat ca un document compus (depus ntr-un LOB) sau ca un document
descompus (fragmente de documente) depus n tabele relationale.



3. Controlul executiei unui bloc PL/SQL




5

PL/SQL este un limbaj cu structura de bloc, adica programele sunt compuse din blocuri care pot fi
complet separate sau imbricate. Structura unui bloc poate fi obtinuta combinnd subprograme, pachete, blocuri
imbricate. Blocurile pot fi folosite n utilitarele Oracle.
Pentru modularizarea unui program este necesara:
gruparea logica a instructiunilor n blocuri;
imbricarea de subblocuri n blocuri mai mari;
descompunerea unei probleme complexe ntr-o multime de module logice si implementarea acestora
cu ajutorul blocurilor;
plasarea n biblioteci a codului PL/SQL reutilizabil, de unde poate fi folosit de aplicatii;
depunerea codului ntr-un server Oracle, de unde este accesibil oricarei aplicatii care interactioneaza
cu baza de date Oracle.
Un program PL/SQL poate cuprinde unul sau mai multe blocuri. Un bloc poate fi anonim sau neanonim.
Blocurile anonime sunt blocuri PL/SQL fara nume, care sunt construite dinamic si sunt executate o
singura data. Acest tip de bloc nu are argumente si nu returneaza un rezultat. Ele sunt declarate ntr-un punct al
aplicatiei, unde vor fi executate (trimise motorului PL/SQL). n blocurile anonime pot fi declarate proceduri si
functii PL/SQL.
Blocurile anonime pot sa apara ntr-un program ce lucreaza cu precompilator sau n SQL*Plus. De
obicei, blocurile anonime sunt plasate ntr-un fisier, iar apoi fisierul este executat din SQL*Plus. De asemenea,
declansatorii din componentele Developer Suite constau din astfel de blocuri.
Blocurile neanonime sunt fie blocuri cu nume (etichetate) construite static sau dinamic si executate o
singura data, fie subprograme, pachete sau declansatori.
Subprogramele sunt proceduri sau functii depuse n baza de date. Aceste blocuri sunt executate de mai
multe ori si, n general, nu mai sunt modificate dupa ce au fost construite. Procedurile si functiile stocate sunt
depuse pe server-ul Oracle, accepta parametri si pot fi apelate prin nume. Procedurile si functiile aplicatie sunt
depuse ntr-o aplicatie Developer Suite sau ntr-o biblioteca.
Pachetele (stocate sau aplicatie) sunt blocuri neanonime care grupeaza proceduri, functii, cursoare, tipuri,
constante, variabile ntr-o unitate logica, n baza de date.
Declansatorii sunt blocuri PL/SQL neanonime depuse n baza de date, care pot fi asociati bazei, iar n
acest caz sunt executati implicit ori de cte ori apare un anumit eveniment declansator (de exemplu, instructiuni
INSERT, UPDATE sau DELETE ce se executa asupra unui tabel al bazei de date) sau pot fi asociati unei aplicatii
(de exemplu, declansator SQL*Forms), ceea ce presupune ca se executa automat, n functie de anumite conditii
sistem.
Structura unui bloc PL/SQL
Un bloc PL/SQL este compus din trei sectiuni distincte.
Sectiunea declarativa (optionala) contine declaratii pentru toate variabilele, constantele, cursoarele si
erorile definite de utilizator la care se face referinta n sectiunea executabila sau chiar n cea
declarativa. De asemenea, pot fi declarate subprograme locale care sunt vizibile doar n blocul
respectiv.
Sectiunea executabila contine instructiuni neprocedurale SQL pentru prelucrarea datelor din baza de
date si instructiuni PL/SQL pentru prelucrarea datelor n cadrul blocului.
Sectiunea pentru tratarea erorilor (optionala) specifica actiunile ce vor fi efectuate atunci cnd n
executia blocului apar erori sau conditii anormale.
Blocul PL/SQL are urmatoarea structura generala:
[<<nume_bloc>>]
[DECLARE
instructiuni de declarare]
BEGI N


6

instructiuni executabile (SQL sau PL/SQL)
[EXCEPTI ON
tratarea erorilor]
END [nume_bloc];
Daca blocul PL/SQL este executat fara erori, invariant va aparea mesajul:
PL/SQL procedure successfully completed
Compatibilitate SQL
Din punct de vedere al compatibilitatii dintre PL/SQL si SQL, se remarca urmatoarele reguli de baza:
PL/SQL furnizeaza toate comenzile LMD ale lui SQL, comanda SELECT cu clauza INTO, comenzile
LCD, functiile, pseudocoloanele si operatorii SQL;
PL/SQL nu furnizeaza comenzile LDD.
Totusi, n ultimele sale versiuni, Oracle permite folosirea dinamica a comenzilor SQL, utiliznd tehnica
oferita de SQL dinamic. n felul acesta, orice comanda SQL (inclusiv comanda LDD) poate sa fie utilizata n
PL/SQL.
Majoritatea functiilor SQL sunt disponibile n PL/SQL. Exista nsa functii specifice PL/SQL, cum sunt
functiile SQLCODE si SQLERRM. De asemenea, exista functii SQL care nu sunt disponibile n instructiuni
procedurale (DECODE, functiile grup), dar care sunt disponibile n instructiunile SQL dintr-un bloc PL/SQL.
SQL nu poate folosi functii sau atribute specifice PL/SQL.
Functiile grup trebuie folosite cu atentie, deoarece clauza GROUP BY nu are sens sa apara n
instructiunea SELECT INTO. Oracle9i introduce clauza OVER, care permite ca functia grup careia i este
asociata sa fie considerata o functie analitica (poate returna mai multe linii pentru fiecare grup).
Urmatoarele functii SQL nu sunt permise n PL/SQL: WIDTH_BUCKET, BIN_TO_NUM, COMPOSE,
DECOMPOSE, TO_LOB, DECODE, DUMP, EXISTSNODE, TREAT, NULLIF, SYS_CONNECT_BY_PATH,
SYS_DBURIGEN, EXTRACT.
Instructiuni PL/SQL
Orice program poate fi scris utiliznd structuri de control de baza care sunt combinate n diferite moduri
pentru rezolvarea problemei propuse. PL/SQL dispune de comenzi ce permit controlul executiei unui bloc.
Instructiunile limbajului pot fi: iterative (LOOP, WHILE, FOR), de atribuire (:=), conditionale (IF, CASE), de
salt (GOTO, EXIT) si instructiunea vida (NULL).
Observatii:
Comentariile sunt ignorate de compilatorul PL/SQL. Exista comentarii pe o singura linie, prefixate de
simbolurile --, care ncep n orice punct al liniei si se termina la sfrsitul acesteia. De asemenea,
exista comentarii pe mai multe linii, care sunt delimitate de simbolurile /* si */. Nu se admit
comentarii imbricate.
Caracterul ; este separator pentru instructiuni.
Att operatorii din PL/SQL, ct si ordinea de executie a acestora, sunt identici cu cei din SQL. n
PL/SQL este introdus un nou operator (**) pentru ridicare la putere.
Un identificator este vizibil n blocul n care este declarat si n toate subblocurile, procedurile si
functiile imbricate n acesta. Daca blocul nu gaseste identificatorul declarat local, atunci l cauta n
sectiunea declarativa a blocurilor care includ blocul respectiv si niciodata nu cauta n blocurile
ncuibarite n acesta.
Comenzile SQL*Plus nu pot sa apara ntr-un bloc PL/SQL.
n comanda SELECT trebuie specificate variabilele care recupereaza rezultatul actiunii acestei
comenzi. n clauza INTO, care este obligatorie, pot fi folosite variabile PL/SQL sau variabile de
legatura.


7

Referirea la o variabila de legatura se face prin prefixarea acesteia cu simbolul :.
Cererea dintr-o comanda SELECT trebuie sa returneze o singura linie drept rezultat. Atunci cnd
comanda SELECT ntoarce mai multe linii, apare eroarea TOO_MANY_ROWS, iar n cazul n care
comanda nu gaseste date se genereaza eroarea NO_DATA_FOUND.
Un bloc PL/SQL nu este o unitate tranzactionala. ntr-un bloc pot fi mai multe tranzactii sau blocul
poate face parte dintr-o tranzactie. Actiunile COMMIT, SAVEPOINT si ROLLBACK sunt
independente de blocuri, dar instructiunile asociate acestor actiuni pot fi folosite ntr-un bloc.
PL/SQL nu suporta comenzile GRANT si REVOKE, utilizarea lor fiind posibila doar prin SQL
dinamic.
Fluxul secvential de executie a comenzilor unui program PL/SQL poate fi modificat cu ajutorul
structurilor de control: IF, CASE, LOOP, FOR, WHILE, GOTO, EXIT.
Instructiunea de atribuire
Instructiunea de atribuire se realizeaza cu ajutorul operatorului de asignare (:=) si are forma generala
clasica (variabila := expresie). Comanda respecta proprietatile instructiunii de atribuire din clasa LG3. De
remarcat ca nu poate fi asignata valoarea null unei variabile care a fost declarata NOT NULL.
Exemplu:
Urmatorul exemplu prezinta modul n care actioneaza instructiunea de atribuire n cazul unor tipuri de
date particulare.
DECLARE
alfa INTERVAL YEAR TO MONTH;
BEGIN
alfa := INTERVAL '200-7' YEAR TO MONTH;
-- alfa ia valoarea 200 de ani si 7 luni
alfa := INTERVAL '200' YEAR;
-- pot fi specificati numai anii
alfa := INTERVAL '7' MONTH;
-- pot fi specificate numai lunile
alfa := '200-7';
-- conversie implicita din caracter
END;
Instructiunea IF
Un program PL/SQL poate executa diferite portiuni de cod, n functie de rezultatul unui test (predicat).
Instructiunile care realizeaza acest lucru sunt cele conditionale (IF, CASE).
Structura instructiunii IF n PL/SQL este similara instructiunii IF din alte limbaje procedurale, permitnd
efectuarea unor actiuni n mod selectiv, n functie de anumite conditii. Instructiunea IF-THEN-ELSIF are
urmatoarea forma sintactica:
IF conditie1 THEN
secventa_de_comenzi_1
[ELSI F conditie2 THEN
secventa_de_comenzi_2]

[ELSE
secventa_de_comenzi_n]
END I F;
O secventa de comenzi din IF este executata numai n cazul n care conditia asociata este TRUE. Atunci
cnd conditia este FALSE sau NULL, secventa nu este executata. Daca pe ramura THEN se doreste verificarea
unei alternative, se foloseste ramura ELSIF (atentie, nu ELSEIF) cu o noua conditie. Este permis un numar


8

arbitrar de optiuni ELSIF, dar poate aparea cel mult o clauza ELSE. Aceasta se refera la ultimul ELSIF.
Exemplu:
Sa se specifice daca o galerie este mare, medie sau mica dupa cum numarul operelor de arta expuse n
galeria respectiva este mai mare dect 200, cuprins ntre 100 si 200 sau mai mic dect 100.
SET SERVEROUTPUT ON
DEFINE p_cod_gal = 753
DECLARE
v_cod_galerie opera.cod_galerie%TYPE := &p_cod_gal;
v_numar NUMBER(3) := 0;
v_comentariu VARCHAR2(10);
BEGIN
SELECT COUNT(*)
INTO v_numar
FROM opera
WHERE cod_galerie = v_cod_galerie;
IF v_numar < 100 THEN
v_comentariu := 'mica';
ELSIF v_numar BETWEEN 100 AND 200 THEN
v_comentariu := 'medie';
ELSE
v_comentariu := 'mare';
END IF;
DBMS_OUTPUT.PUT_LINE('Galeria avand codul '||v_cod_galerie
||' este de tip '|| v_comentariu);
END;
/
SET SERVEROUTPUT OFF
Instructiunea CASE
Oracle9i furnizeaza o noua comanda (CASE) care permite implementarea unor conditii multiple.
Instructiunea are urmatoarea forma sintactica:
[<<eticheta>>]
CASE test_var
WHEN valoare_1 THEN secventa_de_comenzi_1;
WHEN valoare_2 THEN secventa_de_comenzi_2;

WHEN valoare_k THEN secventa_de_comenzi_k;
[ELSE alta_secventa;]
END CASE [eticheta];
Se va executa secventa_de_comenzi_p, daca valoarea selectorului test_var este valoare_p. Dupa ce este
executata secventa de comenzi, controlul va trece la urmatoarea instructiune dupa CASE. Selectorul test_var
poate fi o variabila sau o expresie complexa care poate contine chiar si apeluri de functii.
Clauza ELSE este optionala. Daca aceasta clauza este necesara n implementarea unei probleme, dar
totusi lipseste, iar test_var nu ia nici una dintre valorile ce apar n clauzele WHEN, atunci se declanseaza eroarea
predefinita CASE_NOT_FOUND (ORA - 06592).
Comanda CASE poate fi etichetata si, n acest caz, eticheta poate sa apara la sfrsitul clauzei END CASE.
De remarcat ca eticheta dupa END CASE este permisa numai n cazul n care comanda CASE este etichetata.
Selectorul test_var poate sa lipseasca din structura comenzii CASE, care n acest caz va avea urmatoarea
forma sintactica:
[<<eticheta>>]


9

CASE
WHEN conditie_1 THEN secventa_de_comenzi_1;
WHEN conditie_2 THEN secventa_de_comenzi_2;

WHEN conditie_k THEN secventa_de_comenzi_k;
[ELSE alta_secventa;]
END CASE [eticheta];
Fiecare clauza WHEN contine o expresie booleana. Daca valoarea lui conditie_p este TRUE, atunci este
executata secventa_de_comenzi_p.
Exemplu:
n functie de o valoare introdusa de utilizator, care reprezinta abrevierea zilelor unei saptamni, sa se
afiseze (n cele doua variante) un mesaj prin care este specificata ziua saptamnii corespunzatoare abrevierii
respective.
Varianta 1:
SET SERVEROUTPUT ON
DEFINE p_zi = x
DECLARE
v_zi CHAR(2) := UPPER('&p_zi');
BEGIN
CASE v_zi
WHEN 'L' THEN DBMS_OUTPUT.PUT_LINE('Luni');
WHEN 'M' THEN DBMS_OUTPUT.PUT_LINE('Marti');
WHEN 'MI' THEN DBMS_OUTPUT.PUT_LINE('Miercuri');
WHEN 'J' THEN DBMS_OUTPUT.PUT_LINE('Joi');
WHEN 'V' THEN DBMS_OUTPUT.PUT_LINE('Vineri');
WHEN 'S' THEN DBMS_OUTPUT.PUT_LINE('Sambata');
WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE('Duminica');
ELSE DBMS_OUTPUT.PUT_LINE('este o eroare!');
END CASE;
END;
/
SET SERVEROUTPUT OFF
Varianta 2:
SET SERVEROUTPUT ON
DEFINE p_zi = x
DECLARE
v_zi CHAR(2) := UPPER('&p_zi');
BEGIN
CASE
WHEN v_zi = 'L' THEN DBMS_OUTPUT.PUT_LINE('Luni');
WHEN v_zi = 'M' THEN DBMS_OUTPUT.PUT_LINE('Marti');
WHEN v_zi = 'MI' THEN DBMS_OUTPUT.PUT_LINE('Miercuri');
WHEN v_zi = 'J' THEN DBMS_OUTPUT.PUT_LINE('Joi');
WHEN v_zi = 'V' THEN DBMS_OUTPUT.PUT_LINE('Vineri');
WHEN v_zi = 'S' THEN DBMS_OUTPUT.PUT_LINE('Sambata');
WHEN v_zi = 'D' THEN DBMS_OUTPUT.PUT_LINE('Duminica');
ELSE DBMS_OUTPUT.PUT_LINE('Este o eroare!');
END CASE;
END;
/
SET SERVEROUTPUT OFF
Oracle8i a implementat suportul pentru expresii CASE care sunt permise numai n comenzi SQL. n
Oracle9i poate fi utilizata o constructie CASE ntr-o comanda SQL a unui bloc PL/SQL.


10

Expresia CASE are sintaxa similara comenzii CASE, dar clauzele WHEN nu se termina prin caracterul ;,
clauza END nu include cuvntul cheie CASE si nu se fac atribuiri n clauza WHEN.
Exemplu:
BEGIN
FOR j IN (SELECT
CASE valoare
WHEN 1000 THEN 1100
WHEN 10000 THEN 11000
WHEN 100000 THEN 110000
ELSE valoare
END
FROM opera)

END LOOP;
END;

Instructiuni iterative
Exista trei tipuri de comenzi iterative: ciclarea simpla LOOP, ciclarea WHILE si ciclarea FOR.
Acestea permit repetarea (conditionata sau neconditionata) executiei uneia sau mai multor instructiuni.
Ciclurile pot fi imbricate pe mai multe niveluri. Ele pot fi etichetate, iar iesirea din ciclu se poate realiza cu
ajutorul comenzii EXIT.
Se utilizeaza:
comanda LOOP, daca instructiunile din cadrul ciclului trebuie sa se execute cel putin o data;
comanda WHILE, n cazul n care conditia trebuie evaluata la nceputul fiecarei iteratii;
comanda FOR, daca numarul de iteratii este cunoscut.
Instructiunea LOOP are urmatoarea forma sintactica:
LOOP
secventa_de_comenzi;
END LOOP;
Ciclarea simpla cuprinde o multime de comenzi incluse ntre cuvintele cheie LOOP si END LOOP.
Aceste comenzi se executa cel putin o data. Daca nu este utilizata comanda EXIT, ciclarea poate continua la
infinit.
Exemplu:
Se presupune ca a fost creata structura tabelului org_tab, constnd din doua coloane: cod_tab de tip
INTEGER, ce contine un contor al nregistrarilor si text_tab de tip VARCHAR2, ce contine un text asociat
fiecarei nregistrari. Sa se introduca 70 de nregistrari n tabelul org_tab.
DECLARE
v_contor BINARY_INTEGER := 1;
BEGIN
LOOP
INSERT INTO org_tab
VALUES (v_contor, 'indicele ciclului');
v_contor := v_contor + 1;
EXIT WHEN v_contor > 70;
END LOOP;
END;
Instructiunea repetitiva WHILE permite repetarea unei secvente de instructiuni, atta timp ct o anumita


11

conditie specificata este adevarata.
Comanda WHILE are urmatoarea sintaxa:
WHI LE conditie LOOP
secventa_de_comenzi;
END LOOP;
Daca variabilele care apar n conditie nu se schimba n interiorul ciclului, atunci conditia ramne
adevarata si ciclul nu se termina.
Cnd conditia este evaluata ca fiind FALSE sau NULL, atunci secventa de comenzi nu este executata si
controlul trece la prima instructiune dupa END LOOP.
Exemplu:
Exemplul precedent poate fi implementat utiliznd comanda WHILE n urmatoarea maniera:
DECLARE
v_contor BINARY_INTEGER := 1;
BEGIN
WHILE v_contor <= 70 LOOP
INSERT INTO org_tab
VALUES (v_contor, 'indicele ciclului');
v_contor := v_contor + 1;
END LOOP;
END;
Instructiunea repetitiva FOR (ciclare cu pas) permite executarea unei secvente de instructiuni pentru
valori ale variabilei contor cuprinse ntre doua limite, lim_inf si lim_sup. Daca este prezenta optiunea REVERSE,
iteratia se face (n sens invers) de la lim_sup la lim_inf.
Comanda FOR are sintaxa:
FOR contor_ciclu I N [REVERSE] lim_inf..lim_sup LOOP
secventa_de_comenzi;
END LOOP;
Variabila contor_ciclu nu trebuie declarata. Ea este neidentificata n afara ciclului si implicit de tip
BINARY_INTEGER. Pasul are implicit valoarea 1 si nu poate fi modificat. Limitele domeniului pot fi variabile
sau expresii, care sa poata fi convertite la ntreg.
Exemplu:
n structura tabelului opera se va introduce un nou cmp (stea). Sa se creeze un bloc PL/SQL care va
reactualiza acest cmp, introducnd o steluta pentru fiecare 10000$ din valoarea unei opere de arta al carei cod
este specificat.
ALTER TABLE opera
ADD stea VARCHAR2(20);
DEFINE p_cod_opera = 7777
DECLARE
v_cod_opera opera.cod_opera%TYPE := &p_cod_opera;
v_valoare opera.valoare%TYPE;
v_stea opera.stea%TYPE := NULL;
BEGIN
SELECT NVL(ROUND(valoare/10000),0)
INTO v_valoare
FROM opera
WHERE cod_opera = v_cod_opera;
IF v_valoare > 0 THEN
FOR i IN 1..v_valoare LOOP
v_stea := v_stea || '*';
END LOOP;


12

END IF;
UPDATE opera
SET stea = v_stea
WHERE cod_opera = v_cod_opera;
COMMIT;
END;
Instructiuni de salt
Instructiunea EXIT permite iesirea dintr-un ciclu. Ea are o forma neconditionala (iesire fara conditii) si
una conditionala. Controlul trece fie la prima instructiune situata dupa clauza END LOOP corespunzatoare, fie
dupa instructiunea LOOP avnd eticheta nume_eticheta.
EXIT [nume_eticheta] [WHEN conditie];
Numele etichetelor urmeaza aceleasi reguli ca cele definite pentru identificatori. Eticheta se plaseaza
naintea comenzii, fie pe aceeasi linie, fie pe o linie separata. n PL/SQL etichetele se definesc prin intercalarea
numelui etichetei ntre caracterele << si >> (<<eticheta>>).
Exemplu:
DECLARE
v_contor BINARY_INTEGER := 1;
raspuns VARCHAR2(10);
alt_raspuns VARCHAR2(10);
BEGIN

<<exterior>>
LOOP
v_contor := v_contor + 1;
EXIT WHEN v_contor > 70;
<<interior>>
LOOP

EXIT exterior WHEN raspuns = 'DA';
-- se parasesc ambele cicluri
EXIT WHEN alt_raspuns = 'DA';
-- se paraseste ciclul interior

END LOOP interior;

END LOOP exterior;
END;
Instructiunea GOTO determina un salt neconditionat la o instructiune executabila sau la nceputul unui
bloc care are eticheta specificata n comanda. Instructiunea are urmatoarea forma sintactica:
GOTO nume_eticheta;
Nu este permis saltul:
n interiorul unui bloc (subbloc);
n interiorul unei comenzi IF, CASE sau LOOP;
de la o clauza a comenzii CASE, la alta clauza a aceleasi comenzi;
de la tratarea unei exceptii, n blocul curent;
n exteriorul unui subprogram.
Instructiunea vida
Instructiunea vida (NULL) este folosita pentru o mai buna lizibilitate a programului. NULL este
instructiunea care nu are nici un efect, marcnd faptul ca nu trebuie ntreprinsa nici o actiune. Nu trebuie


13

confundata instructiunea NULL cu valoarea null!
Uneori instructiunea NULL este folosita ntr-o comanda IF, indicnd faptul ca pentru o anumita clauza
ELSIF nu se executa nici o actiune.



4. Tipuri de date n PL/SQL


Fiecare variabila sau constanta utilizata ntr-un bloc PL/SQL este de un anumit tip care determina
formatul sau de stocare, constrngerile pe care trebuie sa le verifice si domeniul valorilor sale.
Variabilele folosite n Oracle9i pot fi mpartite n doua clase:
variabile specifice PL/SQL, care se clasifica n variabile de tip scalar, compuse, referinta, LOB (large
objects) si tipuri obiect;
variabile nespecifice PL/SQL, care pot fi variabile de legatura (bind variables), variabile gazda (host
variables) si variabile indicator.
Variabile specifice PL/SQL
Tipurile de date scalare nu au componente interne (contin valori atomice). Aceste tipuri de date se mpart
n cinci clase fundamentale.
Tipurile de date ce stocheaza valori numerice, cuprind: tipul NUMBER cu subtipurile DEC,
DECIMAL, DOUBLE PRECISION, INT, INTEGER, NUMERIC, FLOAT, REAL, SMALLINT; tipul
de date BINARY_INTEGER cu subtipurile NATURAL, NATURALN, POSITIVE, POSITIVEN,
SIGNTYPE; tipul PLS_INTEGER..
Tipurile de date ce stocheaza caractere, cuprind: tipul VARCHAR2 cu subtipurile STRING,
VARCHAR; tipul de date CHAR cu subtipul CHARACTER; tipurile LONG, RAW, LONG RAW,
ROWID.
Tipurile de date ce stocheaza data calendaristica si ora, cuprind tipurile DATE, TIMESTAMP,
TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE, INTERVAL YEAR
TO MONTH, INTERVAL DAY TO SECOND.
Tipurile de date pentru globalizare, care includ tipurile NCHAR si NVARCHAR2, stocheaza date n
format Unicode.
Tipul de date BOOLEAN stocheaza valori logice (true, false sau null).
Tipurile de date compuse au componente interne care pot fi prelucrate individual. Sistemul ofera
programatorului doua tipuri de date compuse: nregistrare (RECORD) si colectie (INDEX-BY TABLE, NESTED
TABLE, VARRAY).
Tipurile de date referinta (REF CURSOR, REF obiect) sunt tipuri de date ale caror valori, numite
pointer-i, fac referinta catre obiecte din program. Pointer-ii contin locatia de memorie (adresa) a unui element si
nu elementul n sine. Tipul REF CURSOR este folosit pentru a face referinta la un cursor explicit. Tipul REF
obiect face referinta la adresa unui obiect.
Tipurile LOB (large object) sunt acele tipuri de date ale caror valori, numite locatori (locators), specifica
locatia (localizarea) unor obiecte de dimensiuni mari, adica blocuri de date nestructurate, cum ar fi texte, imagini
grafice, clipuri video si sunete. Ele cuprind tipurile: CLOB (Character Large Object), BLOB (Binary Large
Object), NCLOB (National Language Character Large Object) si BFILE (Binary File). Tipurile LOB sunt
prelucrate cu ajutorul pachetului DBMS_LOB.
Tipurile obiect sunt tipuri compuse, definite de utilizator, care ncapsuleaza structuri de date (atribute)
mpreuna cu subprograme pentru prelucrarea datelor (metode). Oracle9i extinde modelul obiect de la versiunea


14

Oracle8i, implementnd mostenirea prin intermediul subtipurilor. Tipul obiect va fi analizat ntr-un capitol
separat.
Dintre tipurile scalare PL/SQL, urmatoarele sunt si tipuri SQL (adica pot fi folosite pentru coloanele
tabelelor Oracle): NUMBER, VARCHAR2, CHAR, LONG, RAW, LONG RAW, ROWID, NCHAR,
NVARCHAR2, DATE. n unele cazuri, tipurile de date PL/SQL difera de corespondentele lor din SQL prin
dimensiunea maxima permisa.
Tipul NUMBER memoreaza numerele n virgula fixa si virgula mobila. El are forma generala NUMBER
(m, n), unde m reprezinta numarul total de cifre, iar n numarul de zecimale. Valoarea unei variabile de tip
NUMBER este cuprinsa ntre 1.0E-129 si 9.99E125. Numarul de zecimale determina pozitia n care apare
rotunjirea. Valoarea sa este cuprinsa ntre -84 si 127, iar implicit este 0.
Tipul NUMBER are urmatoarele subtipuri, care au aceleasi intervale de valori: NUMERIC, REAL, DEC,
DECIMAL si DOUBLE PRECISION (pentru memorarea datelor numerice n virgula fixa), FLOAT (pentru
memorarea datelor numerice n virgula mobila), SMALLINT, INTEGER si INT (pentru memorarea numerelor
ntregi). Aceste subtipuri se pot utiliza pentru compatibilitate ANSI/ISO, IBM SQL/DS sau IBM DB2.
Tipul BINARY_INTEGER memoreaza numere ntregi cu semn avnd valori cuprinse ntre -2
31
- 1 si 2
31
-
1. Acest tip de date este utilizat frecvent pentru indecsii tabelelor, nu necesita conversii si admite mai multe
subtipuri. De exemplu, pentru a restrictiona domeniul variabilelor la valori ntregi nenegative se utilizeaza tipurile
NATURAL (0 .. 2
31
1) si POSITIVE (1 .. 2
31
1).
Tipul PLS_INTEGER este utilizat pentru stocarea numerelor ntregi cu semn si are acelasi interval de
definire ca si tipul BINARY_INTEGER. Operatiile cu acest tip sunt efectuate mai rapid (folosesc aritmetica
masinii), dect cele cu tipurile NUMBER sau BINARY_INTEGER (folosesc librarii aritmetice). Prin urmare,
pentru o mai buna performanta, este preferabil sa se utilizeze tipul PLS_INTEGER.
Variabilele alfanumerice pot fi de tip CHAR, VARCHAR2, LONG, RAW si LONGRAW. Reprezentarea
interna depinde de setul de caractere ales (ASCII sau EBCDIC).
Tipurile CHAR, VARCHAR2 si RAW pot avea un parametru pentru a preciza lungimea maxima. Daca
aceasta nu este precizata atunci, implicit, se considera 1. Lungimea este exprimata n octeti (nu n caractere).
Subtipurile acestor tipuri se pot utiliza pentru compatibilitate ANSI/ISO, IBM SQL/DS sau IBM DB2.
n Oracle9i a fost extinsa sintaxa pentru CHAR si VARCHAR2, permitnd ca variabila ce precizeaza
lungimea maxima sa fie de tip CHAR sau BYTE.
Variabilele de tip LONG pot memora texte, tabele de caractere sau documente, prin urmare siruri de
caractere de lungime variabila de pna la 32760 octeti. Este similar tipului VARCHAR2.
Tipul RAW permite memorarea datelor binare (biti) sau a sirurilor de octeti. De exemplu, o variabila RAW
poate memora o secventa de caractere grafice sau o imagine digitizata. Tipul RAW este similar tipului
alfanumeric, cu exceptia faptului ca PL/SQL nu interpreteaza datele de tip RAW. Oracle nu face conversia
datelor de acest tip, atunci cnd se transmit de la un sistem la altul. Chiar daca lungimea maxima a unei variabile
RAW poate fi 32767 octeti, ntr-o coloana RAW a bazei de date nu se pot introduce dect 2000 octeti. Pentru a
insera valori mai mari se foloseste o coloana de tip LONG RAW, care are lungimea maxima 2
31
octeti. LONG
RAW este similar tipului LONG, dar datele nu mai sunt interpretate de PL/SQL.
Tipurile TIMESTAMP, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE,
INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND au fost introduse n Oracle9i si permit rafinari
ale tipului DATE. De exemplu, TIMESTAMP poate lua n considerare si fractiuni de secunda.
PL/SQL suporta doua seturi de caractere: una specifica bazei de date care este utilizata pentru definirea
identificatorilor si a codului sursa (database character set - DCS) si o multime de caractere nationale care este
folosita pentru reprezentarea informatiei cu caracter national (national character set - NCS).
Tipurile de date NCHAR si NVARCHAR2 sunt utilizate pentru stocarea n baza de date a sirurilor de
caractere ce folosesc NCS. Ele ofera suport pentru globalizarea datelor, astfel nct utilizatorii din toata lumea


15

pot interactiona cu Oracle n limba lor nationala. Aceste tipuri de date suporta numai date Unicode.
Unicode este o multime de caractere globale care permite stocarea de informatie n orice limba, folosind
o multime unica de caractere. Prin urmare, unicode furnizeaza o valoare cod unica pentru fiecare caracter,
indiferent de platforma, program sau limba.
Variabile nespecifice PL/SQL
Variabila de legatura (bind) se declara ntr-un mediu gazda si este folosita pentru transferul (la executie)
valorilor numerice sau de tip caracter n/din unul sau mai multe programe PL/SQL. Variabilele declarate n
mediul gazda sau n cel apelant pot fi referite n instructiuni PL/SQL, daca acestea nu sunt n cadrul unei
proceduri, functii sau pachet.
n SQL*Plus, variabilele de legatura se declara folosind comanda VARIABLE, iar pentru afisarea valorilor
acestora se utilizeaza comanda PRINT. Ele sunt referite prin prefixarea cu simbolul :, pentru a putea fi
deosebite de variabilele declarate n PL/SQL.
Deoarece instructiunile SQL pot fi integrate n programe C, este necesar un mecanism pentru a transfera
valori ntre mediul de programare C si instructiunile SQL care comunica cu server-ul bazei de date Oracle. n
acest scop, n programul ncapsulat sunt definite variabilele gazda (host). Acestea sunt declarate ntre directivele
BEGIN DECLARE SECTION si END DECLARE SECTION ale preprocesorului.
O valoare null n baza de date nu are o valoare corespunzatoare n mediul limbajului gazda (de exemplu,
limbajul C). Pentru a rezolva problema comunicarii valorilor null ntre programul scris n limbaj gazda si sistemul
Oracle, au fost definite variabilele indicator. Acestea sunt variabile speciale de tip ntreg, folosite pentru a indica
daca o valoare null este recuperata (extrasa) din baza de date sau stocata n aceasta. Ele au urmatoarea forma:
:nume_extern [: indicator]
De exemplu, daca atribuirea este facuta de limbajul gazda, valoarea 1 a indicatorului specifica faptul ca
PL/SQL trebuie sa nlocuiasca valoarea variabilei prin null, iar o valoare a indicatorului mai mare ca zero
precizeaza ca PL/SQL trebuie sa considere chiar valoarea variabilei.
Declararea variabilelor
Identificatorii PL/SQL trebuie declarati nainte de a fi referiti n blocul PL/SQL. Daca n declaratia unei
variabile apar referiri la alte variabile, acestea trebuie sa fi fost declarate anterior. Orice variabila declarata ntr-un
bloc este accesibila blocurilor continute sintactic n acesta.
Tipurile scalare sunt predefinite n pachetul STANDARD. Pentru a folosi un astfel de tip ntr-un program
este suficient sa fie declarata o variabila de tipul respectiv.
Tipurile compuse sunt definite de utilizator. Prin urmare, n acest caz trebuie definit efectiv tipul si apoi
declarata variabila de tipul respectiv.
n declararea variabilelor pot fi utilizate atributele %TYPE si %ROWTYPE, care reprezinta tipuri de date
implicite. Aceste tipuri permit declararea unei variabile n concordanta cu declaratii de variabile facute anterior.
Atributul %TYPE permite definirea unei variabile avnd tipul unei variabile declarate anterior sau tipul
unei coloane dintr-un tabel.
Atributul %ROWTYPE permite definirea unei variabile avnd tipul unei nregistrari dintr-un tabel.
Avantajul utilizarii acestui atribut consta n faptul ca nu este necesar sa se cunoasca numarul si tipurile coloanelor
tabelului. Elementele individuale ale acestei structuri de tip nregistrare sunt referite n maniera clasica, prefixnd
numele coloanei cu numele variabilei declarate.
Calitatea atributelor %TYPE si %ROWTYPE consta n faptul ca simplifica ntretinerea codului PL/SQL.
De exemplu, poate fi modificata dimensiunea unei coloane, fara sa fie necesara modificarea declaratiei
variabilelor al caror tip s-a definit facnd referinta la tipul coloanei respective.
Sintaxa declararii unei variabile este urmatoarea:
identificator [CONSTANT] {tip_de_date | identificator%TYPE |


16

identificator%ROWTYPE} [NOT NULL]
[ {:= | DEFAULT} expresie_PL/SQL];
Se pot defini constante (valoarea stocata nu poate fi modificata) prin specificarea la declarare a
cuvntului cheie CONSTANT.
Exemplu:
v_valoare NUMBER(15) NOT NULL := 0;
v_data_achizitie DATE DEFAULT SYSDATE;
v_material VARCHAR2(15) := 'Matase';
c_valoare CONSTANT NUMBER := 100000;
v_stare VARCHAR2(20) DEFAULT 'Buna';
v_clasificare BOOLEAN DEFAULT FALSE;
v_cod_opera opera.cod_opera%TYPE;
v_opera opera%ROWTYPE;
int_an_luna INTERVAL YEAR TO MONTH :=
INTERVAL '3-2' YEAR TO MONTH;
Observatii:
Pentru a denumi o variabila este utilizata frecvent (pentru usurinta referirii) prefixarea cu litera v
(v_identificator), iar pentru o constanta este folosita prefixarea cu litera c (c_identificator).
Variabilele pot fi initializate, iar daca o variabila nu este initializata, valoarea implicita a acesteia este
null. Daca o variabila este declarata NOT NULL, atunci ea va fi obligatoriu initializata.
Pentru a initializa o variabila sau o constanta poate fi utilizata o expresie PL/SQL compatibila ca tip
cu variabila sau constanta respectiva.
Constantele trebuie initializate cnd sunt declarate, altfel apare o eroare la compilare.
n sectiunea declarativa, pe fiecare linie, exista o singura declaratie de variabila.
Doua obiecte (variabile) pot avea acelasi nume cu conditia sa fie definite n blocuri diferite. Daca ele
coexista, poate fi folosit doar obiectul declarat n blocul curent.
Atributul %ROWTYPE nu poate include clauze de initializare.
Definirea subtipurilor
Subtipurile deriva dintr-un tip de baza, la care se adauga anumite restrictii. De exemplu, NATURAL este
un subtip predefinit PL/SQL, derivat din tipul de baza BINARY_INTEGER, cu restrictia ca permite prelucrarea
valorilor ntregi nenegative.
Prin urmare, un subtip nu reprezinta un nou tip de date, ci un tip existent asupra caruia se aplica anumite
constrngeri. Subtipurile presupun acelasi set de operatii ca si tipul de baza, dar aplicate unui subset de valori al
acestui tip.
Sistemul Oracle permite ca utilizatorul sa-si defineasca propriile sale tipuri si subtipuri de date n partea
declarativa a unui bloc PL/SQL, subprogram sau pachet utiliznd sintaxa:
SUBTYPE nume_subtip IS tip_de_baza [NOT NULL];
n dictionarul datelor exista vizualizari care furnizeaza informatii despre tipurile de date create de
utilizator (USER_TYPES, USER_TYPE_ATTRS).
Conversii ntre tipuri de date
Exista doua tipuri de conversii, implicite si explicite. PL/SQL face automat conversii implicite ntre
caractere si numere sau ntre caractere si date calendaristice. Chiar daca sistemul realizeaza automat aceste
conversii, n practica se utilizeaza frecvent functii de conversie explicita.
Functiile de conversie explicita din SQL sunt utilizabile si n PL/SQL. Acestea sunt: TO_NUMBER,
TO_CHAR, TO_DATE, TO_MULTI_BYTE, TO_SINGLE_BYTE, TO_CLOB, TO_LOB, CHARTOROWID,


17

ROWIDTOCHAR, RAWTOHEX, HEXTORAW.
n Oracle9i se pot folosi urmatoarele functii de conversie: ASCIISTR, BIN_TO_NUM,
NUMTODSINTERVAL, TO_TIMESTAMP, TO_YMINTERVAL, TO_NCHAR, TO_NCLOB,
TO_TIMESTAMP_TZ, NUMTOYMINTERVAL, TO_DSINTERVAL, REFTOHEX, RAWTOHEX,
RAWTONHEX, FROM_TZ, ROWIDTONCHAR, COMPOSE, DECOMPOSE.
Denumirile acestor functii reflecta posibilitatile pe care le ofera. De exemplu, TO_YMINTERVAL
converteste argumentele sale la tipul INTERVAL YEAR TO MONTH conform unui format specificat. Functia
COMPOSE converteste un sir de caractere la un sir unicode (asociaza o valoare cod unica pentru fiecare simbol
din sir).
nregistrari
Tipul RECORD ofera un mecanism pentru prelucrarea nregistrarilor. nregistrarile au mai multe cmpuri
ce pot fi de tipuri diferite, dar care sunt legate din punct de vedere logic.
Declararea tipului RECORD se face conform urmatoarei sintaxe:
TYPE nume_tip I S RECORD
(nume_cmp1 {tip_cmp | variabila%TYPE |
nume_tabel.coloana%TYPE | nume_tabel%ROWTYPE}
[ [NOT NULL] {:= | DEFAULT} expresie1],
(nume_cmp2 {tip_cmp | variabila%TYPE |
nume_tabel.coloana%TYPE | nume_tabel%ROWTYPE}
[ [NOT NULL] {:= | DEFAULT} expresie2],);
Identificatorul nume_tip reprezinta numele tipului RECORD care se va specifica n declararea
nregistrarilor, nume_cmp este numele unui cmp al nregistrarii, iar tip_cmp este tipul de date al cmpului.
Observatii:
Daca un cmp nu este initializat, atunci se considera implicit ca are valoarea null. Daca s-a specificat
constrngerea NOT NULL, atunci obligatoriu cmpul trebuie initializat, iar initializarea se face cu o
valoare diferita de null.
Pentru referirea cmpurilor individuale din nregistrare se prefixeaza numele cmpului cu numele
nregistrarii.
Pot fi asignate valori unei nregistrari utiliznd comenzile SELECT, FETCH sau instructiunea clasica
de atribuire. De asemenea, o nregistrare poate fi asignata altei nregistrari de acelasi tip.
Componentele unei nregistrari pot fi de tip scalar, RECORD, TABLE, obiect, colectie (dar nu de
tipul REF CURSOR).
PL/SQL permite declararea si referirea nregistrarilor imbricate.
Numarul de cmpuri ale unei nregistrari nu este limitat.
nregistrarile nu pot fi comparate (egalitate, inegalitate sau null).
nregistrarile pot fi parametri n subprograme si pot sa apara n clauza RETURN a unei functii.
Diferenta dintre atributul %ROWTYPE si tipul de date compus RECORD este ca tipul RECORD permite
specificarea tipului de date pentru cmpuri si declararea acestora. Numele cmpului poate coincide cu numele
unei coloane.
Oracle9i introduce cteva facilitati legate de acest tip de date.
Se poate insera (INSERT) o linie ntr-un tabel utiliznd o nregistrare. Nu mai este necesara listarea
cmpurilor individuale, ci este suficienta utilizarea numelui nregistrarii.
Se poate reactualiza (UPDATE) o linie a unui tabel utiliznd o nregistrare. Sintaxa SET ROW
permite sa se reactualizeze ntreaga linie folosind continutul unei nregistrari.
ntr-o nregistrare se poate regasi si returna sau sterge informatia din clauza RETURNING a
comenzilor UPDATE sau DELETE.
Daca n comenzile UPDATE sau DELETE se modifica mai multe linii, atunci pot fi utilizate n sintaxa


18

BULK COLLECT INTO, colectii de nregistrari.
Exemplu:
Exemplul urmator arata modul n care poate sa fie utilizata o nregistrare n clauza RETURNING asociata
comenzii DELETE.
DECLARE
TYPE val_opera IS RECORD (
cheie NUMBER,
val NUMBER);
v_info_valoare val_opera;
BEGIN
DELETE FROM opera
WHERE cod_opera = 753
RETURNING cod_opera, valoare
INTO v_info_valoare;

END;
n PL/SQL este folosit frecvent tipul tablou de nregistrari. Referirea la un element al tabloului se face
prin forma clasica: tabel(index).cmp.
Exemplu:
Sa se defineasca un tablou de nregistrari avnd tipul celor din tabelul organizator. Sa se initializeze un
element al tabloului si sa se introduca n tabelul organizator. Sa se stearga elementele tabloului.
DECLARE
TYPE org_table_type IS TABLE OF organizator%ROWTYPE
INDEX BY BINARY INTEGER;
org_table org_table_type;
i NUMBER;
BEGIN
IF org_table.COUNT <>0 THEN
i := org_table.LAST+1;
ELSE i:=1;
END IF;
org_table(i).cod_org := 752;
org_table(i).nume := 'Grigore Ion';
org_table(i).adresa := 'Calea Plevnei 18 Sibiu';
org_table(i).tip := 'persoana fizica';
INSERT INTO organizator
VALUES (org_table(i).cod_org, org_table(i).nume,
org_table(i).adresa, org_table(i).tip);
-- sau folosind noua facilitate Oracle9i
-- INSERT INTO organizator
-- VALUES (org_table(i));
org_table.DELETE;
DBMS_OUTPUT.PUT_LINE('Dupa aplicarea metodei DELETE sunt '
||TO_CHAR(org_table.COUNT)||' elemente');
END;
Colectii
Uneori este preferabil sa fie prelucrate simultan mai multe variabile de acelasi tip. Tipurile de date care
permit acest lucru sunt colectiile. Fiecare element are un indice unic, care determina pozitia sa n colectie.
Oracle7 a furnizat tipul index-by table, initial numit PL/SQL table datorita asemanarii sale cu structura
tabelelor relationale. Oracle8 a introdus doua tipuri colectie, nested table si varray. Oracle9i permite crearea de
colectii pe mai multe niveluri, adica colectii de colectii.
Prin urmare, n PL/SQL exista trei tipuri de colectii:


19

tablouri indexate (index-by tables);
tablouri imbricate (nested tables);
vectori (varrays sau varying arrays).
Tipul index-by table poate fi utilizat numai n declaratii PL/SQL. Tipurile varray si nested table pot fi
utilizate att n declaratii PL/SQL, ct si n declaratii la nivelul schemei (de exemplu, pentru definirea tipului unei
coloane a unui tabel relational).
Exemplu:
n exemplul care urmeaza sunt ilustrate cele trei tipuri de colectii.
DECLARE
TYPE tab_index IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
TYPE tab_imbri IS TABLE OF NUMBER;
TYPE vector IS VARRAY(15) OF NUMBER;
v_tab_index tab_index;
v_tab_imbri tab_imbri;
v_vector vector;
BEGIN
v_tab_index(1) := 72;
v_tab_index(2) := 23;
v_tab_imbri := tab_imbri(5, 3, 2, 8, 7);
v_vector := vector(1, 2);
END;
Observatii:
Deoarece colectiile nu pot fi comparate (egalitate sau inegalitate), ele nu pot sa apara n clauzele
DISTINCT, GROUP BY, ORDER BY.
Tipul colectie poate fi definit ntr-un pachet.
Tipul colectie poate sa apara n clauza RETURN a unei functii.
Colectiile pot fi parametri formali ntr-un subprogram.
Accesul la elementele individuale ale unei colectii se face prin utilizarea unui indice.
Tablouri indexate
Tipul de date index-by table ofera un mecanism pentru prelucrarea tablourilor. Tabloul indexat PL/SQL
are doua componente: o coloana ce cuprinde cheia primara pentru acces la liniile tabloului si o coloana care
include valoarea efectiva a elementelor tabloului.
Oracle7 asigura definirea tablourilor de nregistrari care pot fi declarate si utilizate numai n programe
PL/SQL, Oracle8 realizeaza definirea tablourilor de tipuri obiect, iar Oracle9i permite definirea tablourilor de
colectii.
n Oracle9i tipul index-by table este redenumit associative array pentru compatibilitate (de limbaj) cu
termenul folosit n alte limbaje de programare (C++, JavaScript, Perl) pentru a defini aceasta structura de date.
Tablourile indexate PL/SQL trebuie definite n doi pasi: se defineste tipul TABLE; se declara tabloul
indexat PL/SQL de acest tip.
Declararea tipului TABLE se face respectnd urmatoarea sintaxa:
TYPE nume_tip I S TABLE OF
{tip_coloana | variabila%TYPE |
nume_tabel.coloana%TYPE [NOT NULL] |
nume_tabel%ROWTYPE}
I NDEX BY tip_indexare;
Identificatorul nume_tip este numele noului tip definit care va fi specificat n declararea tabloului
PL/SQL, iar tip_coloana este un tip scalar simplu (de exemplu, VARCHAR2, CHAR, DATE sau NUMBER).


20

Pna la versiunea Oracle9i unicul tip de indexare acceptat era INDEX BY BINARY_INTEGER. Oracle9i
permite urmatoarele optiuni pentru tip_indexare: PLS_INTEGER, NATURAL, POSITIVE, VARCHAR2(n) sau
chiar indexarea dupa un tip declarat cu %TYPE. Nu sunt permise indexarile INDEX BY NUMBER, INDEX BY
INTEGER, INDEX BY DATE, INDEX BY VARCHAR2, INDEX BY CHAR(n) sau indexarea dupa un tip declarat
cu %TYPE n care intervine unul dintre tipurile enumerate anterior.
Observatii:
Elementele unui tablou indexat nu sunt ntr-o ordine particulara si pot fi inserate cu chei arbitrare.
Deoarece nu exista constrngeri de dimensiune, dimensiunea tabloului se modifica dinamic.
Tabloul indexat PL/SQL nu trebuie initializat.
Un tablou indexat neinitializat este vid.
Un element al tabloului este nedefinit atta timp ct nu are atribuita o valoare efectiva.
Initial, un tablou indexat este nedens. Dupa declararea unui tablou se poate face referire la liniile lui
prin precizarea valorii cheii primare.
Daca se face referire la o linie care nu exista, atunci se produce exceptia NO_DATA_FOUND.
Daca se doreste contorizarea numarului de linii, trebuie declarata o variabila n acest scop sau poate fi
utilizata o metoda asociata tabloului.
Deoarece numarul de linii nu este limitat, operatia de adaugare de linii este restrictionata doar de
dimensiunea memoriei alocate.
Tablourile pot sa apara ca argumente ntr-o procedura.
Pentru inserarea unor valori din tablourile PL/SQL ntr-o coloana a unui tabel de date se utilizeaza
instructiunea INSERT n cadrul unei secvente repetitive LOOP. Asemanator, pentru regasirea unor valori dintr-o
coloana a unei baze de date ntr-un tablou PL/SQL se utilizeaza instructiunea FETCH (cursoare) sau
instructiunea de atribuire n cadrul unei secvente repetitive LOOP.
Pentru a sterge liniile unui tablou fie se asigneaza elementelor tabloului valoarea null, fie se declara un alt
tablou PL/SQL (de acelasi tip) care nu este initializat si acest tablou vid se asigneaza tabloului PL/SQL care
trebuie sters. n PL/SQL 2.3 stergerea liniilor unui tabel se poate face utiliznd metoda DELETE.
Exemplu:
Sa se defineasca un tablou indexat PL/SQL avnd elemente de tipul NUMBER. Sa se introduca 20 de
elemente n acest tablou. Sa se stearga tabloul.
DECLARE
TYPE tablou_numar IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
v_tablou tablou_numar;
BEGIN
FOR i IN 1..20 LOOP
v_tablou(i) := i*i;
DBMS_OUTPUT.PUT_LINE(v_tablou(i));
END LOOP;
--v_tablou := NULL;
--aceasta atribuire da eroarea PLS-00382
FOR i IN v_tablou.FIRST..v_tablou.LAST LOOP
v_tablou(i) := NULL;
END LOOP;
DBMS_OUTPUT.PUT_LINE('tabloul are ' || v_tablou.COUNT ||
' elemente');
END;
Vectori


21

Vectorii (varray) sunt structuri asemanatoare vectorilor din limbajele C sau Java. Spre deosebire de
tablourile indexate, vectorii au o dimensiune maxima (constanta) stabilita la declarare. n special, se utilizeaza
pentru modelarea relatiilor one-to-many, atunci cnd numarul maxim de elemente din partea many este
cunoscut si ordinea elementelor este importanta.
Vectorii reprezinta structuri dense. Fiecare element are un index care da pozitia sa n vector si care este
folosit pentru accesarea elementelor particulare. Limita inferioara a indicelui este 1. Vectorul poate contine un
numar variabil de elemente, de la 0 (vid) la numarul maxim specificat obligatoriu n definitia sa.
Tipul de date vector este declarat utiliznd sintaxa:
TYPE nume_tip IS
{VARRAY | VARYI NG ARRAY} (lungime_maxima)
OF tip_elemente [NOT NULL];
Identificatorul nume_tip este numele tipului de date vector, iar lungime_maxima reprezinta numarul
maxim de elemente din vector. Tip_elemente este un tip scalar PL/SQL, tip nregistrare sau tip obiect. De
asemenea, acest tip poate fi definit utiliznd atributele %TYPE sau %ROWTYPE.
n Oracle9i sunt permise (pentru tip_elemente) tipurile TABLE sau alt tip VARRAY. Exista restrictii
referitoare la tipul elementelor, n sensul ca acesta nu poate sa fie BOOLEAN, NCHAR, NCLOB, NVARCHAR2,
REF CURSOR, PLS_INTEGER, LONG, LONG RAW, NATURAL, NATURALN, POSITIVE, POSITIVEN,
BINARY_INTEGER, SIGNTYPE, STRING, tip obiect cu atribute TABLE sau VARRAY, BLOB, CLOB, tip obiect
cu atribute BLOB sau CLOB.
Exemplu:
DECLARE
TYPE secventa IS VARRAY(5) OF VARCHAR2(10);
v_sec secventa := secventa ('alb','negru','rosu','verde');
BEGIN
v_sec (3) := 'rosu';
v_sec.EXTEND;
v_sec(5) := 'albastru';
-- extinderea la 6 elemente va genera eroarea ORA-06532
v_sec.EXTEND;
END;
Tablouri imbricate
Tablourile imbricate (nested table) sunt tablouri indexate a caror dimensiune nu este stabilita. Un tablou
imbricat este o multime neordonata de elemente de acelasi tip. Valorile de acest tip pot fi stocate n baza de date,
pot fi prelucrate direct n instructiuni SQL si au exceptii predefinite proprii. Numarul maxim de linii ale unui
tablou imbricat este dat de capacitatea maxima 2 GB.
Sistemul Oracle nu stocheaza liniile unui tablou imbricat ntr-o ordine particulara. Dar, cnd se regaseste
tabloul n variabile PL/SQL, liniile vor avea indici consecutivi ncepnd cu valoarea 1. Initial, aceste tablouri sunt
structuri dense, dar se poate ca n urma prelucrarii sa nu mai aiba indici consecutivi.
Comanda de declarare a tipului de date tablou imbricat are sintaxa:
TYPE nume_tip I S TABLE OF tip_ elemente [NOT NULL];
Identificatorul nume_tip reprezinta numele noului tip de date tablou imbricat, iar tip_elemente este tipul
fiecarui element din tabloul imbricat, care poate fi un tip definit de utilizator sau o expresie cu %TYPE, respectiv
%ROWTYPE.
n Oracle9i sunt permise (pentru tip_elemente) tipurile TABLE sau alt tip VARRAY. Exista restrictii
referitoare la tipul elementelor, n sensul ca acesta nu poate sa fie BOOLEAN, STRING, NCHAR, NCLOB,
NVARCHAR2, REF CURSOR, BINARY_INTEGER, PLS_INTEGER, LONG, LONG RAW, NATURAL,
NATURALN, POSITIVE, POSITIVEN, SIGNTYPE, tip obiect cu atributele TABLE sau VARRAY.


22

Tabloul imbricat are o singura coloana, iar daca aceasta este de tip obiect, tabloul poate fi vizualizat ca un
tabel multicoloana, avnd cte o coloana pentru fiecare atribut al tipului obiect.
Exemplu:
DECLARE
TYPE numartab IS TABLE OF NUMBER;
-- se creeaza un tablou cu un singur element
v_tab_1 numartab := numartab(-7);
-- se creeaza un tablou cu 4 elemente
v_tab_2 numartab := numartab(7,9,4,5);
-- se creeaza un tablou fara nici un element
v_tab_3 numartab := numartab();
BEGIN
v_tab_1(1) := 57;
FOR j IN 1..4 LOOP
DBMS_OUTPUT.PUT_LINE (v_tab_2(j) || ' ');
END LOOP;
END;
Se observa ca singura diferenta sintactica ntre tablourile indexate si cele imbricate este absenta clauzei
INDEX BY BINARY_INTEGER. Mai exact, daca aceasta clauza lipseste, tipul este tablou imbricat.
Observatii:
Spre deosebire de tablourile indexate, vectorii si tablourile imbricate pot sa apara n definirea
tabelelor bazei de date.
Tablourile indexate pot avea indice negativ, domeniul permis pentru index fiind
2147483647..2147483647, iar pentru tabele imbricate domeniul indexului este 1..2147483647.
Tablourile imbricate, spre deosebire de tablourile indexate, pot fi prelucrate prin comenzi SQL.
Tablourile imbricate trebuie initializate si/sau extinse pentru a li se adauga elemente.
Cnd este creat un tablou indexat care nu are nca elemente, el este vid. Daca un tablou imbricat (sau un
vector) este declarat, dar nu are nca nici un element (nu este initializat), el este automat initializat (atomic) null.
Adica, colectia este null, nu elementele sale. Prin urmare, pentru tablouri imbricate poate fi utilizat operatorul IS
NULL. Daca se ncearca sa se adauge un element la un tablou imbricat null, se va genera eroarea ORA - 06531:
reference to uninitialized collection care corespunde exceptiei predefinite COLLECTION_IS_NULL.
Prin urmare, cum poate fi initializat un tablou imbricat? Ca si obiectele, vectorii si tablourile imbricate
sunt initializate cu ajutorul constructorului. Acesta are acelasi nume ca si tipul colectiei referite. PL/SQL apeleaza
un constructor numai n mod explicit. Tabelele indexate nu au constructori.
Constructorul primeste ca argumente o lista de valori de tip tip_elemente. Elementele sunt numerotate n
ordine, de la 1 la numarul de valori date ca parametrii constructorului. Dimensiunea initiala a colectiei este egala
cu numarul de argumente date n constructor, cnd aceasta este initializata. Pentru vectori nu poate fi depasita
dimensiunea maxima precizata la declarare. Atunci cnd constructorul este fara argumente, va crea un obiect fara
nici un element, dar care nu are valoarea null. Exemplul urmator este concludent n acest sens.
Exemplu:
DECLARE
TYPE alfa IS TABLE OF VARCHAR2(50);
-- creeaza un tablou null
tab1 alfa ;
-- creeaza un tablou cu un element care este null
tab2 alfa := alfa() ;
BEGIN
IF tab1 IS NULL THEN


23

DBMS_OUTPUT.PUT_LINE('tab1 este NULL');
ELSE
DBMS_OUTPUT.PUT_LINE('tab1 este NOT NULL');
END IF;
IF tab2 IS NULL THEN
DBMS_OUTPUT.PUT_LINE('tab2 este NULL');
ELSE
DBMS_OUTPUT.PUT_LINE('tab2 este NOT NULL');
END IF;
END;
n urma executiei acestui bloc se obtine urmatorul rezultat:
tab1 este NULL
tab2 este NOT NULL
Exceptiile semnificative care apar n cazul utilizarii incorecte a colectiilor sunt prezentate n exemplul care
urmeaza.
Exemplu:
DECLARE
TYPE numar IS TABLE OF INTEGER;
alfa numar;
BEGIN
alfa(1) := 77;
-- declanseaza exceptia COLLECTION_IS_NULL
alfa := numar(15, 26, 37);
alfa(1) := ASCII('X');
alfa(2) := 10*alfa(1);
alfa('P') := 77;
/* declanseaza exceptia VALUE_ERROR deoarece indicele
nu este convertibil la intreg */
alfa(4) := 47;
/* declanseaza exceptia SUBSCRIPT_BEYOND_COUNT deoarece
indicele se refera la un element neinitializat */
alfa(null) := 7; -- declanseaza exceptia VALUE_ERROR
alfa(0) := 7; -- exceptia SUBSCRIPT_OUTSIDE_LIMIT
alfa.DELETE(1);
IF alfa(1) = 1 THEN -- exceptia NO_DATA_FOUND

END;
Tablourile imbricate si vectorii pot fi utilizati drept cmpuri n tabelele bazei. Aceasta presupune ca
fiecare nregistrare din tabelul respectiv contine un obiect de tip colectie. nainte de utilizare, tipul trebuie stocat
n dictionarul datelor, deci trebuie declarat prin comanda:
CREATE TYPE nume_tip AS {TABLE | VARRAY} OF tip_elemente;
Dupa crearea tabelului (prin comanda CREATE TABLE), pentru fiecare cmp de tip tablou imbricat din
tabel este necesara clauza de stocare:
NESTED TABLE nume_cmp STORE AS nume_tabel;
Colectii pe mai multe niveluri
n Oracle9i se pot construi colectii pe mai multe niveluri (multilevel collections), prin urmare colectii ale
caror elemente sunt, n mod direct sau indirect, colectii. n felul acesta pot fi definite structuri complexe: vectori


24

de vectori, vectori de tablouri imbricate, tablou imbricat de vectori, tablou imbricat de tablouri imbricate, tablou
imbricat sau vector de un tip definit de utilizator care are un atribut de tip tablou imbricat sau vector.
Aceste structuri complexe pot fi utilizate ca tipuri de date pentru definirea coloanelor unui tabel
relational, ale atributelor unui obiect ntr-un tabel obiect sau ale variabilelor PL/SQL. Numarul nivelurilor de
imbricare este limitat doar de capacitatea de stocare a sistemului.
Pentru a accesa un element al colectiei incluse sunt utilizate doua seturi de paranteze. Obiectele de tipul
colectie pe mai multe niveluri nu pot fi comparate.
Exemplu:
n exemplele care urmeaza sunt definite trei structuri complexe si sunt prezentate cteva modalitati de
utilizare ale acestora. Exemplele se refera la vectori pe mai multe niveluri, tablouri imbricate pe mai multe
niveluri si tablouri indexate pe mai multe niveluri.
DECLARE
TYPE alfa IS VARRAY(10) OF INTEGER;
TYPE beta IS VARRAY(10) OF alfa;
valf alfa := alfa(12,31,5); --initializare
vbet beta := beta(valf,alfa(55,6,77),alfa(2,4),valf);
i integer;
var1 alfa;
BEGIN
i := vbet(2)(3); -- i va lua valoarea 77
vbet.EXTEND; -- se adauga un element de tip vector la vbet
vbet(5) := alfa(56,33);
vbet(4) := alfa(44,66,77,4321);
vbet(4)(4) := 7; -- 4321 este inlocuit cu 7
vbet(4).EXTEND; -- se adauga un element la al 4-lea element
vbet(4)(5) := 777; -- acest nou element adaugat va fi 777
END;
/

DECLARE
TYPE gama IS TABLE OF VARCHAR2(20);
TYPE delta IS TABLE OF gama;
TYPE teta IS VARRAY(10) OF INTEGER;
TYPE epsi IS TABLE OF teta;
var1 gama := gama('alb','negru');
var2 delta := delta(var1);
var3 epsi := epsi(teta(31,15),teta(1,3,5));
BEGIN
var2.EXTEND;
var2(2) := var2(1);
var2.DELETE(1); -- sterge primul element din var2
/* sterge primul sir de caractere din al doilea
tabel al tabelului imbricat */
var2(2).DELETE(1);
END;
/
DECLARE
TYPE alfa IS TABLE OF INTEGER INDEX BY BINARY_INTEGER;
TYPE beta IS TABLE OF alfa INDEX BY BINARY_INTEGER;
TYPE gama IS VARRAY(10) OF VARCHAR2(30);
TYPE delt IS TABLE OF gama INDEX BY BINARY_INTEGER;
var1 gama := gama('alb','negru');


25

var2 beta;
var3 delt;
var4 alfa;
var5 alfa; -- tabel null
BEGIN
var4(1) := 324;
var4(2) := 222;
var4(42) := 333;
var2(27) := var4;
var3(39) := gama(77,76,89,908);
-- var2(40)(3) := 55; eroare nu exista elementul 40 in var2
var2(40) := var5; -- asignez un tabel null
var2(40)(3) := 55; -- corect
END;
/
Prelucrarea colectiilor
O colectie poate fi exploatata fie n ntregime (atomic) utiliznd comenzi LMD, fie pot fi prelucrate
elemente individuale dintr-o colectie (piecewise updates) utiliznd operatori SQL sau anumite facilitati oferite de
PL/SQL.
Comanda INSERT permite inserarea unei colectii ntr-o linie a unui tabel. Colectia trebuie sa fie creata si
initializata anterior. Comanda UPDATE este folosita pentru modificarea unei colectii stocate, iar DELETE poate
sterge o linie ce contine o colectie. Colectiile din baza de date pot fi regasite n variabile PL/SQL, utiliznd
comanda SELECT.
Exemplu:
CREATE OR REPLACE TYPE operalist AS VARRAY(10) OF NUMBER(4);
CREATE TABLE gal_ope (
cod_galerie NUMBER(10),
nume_galerie VARCHAR2(20),
info operalist);
DECLARE
v_opera operalist := operalist (777, 888, 999);
v_info_op operalist := operalist (7007);
v_info gal_ope.info%TYPE;
v_cod gal_ope.cod_galerie%TYPE;
BEGIN
INSERT INTO gal_ope
VALUES (4567, 'Impresionisti', operalist(4567,4987));
INSERT INTO gal_ope
VALUES (2345, 'Cubism', v_opera);
INSERT INTO gal_ope
VALUES (123, 'Alfa', v_info_op);
SELECT info
INTO v_info
FROM gal_ope
WHERE cod_galerie = v_cod;
END;
Un vector stocat ntr-un tabel este prelucrat ca un ntreg (nu pot fi modificate elemente individuale). Prin
urmare, elementele individuale ale unui vector nu pot fi referite n comenzile INSERT, UPDATE sau DELETE.
Pentru referirea acestora trebuie utilizate comenzi procedurale PL/SQL. Pentru a modifica un vector, el trebuie
selectat ntr-o variabila PL/SQL a carei valoare poate fi modificata si apoi reinserata n tabel.
Tablourile imbricate depuse n baza de date sunt mai flexibile, deoarece pot fi prelucrate fie n ntregime,


26

fie ca elemente individuale. n fiecare caz pot fi utilizate numai comenzi SQL. Se pot face reactualizari sau
inserari asupra tablourilor imbricate care dau o valoare noua pentru ntreaga colectie sau se pot face inserari,
stergeri, reactualizari de elemente particulare din colectie.
O colectie poate fi asignata altei colectii prin comenzile INSERT, UPDATE, FETCH, SELECT,
instructiunea de atribuire sau prin apelul unui subprogram, dar colectiile trebuie sa fie de acelasi tip. Daca unei
colectii i se asigneaza o colectie atomic null, aceasta devine atomic null si trebuie reinitializata.
n Oracle8i a fost introdus operatorul TABLE, ce permite prelucrarea elementelor unui tablou imbricat
care este stocat ntr-un tabel. Operatorul permite interogarea unei colectii n clauza FROM (la fel ca un tabel).
Operandul lui TABLE este fie numele unei colectii si atunci rezultatul operandului este tot o colectie, fie
este o subinterogare referitoare la o colectie, iar n acest caz, operatorul TABLE returneaza o singura valoare
(coloana) care este un tablou imbricat sau un vector. Prin urmare, lista din clauza SELECT a subcererii trebuie sa
aiba un singur articol. Subcererea nu poate returna colectii pentru mai multe linii.
Exemplu:
Se presupune ca tabelul opera are o coloana info de tip tablou imbricat. Acest tablou are doua
componente n care pentru fiecare opera de arta sunt depuse numele articolului referitor la opera respectiva si
revista n care a aparut. Sa se insereze o linie n tabelul imbricat.
INSERT INTO TABLE (SELECT info
FROM opera
WHERE titlu = 'Primavara')
VALUES ('Pictura moderna', 'Orizonturi');
Listarea codului fiecarei opere de arta si a colectiei articolelor referitoare la aceste opere de arta se face
prin comanda:
SELECT a.cod_opera, b.*
FROM opera a, TABLE (a.info) b;
Pentru tablouri imbricate pe mai multe niveluri, operatiile LMD pot fi facute atomic sau pe elemente
individuale, iar pentru vectori pe mai multe niveluri, operatiile pot fi facute numai atomic.
Pentru prelucrarea unei colectii locale se folosesc operatorii TABLE si CAST. CAST are forma sintactica:
CAST (nume_colectie AS tip_colectie)
Operanzii lui CAST sunt o colectie declarata local (de exemplu, ntr-un bloc PL/SQL anonim) si un tip
colectie SQL. CAST converteste colectia locala la tipul specificat. n felul acesta, o colectie poate fi prelucrata ca
si cum ar fi un tabel SQL al bazei de date.
Metodele unei colectii
PL/SQL ofera subprograme numite metode (methods), care opereaza asupra unei colectii. Acestea pot fi
apelate numai din comenzi procedurale, si nu din SQL.
Metodele sunt apelate prin expresia:
nume_colectie.nume_metoda [ (parametri) ]
Metodele care se pot aplica colectiilor PL/SQL sunt urmatoarele:
COUNT returneaza numarul curent de elemente ale unei colectii PL/SQL;
DELETE(n) sterge elementul n dintr-o colectie PL/SQL; DELETE(m, n) sterge toate elementele
avnd indecsii ntre m si n; DELETE sterge toate elementele unei colectii PL/SQL (nu este valida
pentru tipul varrays);
EXISTS(n) returneaza TRUE daca exista al n-lea element al unei colectii PL/SQL (altfel, returneaza
FALSE, chiar daca elementul este null);
FIRST, LAST returneaza indicele primului, respectiv ultimului element din colectie;


27

NEXT(n), PRIOR(n) returneaza indicele elementului urmator, respectiv precedent celui de rang n din
colectie, iar daca nu exista un astfel de element returneaza valoarea null;
EXTEND adauga elemente la sfrsitul unei colectii: EXTEND adauga un element null la sfrsitul
colectiei, EXTEND(n) adauga n elemente null, EXTEND(n, i) adauga n copii ale elementului de rang
i (nu este valida pentru tipul index-by tables);
LIMIT returneaza numarul maxim de elemente ale unei colectii (cel de la declarare) pentru tipul
vector si null pentru tablouri imbricate (nu este valida pentru tipul index-by tables);
TRIM sterge elementele de la sfrsitul unei colectii: TRIM sterge ultimul element, TRIM(n) sterge
ultimele n elemente (nu este valida pentru tipul index-by tables). Similar metodei EXTEND, metoda
TRIM opereaza asupra dimensiunii interne a tabloului imbricat.
EXISTS este singura metoda care poate fi aplicata unei colectii atomice null. Orice alta metoda
declanseaza exceptia COLLECTION_IS_NULL.
COUNT, EXISTS, FIRST, LAST, NEXT, PRIOR si LIMIT sunt functii, iar restul sunt proceduri PL/SQL.
Bulk bind
n exemplul care urmeaza, comanda DELETE este trimisa motorului SQL pentru fiecare iteratie a
comenzii FOR.
Exemplu:
DECLARE
TYPE nume IS VARRAY(20) OF NUMBER;
alfa nume := nume(10,20,70); -- coduri ale galeriilor
BEGIN
FOR j IN alfa.FIRST..alfa.LAST LOOP
DELETE FROM opera
WHERE cod_galerie = alfa (j);
END LOOP;
END;
Pentru a realiza mai rapid aceasta operatie, ar trebui sa existe posibilitatea de a sterge (prelucra) ntreaga
colectie si nu elemente individuale. Mecanismul care permite acest lucru este cunoscut sub numele bulk bind.
n timpul compilarii, motorul PL/SQL asociaza identificatoriilor o adresa, un tip de date si o valoare.
Acest proces este numit binding.
Comenzile SQL din blocurile PL/SQL sunt trimise motorului SQL pentru a fi executate. Motorul SQL
poate trimite date napoi motorului PL/SQL (de exemplu, ca rezultat al unei interogari). De multe ori, datele care
trebuie prelucrate apartin unei colectii, iar colectia este iterata printr-un ciclu FOR. Prin urmare, transferul (n
ambele sensuri) ntre SQL si PL/SQL are loc pentru fiecare linie a colectiei.
ncepnd cu Oracle8i exista posibilitatea ca toate liniile unei colectii sa fie transferate simultan printr-o
singura operatie. Procedeul este numit bulk bind si este realizat cu ajutorul comenzii FORALL, ce poate fi
folosita cu orice tip de colectie.
Comanda FORALL are sintaxa:
FORALL index I N lim_inf..lim_sup
comanda_sql;
Motorul SQL executa comanda_sql o singura data pentru toate valorile indexului. Comanda_sql este una
dintre instructiunile INSERT, UPDATE, DELETE care refera elementele uneia sau mai multor colectii. Variabila
index poate fi referita numai n comanda FORALL si numai ca indice de colectie.
Exemplul care urmeaza optimizeaza problema anterioara, n sensul ca DELETE este trimisa motorului
SQL o singura data, pentru toate liniile colectiei.
Exemplu:
DECLARE


28

TYPE nume IS VARRAY(20) OF NUMBER;
alfa nume := nume(10,20,70); -- coduri ale galeriilor
BEGIN

FORALL j IN alfa.FIRST..alfa.LAST
DELETE FROM opera
WHERE cod_galerie = alfa (j);
END;
Pentru utilizarea comenzii FORALL sunt necesare urmatoarele restrictii:
comanda poate fi folosita numai n programe server-side, altfel apare eroarea this feature is not
supported in client-side programs;
comenziile INSERT, UPDATE, DELETE trebuie sa refere cel putin o colectie;
toate elementele colectiei din domeniul precizat trebuie sa existe (daca, de exemplu, un element a fost
sters, atunci este semnalata o eroare);
indicii colectiilor nu pot sa fie expresii si trebuie sa aiba valori continue.
Daca exista o eroare n procesarea unei linii printr-o operatie LMD de tip bulk, numai acea linie va fi
derulata napoi (rollback).
Cursorul SQL are un atribut compus, %BULK_ROWCOUNT, care numara liniile afectate de iteratiile
comenzii FORALL. %BULK_ROWCOUNT(i) reprezinta numarul de linii procesate de a i-a executie a comenzii
SQL. Atributul nu poate fi parametru ntr-un subprogram si nu poate fi asignat altei colectii.
ncepnd cu Oracle9i, este inclusa o noua clauza n comanda FORALL. Clauza, numita SAVE
EXCEPTIONS, permite ca toate exceptiile care apar n timpul executiei comenzii FORALL sa fie salvate si astfel
procesarea poate sa continue.
n acest context, poate fi utilizat atributul cursor %BULK_EXCEPTIONS pentru a vizualiza informatii
despre aceste exceptii. Atributul actioneaza ca un tablou PL/SQL si are doua cmpuri:
%BULK_EXCEPTIONS(i).ERROR_INDEX, reprezentnd iteratia n timpul careia s-a declansat
exceptia;
%BULK_EXCEPTIONS(i).ERROR_CODE, reprezentnd codul Oracle al erorii respective.
Regasirea rezultatului unei interogari n colectii (nainte de a fi trimisa motorului PL/SQL) se poate obtine
cu ajutorul clauzei BULK COLLECT.
Aceasta clauza poate sa apara n comenzile SELECT INTO (cursoare implicite), FETCH INTO (cursoare
explicite) sau n clauza RETURNING INTO a comenzilor INSERT, UPDATE, DELETE. n cazul cursoarelor
explicite, numarul liniilor ncarcate din baza de date poate fi limitat utiliznd optiunea LIMIT.
Clauza BULK COLLECT are urmatoarea sintaxa:
BULK COLLECT I NTO nume_colectie [, nume_colectie]
Exemplu:
DECLARE
TYPE tip1 IS TABLE OF opera.cod_opera%TYPE;
TYPE tip2 IS TABLE OF opera.titlu%TYPE;
alfa tip1;
beta tip2;
BEGIN

/* motorul SQL incarca in intregime coloanele cod_opera si
titlu in tabelele imbricate, inainte de a returna
tabelele motorului PL/SQL */
SELECT cod_opera, titlu BULK COLLECT INTO alfa,beta
FROM opera;

/* daca exista n opere de arta in stare buna, atunci
alfa va contine codurile celor n opere de arta */


29

DELETE FROM opera WHERE stare = 'buna'
RETURNING cod_opera BULK COLLECT INTO alfa;

END;
Comanda FORALL se poate combina cu clauza BULK COLLECT. Totusi, trebuie subliniat ca ele nu pot
fi folosite simultan n comanda SELECT.



5. Gestiunea cursoarelor


Pentru a procesa o comanda SQL, sistemul Oracle foloseste o zona de memorie cunoscuta sub numele de
zona context (context area). Cnd este procesata o instructiune SQL, server-ul Oracle deschide aceasta zona de
memorie n care comanda este analizata sintactic si executata.
Zona contine informatii necesare procesarii comenzii, cum ar fi:
numarul de rnduri procesate de instructiune;
un pointer catre reprezentarea interna a comenzii;
n cazul unei cereri, multimea rndurilor rezultate n urma executiei acestei comenzi (active set).
Un cursor este un pointer la aceasta zona context. Prin intermediul cursoarelor, un program PL/SQL
poate controla zona context si transformarile petrecute n urma procesarii comenzii.
Exista doua tipuri de cursoare:
implicite, generate de server-ul Oracle cnd n partea executabila a unui bloc PL/SQL apare o
instructiune SQL;
explicite, declarate si definite de catre utilizator atunci cnd o cerere (SELECT), care apare ntr-un
bloc PL/SQL, ntoarce mai multe linii ca rezultat.
Att cursoarele implicite ct si cele explicite au o serie de atribute ale caror valori pot fi folosite n
expresii. Lista atributelor este urmatoarea:
%ROWCOUNT, care este de tip ntreg si reprezinta numarul liniilor ncarcate de cursor;
%FOUND, care este de tip boolean si ia valoarea TRUE daca ultima operatie de ncarcare (FETCH)
dintr-un cursor a avut succes (n cazul cursoarelor explicite) sau daca instructiunea SQL a ntors cel
putin o linie (n cazul cursoarelor implicite);
%NOTFOUND, care este de tip boolean si are semnificatie opusa fata de cea a atributului %FOUND;
%ISOPEN, care este de tip boolean si indica daca un cursor este deschis (n cazul cursoarelor
implicite, acest atribut are ntotdeauna valoarea FALSE, deoarece un cursor implicit este nchis de
sistem imediat dupa executarea instructiunii SQL asociate).
Atributele pot fi referite prin expresia SQL%nume_atribut, n cazul cursoarelor implicite, sau prin
nume_cursor%nume_atribut, n cazul unui cursor explicit. Ele pot sa apara n comenzi PL/SQL, n functii, n
sectiunea de tratare a erorilor, dar nu pot fi utilizate n comenzi SQL.
Cursoare implicite
Cnd se proceseaza o comanda LMD, motorul SQL deschide un cursor implicit. Atributele scalare ale
cursorului implicit (SQL%ROWCOUNT, SQL%FOUND, SQL%NOTFOUND, SQL%ISOPEN) furnizeaza
informatii referitoare la ultima comanda INSERT, UPDATE, DELETE sau SELECT INTO executata. nainte ca
Oracle sa deschida cursorul SQL implicit, atributele acestuia au valoarea null.
n Oracle9i, pentru cursoare implicite a fost introdus atributul compus %BULK_ROWCOUNT, care este
asociat comenzii FORALL. Atributul are semantica unui tablou indexat. Componenta %BULK_ROWCOUNT(j)
contine numarul de linii procesate de a j-a executie a unei comenzi INSERT, DELETE sau UPDATE. Daca a j-a
executie nu afecteaza nici o linie, atunci atributul returneaza valoarea 0. Comanda FORALL si atributul
%BULK_ROWCOUNT au aceiasi indici, deci folosesc acelasi domeniu. Daca %BULK_ROWCOUNT(j) este


30

zero, atributul %FOUND este FALSE.
Exemplu:
n exemplul care urmeaza, comanda FORALL insereaza un numar arbitrar de linii la fiecare iteratie, iar
dupa fiecare iteratie atributul %BULK_ROWCOUNT returneaza numarul acestor linii inserate.
SET SERVEROUTPUT ON
DECLARE
TYPE alfa IS TABLE OF NUMBER;
beta alfa;
BEGIN
SELECT cod_artist BULK COLLECT INTO beta FROM artist;
FORALL j IN 1..beta.COUNT
INSERT INTO tab_art
SELECT cod_artist,cod_opera
FROM opera
WHERE cod_artist = beta(j);
FOR j IN 1..beta.COUNT LOOP
DBMS_OUTPUT.PUT_LINE ('Pentru artistul avand codul ' ||
beta(j) || ' au fost inserate ' || SQL%BULK_ROWCOUNT(j)
|| inregistrari (opere de arta)');
END LOOP;
DBMS_OUTPUT.PUT_LINE ('Numarul total de inregistrari
inserate este '||SQL%ROWCOUNT);
END;
/
SET SERVEROUTPUT OFF
Cursoare explicite
Pentru gestiunea cursoarelor explicite sunt necesare urmatoarele etape:
declararea cursorului (atribuirea unui nume si asocierea cu o comanda SELECT);
deschiderea cursorului pentru cerere (executarea interogarii asociate si determinarea multimii
rezultat);
recuperarea liniilor rezultatului n variabile PL/SQL;
nchiderea cursorului (eliberarea resurselor relative la cursor).
Prin urmare, pentru a utiliza un cursor, el trebuie declarat n sectiunea declarativa a programului, trebuie
deschis n partea executabila, urmnd sa fie utilizat apoi pentru extragerea datelor. Daca nu mai este necesar n
restul programului, cursorul trebuie sa fie nchis.
DECLARE
declarare cursor
BEGI N
deschidere cursor (OPEN)
WHI LE ramn linii de recuperat LOOP
recuperare linie rezultat (FETCH)

END LOOP
nchidere cursor (CLOSE)

END;
Pentru a controla activitatea unui cursor sunt utilizate comenzile DECLARE, OPEN, FETCH si CLOSE.
Declararea unui cursor explicit


31

Prin declaratia CURSOR n cadrul comenzii DECLARE este definit un cursor explicit si este precizata
structura cererii care va fi asociata acestuia.
Declaratia CURSOR are urmatoarea forma sintactica:
CURSOR nume_cursor I S comanda_select
Identificatorul nume_cursor este numele cursorului, iar comanda_select este cererea SELECT care va fi
procesata.
Observatii:
Comanda SELECT care apare n declararea cursorului, nu trebuie sa includa clauza INTO.
Daca se cere procesarea liniilor ntr-o anumita ordine, atunci n cerere este utilizata clauza ORDER
BY.
Variabilele care sunt referite n comanda de selectare trebuie declarate naintea comenzii CURSOR.
Ele sunt considerate variabile de legatura.
Daca n lista comenzii SELECT apare o expresie, atunci pentru expresia respectiva trebuie utilizat un
alias, iar cmpul expresie se va referi prin acest alias.
Numele cursorului este un identificator unic n cadrul blocului, care nu poate sa apara ntr-o expresie
si caruia nu i se poate atribui o valoare.
Deschiderea unui cursor explicit
Comanda OPEN executa cererea asociata cursorului, identifica multimea liniilor rezultat si pozitioneaza
cursorul naintea primei linii.
Deschiderea unui cursor se face prin comanda:
OPEN nume_cursor;
Identificatorul nume_cursor reprezinta numele cursorului ce va fi deschis.
La deschiderea unui cursor se realizeaza urmatoarele operatii:
se evalueaza cererea asociata (sunt examinate valorile variabilelor de legatura ce apar n declaratia
cursorului);
este determinata multimea rezultat (active set) prin executarea cererii SELECT, avnd n vedere
valorile de la pasul anterior;
pointer-ul este pozitionat la prima linie din multimea activa.
ncarcarea datelor dintr-un cursor explicit
Comanda FETCH regaseste liniile rezultatului din multimea activa. FETCH realizeaza urmatoarele
operatii:
avanseaza pointer-ul la urmatoarea linie n multimea activa (pointer-ul poate avea doar un sens de
deplasare de la prima spre ultima nregistrare);
citeste datele liniei curente n variabile PL/SQL;
daca pointer-ul este pozitionat la sfrsitul multimii active atunci se iese din bucla cursorului.
Comanda FETCH are urmatoarea sintaxa:
FETCH nume_cursor
I NTO {nume_variabila [, nume_variabila] | nume_nregistrare};
Identificatorul nume_cursor reprezinta numele unui cursor declarat si deschis anterior. Variabila sau lista
de variabile din clauza INTO trebuie sa fie compatibila (ca ordine si tip) cu lista selectata din cererea asociata
cursorului.
La un moment dat, comanda FETCH regaseste o singura linie. Totusi, n ultimele versiuni Oracle pot fi
ncarcate mai multe linii (la un moment dat) ntr-o colectie, utiliznd clauza BULK COLLECT.
Exemplu:


32

n exemplul care urmeaza se ncarca date dintr-un cursor n doua colectii.
DECLARE
TYPE ccopera IS TABLE OF opera.cod_opera%TYPE;
TYPE ctopera IS TABLE OF opera.titlu%TYPE;
cod1 ccopera;
titlu1 ctopera;
CURSOR alfa IS SELECT cod_opera, titlu
FROM opera
WHERE stil = 'impresionism';
BEGIN
OPEN alfa;
FETCH alfa BULK COLLECT INTO cod1, titlu1;

CLOSE alfa;
END;
nchiderea unui cursor explicit
Dupa ce a fost procesata multimea activa, cursorul trebuie nchis. Prin aceasta operatie, PL/SQL este
informat ca programul a terminat folosirea cursorului si resursele asociate acestuia pot fi eliberate. Aceste
resurse includ spatiul utilizat pentru memorarea multimii active si spatiul temporar folosit pentru determinarea
multimii active.
Cursorul va fi nchis prin comanda CLOSE, care are urmatoarea sintaxa:
CLOSE nume_cursor;
Identificatorul nume_cursor este numele unui cursor deschis anterior.
Pentru a reutiliza cursorul este suficient ca acesta sa fie redeschis. Daca se ncearca ncarcarea datelor
dintr-un cursor nchis, atunci apare exceptia INVALID_CURSOR. Un bloc PL/SQL poate sa se termine fara a
nchide cursoarele, dar acest lucru nu este indicat, deoarece este bine ca resursele sa fie eliberate.
Exemplu:
Pentru toti artistii care au opere de arta expuse n muzeu sa se insereze n tabelul temp informatii
referitoare la numele acestora si anul nasterii.
DECLARE
v_nume artist.nume%TYPE;
v_an_nas artist.an_nastere%TYPE;
CURSOR info IS
SELECT DISTINCT nume, an_nastere
FROM artist;
BEGIN
OPEN info;
LOOP
FETCH info INTO v_nume, v_an_nas;
EXIT WHEN info%NOTFOUND;
INSERT INTO temp
VALUES (v_nume || TO_CHAR(v_an_nas));
END LOOP;
CLOSE info;
COMMIT;
END;
Valorile atributelor unui cursor explicit sunt prezentate n urmatorul tabel:

%FOUND %I SOPEN %NOTFOUND %ROWCOUNT
OPEN nainte Exceptie False Exceptie Exceptie


33

Dupa Null True Null 0

Prima
ncarcare
nainte
Dupa
Null
True
True
True
Null
False
0
1

Urmatoarea
ncarcare
nainte
Dupa
True
True
True
True
False
False
1
Depinde de date

Ultima
ncarcare
nainte
Dupa
True
False
True
True
False
True
Depinde de date
Depinde de date

CLOSE nainte
Dupa
False
Exceptie
True
False
True
Exceptie
Depinde de date
Exceptie


Dupa prima ncarcare, daca multimea rezultat este vida, %FOUND va fi FALSE, %NOTFOUND va fi
TRUE, iar %ROWCOUNT este 0.
ntr-un pachet poate fi separata specificarea unui cursor de corpul acestuia. Cursorul va fi declarat n
specificatia pachetului prin comanda:
CURSOR nume_cursor [ (parametru [, parametru]) ]
RETURN tip_returnat;
n felul acesta va creste flexibilitatea programului, putnd fi modificat doar corpul cursorului, fara a
schimba specificatia.
Exemplu:
CREATE PACKAGE exemplu AS
CURSOR alfa (p_valoare_min NUMBER) RETURN opera%ROWTYPE;
-- declaratie specificatie cursor

END exemplu;
CREATE PACKAGE BODY exemplu AS
CURSOR alfa (p_valoare_min NUMBER) RETURN opera%ROWTYPE IS
SELECT * FROM opera WHERE valoare > p_valoare_min;
-- definire corp cursor

END exemplu;
Procesarea liniilor unui cursor explicit
Pentru procesarea diferitelor linii ale unui cursor explicit se foloseste operatia de ciclare (LOOP, WHILE,
FOR), prin care la fiecare iteratie se va ncarca o noua linie. Comanda EXIT poate fi utilizata pentru iesirea din
ciclu, iar valoarea atributului %ROWCOUNT pentru terminarea ciclului.
Procesarea liniilor unui cursor explicit se poate realiza si cu ajutorul unui ciclu FOR special, numit ciclu
cursor. Pentru acest ciclu este necesara doar declararea cursorului, operatiile de deschidere, ncarcare si nchidere
ale acestuia fiind implicite.
Comanda are urmatoarea sintaxa:
FOR nume_nregistrare I N nume_cursor LOOP
secventa_de_instructiuni;
END LOOP;
Variabila nume_nregistrare (care controleaza ciclul) nu trebuie declarata. Domeniul ei este doar ciclul


34

respectiv.
Pot fi utilizate cicluri cursor speciale care folosesc subcereri, iar n acest caz nu mai este necesara nici
declararea cursorului. Exemplul care urmeaza este concludent n acest sens.
Exemplu:
Sa se calculeze, utiliznd un ciclu cursor cu subcereri, valoarea operelor de arta expuse ntr-o galerie al
carei cod este introdus de la tastatura. De asemenea, sa se obtina media valorilor operelor de arta expuse n
galeria respectiva.
SET SERVEROUTPUT ON
ACCEPT p_galerie PROMPT 'Dati codul galeriei:'
DECLARE
v_cod_galerie galerie.cod_galerie%TYPE:=&p_galerie;
val NUMBER;
media NUMBER;
i INTEGER;
BEGIN
val:=0;
i:=0;
FOR numar_opera IN
(SELECT cod_opera, valoare
FROM opera
WHERE cod_galerie = v_cod_galerie) LOOP
val := val + numar_opera.valoare;
i := i+1;
END LOOP;--nchidere implicita
DBMS_OUTPUT.PUT_LINE('Valoarea operelor de arta din galeria
cu numarul ' || TO_CHAR(v_cod_galerie) || ' este ' ||
TO_CHAR(val));
IF i=0 THEN
DBMS_OUTPUT.PUT_LINE('Galeria nu are opere de arta');
ELSE
media := val/i;
DBMS_OUTPUT.PUT_LINE('Media valorilor operelor de arta
din galeria cu numarul ' || TO_CHAR(v_cod_galerie) ||
' este ' || TO_CHAR(media));
END IF;
END;
/
SET SERVEROUTPUT OFF
Cursoare parametrizate
Unei variabile de tip cursor i corespunde o comanda SELECT, care nu poate fi schimbata pe parcursul
programului. Pentru a putea lucra cu niste cursoare ale caror comenzi SELECT atasate depind de parametri ce
pot fi modificati la momentul executiei, n PL/SQL s-a introdus notiunea de cursor parametrizat. Prin urmare, un
cursor parametrizat este un cursor n care comanda SELECT atasata depinde de unul sau mai multi parametri.
Transmiterea de parametri unui cursor parametrizat se face n mod similar procedurilor stocate. Un astfel
de cursor este mult mai usor de interpretat si de ntretinut, oferind si posibilitatea reutilizarii sale n blocul
PL/SQL.
Declararea unui astfel de cursor se face respectnd urmatoarea sintaxa:
CURSOR nume_cursor [ (nume_parametru[, nume_parametru ] ) ]


35

[RETURN tip_returnat]
IS comanda_select;
Identificatorul comanda_select este o instructiune SELECT fara clauza INTO, tip_returnat reprezinta un
tip nregistrare sau linie de tabel, iar nume_parametru are sintaxa:
nume_parametru [I N] tip_parametru [ {:= | DEFAULT} expresie]
n aceasta declaratie, atributul tip_parametru reprezinta tipul parametrului, care este un tip scalar.
Parametrii formali sunt de tip IN si, prin urmare, nu pot returna valori parametrilor actuali. Ei nu suporta
constrngerea NOT NULL.
Deschiderea unui astfel de cursor se face asemanator apelului unei functii, specificnd lista parametrilor
actuali ai cursorului. n determinarea multimii active se vor folosi valorile actuale ale acestor parametri.
Sintaxa pentru deschiderea unui cursor parametrizat este:
OPEN nume_cursor [ (valoare_parametru [, valoare_parametru] ) ];
Parametrii sunt specificati similar celor de la subprograme. Asocierea dintre parametrii formali si cei
actuali se face prin:
pozitie parametrii formali si actuali sunt separati prin virgula;
nume parametrii actuali sunt aranjati ntr-o ordine arbitrara, dar cu o corespondenta de forma
parametru formal => parametru actual.
Daca n definitia cursorului, toti parametrii au valori implicite (DEFAULT), cursorul poate fi deschis fara
a specifica vreun parametru.
Exemplu:
Utiliznd un cursor parametrizat sa se obtina codurile operelor de arta din fiecare sala, identificatorul salii
si al galeriei. Rezultatele sa fie inserate n tabelul mesaje.
DECLARE
v_cod_sala sala.cod_sala%TYPE;
v_cod_galerie galerie.cod_galerie%TYPE;
v_car VARCHAR2(75);
CURSOR sala_cursor IS
SELECT cod_sala,cod_galerie
FROM sala;
CURSOR ope_cursor (v_id_sala NUMBER,v_id_galerie NUMBER) IS
SELECT cod_opera || cod_sala || cod_galerie
FROM opera
WHERE cod_sala = v_id_sala
AND cod_galerie = v_id_galerie;
BEGIN
OPEN sala_cursor;
LOOP
FETCH sala_cursor INTO v_cod_sala,v_cod_galerie;
EXIT WHEN sala_cursor%NOTFOUND;
IF ope_cursor%ISOPEN THEN
CLOSE ope_cursor;
END IF;
OPEN ope_cursor (v_cod_sala, v_cod_galerie);
LOOP
FETCH ope_cursor INTO v_car;
EXIT WHEN ope_cursor%NOTFOUND;
INSERT INTO mesaje (rezultat)
VALUES (v_car);
END LOOP;
CLOSE ope_cursor;


36

END LOOP;
CLOSE sala_cursor;
COMMIT;
END;
Cursoare SELECT FOR UPDATE
Uneori este necesara blocarea liniilor nainte ca acestea sa fie sterse sau reactualizate. Blocarea se poate
realiza (atunci cnd cursorul este deschis) cu ajutorul comenzii SELECT care contine clauza FOR UPDATE.
Declararea unui astfel de cursor se face conform sintaxei:
CURSOR nume_cursor IS
comanda_select
FOR UPDATE [OF lista_cmpuri] [NOWAIT];
Identificatorul lista_cmpuri este o lista ce include cmpurile tabelului care vor fi modificate. Atributul
NOWAIT returneaza o eroare daca liniile sunt deja blocate de alta sesiune. Liniile unui tabel sunt blocate doar
daca clauza FOR UPDATE se refera la coloane ale tabelului respectiv.
n momentul deschiderii unui astfel de cursor, liniile corespunzatoare multimii active, determinate de
clauza SELECT, sunt blocate pentru operatii de scriere (reactualizare sau stergere). n felul acesta este realizata
consistenta la citire a sistemului. De exemplu, aceasta situatie este utila cnd se reactualizeaza o valoare a unei
linii si trebuie avuta siguranta ca linia nu este schimbata de alt utilizator naintea reactualizarii. Prin urmare, alte
sesiuni nu pot schimba liniile din multimea activa pna cnd tranzactia nu este permanentizata sau anulata. Daca
alta sesiune a blocat deja liniile din multimea activa, atunci comanda SELECT FOR UPDATE va astepta (sau
nu) ca aceste blocari sa fie eliberate. Pentru a trata aceasta situatie se utilizeaza clauza WAIT, respectiv
NOWAIT.
n Oracle9i este utilizata sintaxa:
SELECT FROM FOR UPDATE [OF lista_campuri]
[ {WAIT n | NOWAIT} ];
Valoarea lui n reprezinta numarul de secunde de asteptare. Daca liniile nu sunt deblocate n n secunde,
atunci se declanseaza eroarea ORA-30006, respectiv eroarea ORA-00054, dupa cum este specificata clauza
WAIT, respectiv NOWAIT. Daca nu este specificata nici una din clauzele WAIT sau NOWAIT, sistemul asteapta
pna ce linia este deblocata si atunci returneaza rezultatul comenzii SELECT.
Daca un cursor este declarat cu clauza FOR UPDATE, atunci comenzile DELETE si UPDATE
corespunzatoare trebuie sa contina clauza WHERE CURRENT OF nume_cursor.
Aceasta clauza refera linia curenta care a fost gasita de cursor, permitnd ca reactualizarile si stergerile sa
se efectueze asupra acestei linii, fara referirea explicita a cheii primare sau pseudocoloanei ROWID. De subliniat
ca instructiunile UPDATE si DELETE vor reactualiza numai coloanele listate n clauza FOR UPDATE.
Pseudocoloana ROWID poate fi utilizata daca tabelul referit n interogare nu are o cheie primara
specificata. ROWID-ul fiecarei linii poate fi ncarcat ntr-o variabila PL/SQL (declarata de tipul ROWID sau
UROWID), iar aceasta variabila poate fi utilizata n clauza WHERE (WHERE ROWID = v_rowid).
Dupa nchiderea cursorului este necesara comanda COMMIT pentru a realiza scrierea efectiva a
modificarilor, deoarece cursorul lucreaza doar cu niste copii ale liniilor reale existente n tabele.
Deoarece blocarile implicate de clauza FOR UPDATE vor fi eliberate de comanda COMMIT, nu este
recomandata utilizarea comenzii COMMIT n interiorul ciclului n care se fac ncarcari de date. Orice FETCH
executat dupa COMMIT va esua. n cazul n care cursorul nu este definit prin SELECTFOR UPDATE, nu sunt
probleme n acest sens si, prin urmare, n interiorul ciclului unde se fac schimbari ale datelor poate fi utilizat un
COMMIT.
Exemplu:
Sa se dubleze valoarea operelor de arta pictate pe pnza care au fost achizitionate nainte de 1 ianuarie
1956.


37

DECLARE
CURSOR calc IS
SELECT *
FROM opera
WHERE material = 'panza'
AND data_achizitie <= TO_DATE('01-JAN-56','DD-MON-YY')
FOR UPDATE OF valoare NOWAIT;
BEGIN
FOR x IN calc LOOP
UPDATE opera
SET valoare = valoare*2
WHERE CURRENT OF calc;
END LOOP;
-- se permanentizeaza actiunea si se elibereaza blocarea
COMMIT;
END;
Cursoare dinamice
Toate exemplele considerate anterior se refera la cursoare statice. Unui cursor static i se asociaza o
comanda SQL care este cunoscuta n momentul n care blocul este compilat.
n PL/SQL a fost introdusa variabila cursor, care este de tip referinta. 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.
n momentul declararii, variabilele cursor nu solicita o comanda SQL asociata. n acest fel, diferite
comenzi SQL pot fi asociate variabilelor cursor, la diferite momente de timp. Acest tip de variabila trebuie
declarata, deschisa, ncarcata si nchisa n mod similar unui cursor static.
Variabilele cursor sunt dinamice deoarece li se pot asocia diferite interogari atta timp ct coloanele
returnate de fiecare interogare corespund declaratiei variabilei cursor.
Aceste variabile sunt utile n transmiterea seturilor de rezultate ntre subprograme PL/SQL stocate si
diferiti clienti. De exemplu, un client OCI, o aplicatie Oracle Forms si server-ul Oracle pot referi aceeasi zona
de lucru (care contine multimea rezultat). Pentru a reduce traficul n retea, o variabila cursor poate fi declarata pe
statia client, deschisa si se pot ncarca date din ea pe server, apoi poate continua ncarcarea, dar de pe statia
client etc.
Pentru a crea o variabila cursor este necesara definirea unui tip REF CURSOR, urmnd apoi declararea
unei variabile de tipul respectiv. Dupa ce variabila cursor a fost declarata, ea poate fi deschisa pentru orice cerere
SQL care returneaza date de tipul declarat.
Sintaxa pentru declararea variabilei cursor este urmatoarea:
TYPE tip_ref_cursor I S REF CURSOR [RETURN tip_returnat];
var_cursor tip_ref_cursor;
Identificatorul var_cursor este numele variabilei cursor, tip_ref_cursor este un nou tip de data ce poate fi
utilizat n declaratiile urmatoare ale variabilelor cursor, iar tip_returnat este un tip nregistrare sau tipul unei linii
dintr-un tabel al bazei. Acest tip corespunde coloanelor returnate de catre orice cursor asociat variabilelor cursor
de tipul definit. Daca lipseste clauza RETURN, cursorul poate fi deschis pentru orice cerere SELECT.
Daca variabila cursor apare ca parametru ntr-un subprogram, atunci trebuie specificat tipul parametrului
(tipul REF CURSOR) si forma acestuia (IN sau IN OUT).
Exista anumite restrictii referitoare la utilizarea variabilelor cursor:
nu pot fi declarate ntr-un pachet;
cererea asociata variabilei cursor nu poate include clauza FOR UPDATE (restrictia dispare n
Oracle9i);


38

nu poate fi asignata valoarea null unei variabile cursor;
nu poate fi utilizat tipul REF CURSOR pentru a specifica tipul unei coloane n comanda CREATE
TABLE;
nu pot fi utilizati operatorii de comparare pentru a testa egalitatea, inegalitatea sau valoarea null a
variabilelor cursor;
nu poate fi utilizat tipul REF CURSOR pentru a specifica tipul elementelor unei colectii (varray,
nested table);
nu pot fi folosite cu SQL dinamic n Pro*C/C++.
n cazul variabilelor cursor, instructiunile de deschidere (OPEN), ncarcare (FETCH), nchidere (CLOSE)
vor avea o sintaxa similara celor comentate anterior.
Comanda OPENFOR asociaza o variabila cursor cu o cerere multilinie, executa cererea, identifica
multimea rezultat si pozitioneaza cursorul la prima linie din multimea rezultat. Sintaxa comenzii este:
OPEN {variabila_cursor | :variabila_cursor_host}
FOR {cerere_select |
sir_dinamic [USI NG argument_bind [, argument_bind ] ] };
Identificatorul variabila_cursor specifica o variabila cursor declarata anterior, dar fara optiunea
RETURN tip, cerere_select este interogarea pentru care este deschisa variabila cursor, iar sir_dinamic este o
secventa de caractere care reprezinta cererea multilinie.
Optiunea sir_dinamic este specifica prelucrarii dinamice a comenzilor, iar posibilitatile oferite de SQL
dinamic vor fi analizate ntr-un capitol separat. Identificatorul :variabila_cursor_host reprezinta o variabila
cursor declarata ntr-un mediu gazda PL/SQL (de exemplu, un program OCI).
Comanda OPENFOR poate deschide acelasi cursor pentru diferite cereri. Nu este necesara nchiderea
variabilei cursor nainte de a o redeschide. Daca se redeschide variabila cursor pentru o noua cerere, cererea
anterioara este pierduta.
Exemplu:
CREATE OR REPLACE PACKAGE alfa AS
TYPE ope_tip IS REF CURSOR RETURN opera%ROWTYPE;
PROCEDURE deschis_ope (ope_var IN OUT ope_tip,
alege IN NUMBER);
END alfa;
CREATE OR REPLACE PACKAGE BODY alfa AS
PROCEDURE deschis_ope (ope_var IN OUT ope_tip,
alege IN NUMBER) IS
BEGIN
IF alege = 1 THEN
OPEN ope_var FOR SELECT * FROM opera;
ELSIF alege = 2 THEN
OPEN ope_var FOR SELECT * FROM opera WHERE valoare > 200;
ELSIF alege = 3 THEN
OPEN ope_var FOR SELECT * FROM opera WHERE valoare = 777;
END IF;
END deschis_ope;
END alfa;
Comanda FETCH returneaza o linie din multimea rezultat a cererii, atribuie valorile returnate de cerere
componentelor din lista specificata prin clauza INTO si avanseaza cursorul la urmatoarea linie. Comanda are
urmatoarea sintaxa:
FETCH {variabila_cursor | :variabila_cursor_host}
I NTO {variabila [, variabila ] | nregistrare}
[BULK COLLECT I NTO {nume_colectie [, nume_colectie ] } |


39

{nume_array_host [, nume_array_host ] }
[LIMIT expresie_numerica] ];
Clauza BULK COLLECT INTO permite ncarcarea tuturor liniilor simultan n una sau mai multe colectii.
Atributul nume_colectie indica o colectie declarata anterior, n care sunt depuse valorile respective, iar
nume_array_host identifica un vector declarat ntr-un mediu gazda PL/SQL si trimis lui PL/SQL ca variabila de
legatura. Prin clauza LIMIT se limiteaza numarul liniilor ncarcate din baza de date.
Exemplu:
DECLARE
TYPE alfa IS REF CURSOR RETURN opera%ROWTYPE;
TYPE beta IS TABLE OF opera.titlu%TYPE;
TYPE gama IS TABLE OF opera.valoare%TYPE;
var1 alfa;
var2 beta;
var3 gama;
BEGIN
OPEN var1 FOR SELECT titlu, valoare FROM opera;
FETCH var1 BULK COLLECT INTO var2, var3;

CLOSE var1;
END;
Comanda CLOSE dezactiveaza variabila cursor precizata. Ea are sintaxa:
CLOSE {variabila_cursor | :variabila_cursor_host}
Cursoarele si variabilele cursor nu sunt interoperabile. Nu poate fi folosita una dintre ele, atunci cnd este
asteptata cealalta.
Expresie cursor
n Oracle9i a fost introdus conceptul de expresie cursor (cursor expression), care returneaza un cursor
imbricat (nested cursor).
Expresia cursor are urmatoarea sintaxa:
CURSOR (subcerere)
Fiecare linie din multimea rezultat poate contine valori uzuale si cursoare generate de subcereri. PL/SQL
accepta cereri care au expresii cursor n cadrul unei declaratii cursor, declaratii REF CURSOR si a variabilelor
cursor.
Prin urmare, expresia cursor poate sa apara ntr-o comanda SELECT ce este utilizata pentru deschiderea
unui cursor dinamic. De asemenea, expresiile cursor pot fi folosite n cereri SQL dinamice sau ca parametri
actuali ntr-un subprogram.
Un cursor imbricat este ncarcat automat atunci cnd liniile care l contin sunt ncarcate din cursorul
parinte. El este nchis daca:
este nchis explicit de catre utilizator;
cursorul parinte este reexecutat, nchis sau anulat;
apare o eroare n timpul unei ncarcari din cursorul parinte.
Exista cteva restrictii asupra folosirii unei expresii cursor:
nu poate fi utilizata cu un cursor implicit;
poate sa apara numai ntr-o comanda SELECT care nu este imbricata n alta cerere (exceptnd cazul
n care este o subcerere chiar a expresiei cursor) sau ca argument pentru functii tabel, n clauza
FROM a lui SELECT;
nu poate sa apara n interogarea ce defineste o vizualizare;


40

nu se pot efectua operatii BIND sau EXECUTE cu aceste expresii.
Exemplu:
Sa se defineasca un cursor care furnizeaza codurile operelor expuse n cadrul unei expozitii avnd un cod
specificat (val_cod) si care se desfasoara ntr-o localitate precizata (val_oras). Sa se afiseze data cnd a avut loc
vernisajul acestei expozitii.
n acest caz cursorul returneaza doua coloane, cea de-a doua coloana fiind un cursor imbricat.
CURSOR alfa (val_cod NUMBER, val_oras VARCHAR2(20)) IS
SELECT l.datai,
CURSOR (SELECT d.cod_expo,
CURSOR (SELECT f.cod_opera
FROM figureaza_in f
WHERE f.cod_expo=d.cod_expo) AS xx
FROM expozitie d
WHERE l.cod_expo = d.cod_expo) AS yy
FROM locped l
WHERE cod_expo = val_cod AND nume_oras= val_oras;
Exemplu:
Sa se listeze numele galeriilor din muzeu si pentru fiecare galerie sa se afiseze numele salilor din galeria
respectiva.
Sunt prezentate doua variante de rezolvare. Prima varianta reprezinta o implementare simpla utiliznd
programarea secventiala clasica, iar a doua utilizeaza expresii cursor pentru rezolvarea acestei probleme.
Varianta 1:
BEGIN
FOR gal IN (SELECT cod_galerie, nume_galerie
FROM galerie)
LOOP
DBMS_OUTPUT.PUT_LINE (gal.nume_galerie);
FOR sal IN (SELECT cod_sala, nume_sala
FROM sala
WHERE cod_galerie = gal.cod.galerie)
LOOP
DBMS_OUTPUT.PUT_LINE (sal.nume_sala);
END LOOP;
END LOOP;
END;
Varianta 2:
DECLARE
CURSOR c_gal IS
SELECT nume_galerie,
CURSOR (SELECT nume_sala
FROM sala s
WHERE s.cod_galerie = g.cod_galerie)
FROM galerie g;
v_nume_gal galerie.nume_galerie%TYPE;
v_sala SYS.REFCURSOR;
TYPE sala_nume IS TABLE OF sala.nume_sala%TYPE
INDEX BY BINARY_INTEGER;
v_nume_sala sala_nume;
BEGIN
OPEN c_gal;
LOOP
FETCH c_gal INTO v_nume_gal, v_sala;
EXIT WHEN c_gal%NOTFOUND;


41

DBMS_OUTPUT.PUT_LINE (v_nume_gal);
FETCH v_sala BULK COLLECT INTO v_nume_sala;
FOR ind IN v_nume_sala.FIRST..v_nume_sala.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (v_nume_sala (ind));
END LOOP;
END LOOP;
CLOSE c_gal;
END;



5. Modularizarea aplicatiilor prin utilizarea
subprogramelor


Notiunea de subprogram (procedura sau functie) a fost conceputa cu scopul de a grupa o multime de
comenzi SQL cu instructiuni procedurale, pentru a construi o unitate logica de tratare a unei anumite probleme.
n general, procedurile sunt folosite pentru a realiza o actiune, iar functiile pentru a calcula o valoare.
Unitatile de program care pot fi create n PL/SQL sunt:
subprograme locale (definite n partea declarativa a unui bloc PL/SQL sau a unui alt subprogram);
subprograme independente (stocate n baza de date si considerate obiecte ale acesteia);
subprograme mpachetate (definite ntr-un pachet care ncapsuleaza proceduri si functii).
Procedurile si functiile stocate sunt unitati de program PL/SQL apelabile (compilate), care exista ca
obiecte n schema bazei de date Oracle. Recuperarea unui subprogram (n cazul unei corectii) nu cere
recuperarea ntregii aplicatii. Subprogramul, ncarcat n memorie pentru a fi executat, poate fi partajat ntre
aplicatiile care l solicita.
Este important de facut distinctie ntre procedurile stocate si procedurile locale (declarate si folosite n
blocuri anonime).
Procedurile si functiile stocate, care sunt compilate si stocate n baza de date, nu mai trebuie sa fie
compilate la fiecare executie, n timp ce procedurile locale sunt compilate de fiecare data cnd este
executat blocul care le contine.
Procedurile declarate si apelate n blocuri anonime sunt temporare (ele nu mai exista dupa ce blocul
anonim a fost executat complet). O procedura stocata (creata cu CREATE PROCEDURE sau
continuta ntr-un pachet) este permanenta, n sensul ca ea poate fi invocata de un fisier SQL*Plus, un
subprogram PL/SQL sau un declansator.
Procedurile si functiile stocate pot fi apelate din orice bloc, de catre utilizatorul care are privilegiul
EXECUTE asupra acestora, n timp ce procedurile si functiile locale pot fi apelate numai din blocul
care le contine.
Cnd este creat un subprogram stocat, utiliznd comanda CREATE, subprogramul este depus n
dictionarul datelor. Este depus att textul sursa, ct si forma compilata (p-code). Atunci cnd subprogramul este
apelat, p-code este citit de pe disc, este depus n shared pool, unde poate fi accesat de mai multi utilizatori si este
executat daca este necesar. El va parasi shared pool conform algoritmului LRU (least recently used).
Pachetul DBMS_SHARED_POOL permite pastrarea de obiecte n shared pool. Daca un obiect este
gestionat n aceasta maniera, el va parasi shared pool doar daca aceasta se cere explicit. Pentru a pastra n
shared pool subprograme, pachete, declansatori, cursoare, clase Java, tipuri obiect, comenzi SQL, secvente este
utilizata procedura DBMS_SHARED_POOL.KEEP. Unica modalitate de a sterge un obiect pastrat n shared
pool, fara a reporni baza, este cu ajutorul procedurii DBMS_SHARED_POOL.UNKEEP.


42

Subprogramele se pot declara n blocuri PL/SQL, n alte subprograme sau n pachete, dar la sfrsitul
sectiunii declarative. La fel ca blocurile PL/SQL anonime, subprogramele contin o parte declarativa, o parte
executabila si, optional, o parte de tratare a erorilor. Partea declarativa contine declaratii de tipuri, cursoare,
constante, variabile, exceptii si subprograme imbricate. Partea executabila contine instructiuni care asigneaza
valori, controleaza executia programului si prelucreaza datele. Cea de-a treia parte se ocupa cu tratarea
exceptiilor aparute n timpul executiei subprogramului.
Crearea subprogramelor stocate
Principalele etape pentru crearea unui subprogram stocat sunt urmatoarele:
se editeaza subprogramul (CREATE PROCEDURE sau CREATE FUNCTION) si se salveaza ntr-un
script file SQL;
se ncarca si se executa acest script file, se compileaza codul sursa, se obtine p-code (subprogramul
este creat);
se utilizeaza comanda SHOW ERRORS pentru vizualizarea eventualelor erori la compilare (comanda
CREATE PROCEDURE sau CREATE FUNCTION depune codul sursa n dictionarul datelor chiar
daca subprogramul contine erori la compilare);
se executa subprogramul pentru a realiza actiunea dorita (de exemplu, procedura poate fi executata
fie utiliznd comanda EXECUTE din iSQL*Plus, fie invocnd-o dintr-un bloc PL/SQL).
Cnd este apelat subprogramul, motorul PL/SQL executa p-code.
Daca exista erori la compilare si se fac corectiile corespunzatoare, atunci este necesara fie comanda
DROP PROCEDURE (respectiv DROP FUNCTION), fie sintaxa OR REPLACE n cadrul comenzii CREATE.
Atunci cnd este apelata o procedura PL/SQL, server-ul Oracle parcurge anumite etape care vor fi
detaliate n cele ce urmeaza.
Verifica daca utilizatorul are privilegiul sa execute procedura (fie pentru ca el a creat procedura, fie
pentru ca i s-a acordat acest privilegiu).
Verifica daca procedura este prezenta n shared pool. Daca este prezenta va fi executata, altfel va fi
ncarcata de pe disc n database buffer cache.
Verifica daca starea procedurii este valida (VALID) sau invalida (INVALID). Starea unei proceduri
PL/SQL este invalida, fie pentru ca au fost detectate erori la compilarea procedurii, fie pentru ca
structura unui obiect s-a schimbat de cnd procedura a fost executata ultima oara. Daca starea este
invalida atunci procedura este recompilata automat. Daca nici o eroare nu a fost detectata, atunci va
fi executata noua versiune a procedurii.
Daca procedura apartine unui pachet atunci toate procedurile si functiile pachetului sunt de asemenea
ncarcate n database buffer cache (daca nu erau deja acolo). Daca pachetul este activat pentru prima
oara ntr-o sesiune, atunci server-ul va executa blocul de initializare al pachetului.
Proceduri PL/SQL
O procedura PL/SQL este un program independent care se afla compilat n schema bazei de date Oracle.
Cnd procedura este compilata, identificatorul acesteia (stabilit prin comanda CREATE PROCEDURE) devine
un nume de obiect n dictionarul datelor. Tipul obiectului este PROCEDURE.
Sintaxa generala pentru crearea unei proceduri este urmatoarea:
[CREATE [OR REPLACE] ] PROCEDURE nume_procedura
[ (parametru [, parametru ] ) ]
[AUTHI D {DEFINER | CURRENT_USER}]
{IS | AS}
[PRAGMA AUTONOMOUS_TRANSACTI ON;]
[declaratii locale]
BEGI N


43

partea executabila
[EXCEPTI ON
partea de tratare a exceptiilor]
END [nume_procedura];
Parametrii au urmatoarea forma sintactica:
nume_parametru [ {I N | OUT [NOCOPY] | I N OUT [NOCOPY] } ]
tip_de_date [ {:= | DEFAULT} expresie]
Comanda CREATE permite ca procedura sa fie stocata n baza de date. Cnd procedurile sunt create
folosind clauza CREATE OR REPLACE, ele vor fi stocate n baza de date n forma compilata, forma care
permite executia mai rapida a acestora. Daca procedura exista, atunci clauza OR REPLACE va avea ca efect
stergerea procedurii si nlocuirea acesteia cu noua versiune. Daca procedura exista, iar optiunea OR REPLACE
nu este prezenta, atunci comanda CREATE va returna eroarea ORA-00955: Name is already used by an
existing object.
Clauza AUTHID specifica faptul ca procedura stocata se executa cu drepturile proprietarului (implicit)
sau ale utilizatorului curent. De asemenea, aceasta clauza precizeaza daca referintele la obiecte sunt rezolvate n
schema proprietarului procedurii sau n cea a utilizatorului curent.
Clauza PRAGMA_AUTONOMOUS_TRANSACTION anunta compilatorul PL/SQL ca aceasta procedura
este autonoma (independenta). Tranzactiile autonome permit suspendarea tranzactiei principale, executarea unor
instructiuni SQL, permanentizarea sau anularea acestor operatii si continuarea tranzactiei principale.
Parametrii formali (variabile declarate n lista parametrilor specificatiei subprogramului) pot sa fie de tipul
%TYPE, %ROWTYPE sau de un tip explicit, fara specificarea dimensiunii.
Exemplu:
Sa se creeze o procedura stocata care micsoreaza cu o valoare data (cant) politele de asigurare emise de
firma SALVAL.
CREATE OR REPLACE PROCEDURE mic (cant IN NUMBER) AS
BEGIN
UPDATE politaasig
SET valoare = valoare - cant
WHERE firma = 'SALVAL';
END;
/
Daca n subprograme se executa operatii de reactualizare si exista declansatori relativ la aceste operatii
care nu trebuie sa se execute, atunci nainte de apelarea subprogramului declansatorii trebuie dezactivati, urmnd
ca ei sa fie reactivati dupa ce s-a terminat executia subprogramului.
De exemplu, n problema prezentata anterior ar trebui dezactivati declansatorii referitori la tabelul
politaasig, apelata procedura mic si n final, reactivati acesti declansatori.
ALTER TABLE politaasig DISABLE ALL TRIGGERS;
EXECUTE mic(10000)
ALTER TABLE politaasig ENABLE ALL TRIGGERS;
Exemplu:
Sa se creeze o procedura locala prin care se insereaza informatii n tabelul editata_de.
DECLARE
PROCEDURE editare
(v_cod_sursa editata_de.cod_sursa%TYPE,
v_cod_autor editata_de.cod_autor%TYPE) IS
BEGIN
INSERT INTO editata_de
VALUES (v_cod_sursa,v_cod_autor);
END;


44

BEGIN

editare(75643, 13579);

END;
/
Procedurile stocate pot fi apelate:
din corpul altei proceduri sau al unui declansator;
interactiv, de catre utilizator, folosind un instrument Oracle (de exemplu, iSQL*Plus);
explicit dintr-o aplicatie (de exemplu, Oracle Forms sau prin utilizarea de precompilatoare).
Apelarea unei proceduri se poate face n functie de mediul care o solicita:
1) n SQL*Plus, prin comanda
EXECUTE nume_procedura [ (lista_parametri_actuali) ];
2) n PL/SQL, prin invocarea numelui procedurii urmat de lista parametrilor actuali.
Parametrii actuali sunt variabile sau expresii referite n lista parametrilor subprogramului apelant. Ei
trebuie sa fie compatibili ca tip si numar cu parametrii formali.
Functii PL/SQL
O functie PL/SQL este similara unei proceduri cu exceptia ca ea trebuie sa ntoarca un rezultat. O functie
fara comanda RETURN va genera o eroare la compilare.
Cnd functia este compilata, identificatorul acesteia devine obiect n dictionarul datelor avnd tipul
FUNCTION. Algoritmul din interiorul corpului subprogramului functie trebuie sa asigure faptul ca toate
traiectoriile sale conduc la comanda RETURN. Daca o traiectorie a algoritmului trimite n partea de tratare a
erorilor, atunci handler-ul acesteia trebuie sa includa o comanda RETURN. Orice functie trebuie sa contina
clauza RETURN n antet si cel putin o comanda RETURN n partea executabila.
Sintaxa simplificata pentru scrierea unei functii este urmatoarea:
[CREATE [OR REPLACE] ] FUNCTI ON nume_functie
[ (parametru [, parametru ] ) ]
RETURN tip_de_date
[AUTHI D {DEFINER | CURRENT_USER} ]
[DETERMI NI STI C]
{IS | AS}
[PRAGMA AUTONOMOUS_TRANSACTI ON;]
[declaratii locale]
BEGI N
partea executabila
[EXCEPTI ON
partea de tratare a exceptiilor]
END [nume_functie];
Optiunea tip_de_date specifica tipul valorii returnate de functie, tip care nu poate contine specificatii de
dimensiune. Daca totusi sunt necesare aceste specificatii, se pot defini subtipuri, iar parametrii si valoarea
returnata vor fi declarati de acel subtip.
n interiorul functiei trebuie sa apara instructiunea RETURN expresie, unde expresie este valoarea
rezultatului furnizat de functie. Pot sa fie mai multe comenzi RETURN ntr-o functie, dar numai una din ele va fi
executata. Comanda RETURN (fara o expresie asociata) poate sa apara si ntr-o procedura. n acest caz, ea va
avea ca efect saltul la comanda ce urmeaza instructiunii apelante.


45

Optiunea DETERMINISTIC ajuta optimizorul Oracle n cazul unor apeluri repetate ale aceleasi functii,
cu aceleasi argumente. Ea indica posibilitatea folosirii unui rezultat obtinut anterior.
Observatii:
n blocul PL/SQL al unei functii stocate (cel care defineste actiunea efectuata de functie) nu pot fi
referite variabile host sau variabile bind.
O functie poate accepta unul sau mai multi parametri, dar trebuie sa returneze o singura valoare. Ca si
n cazul procedurilor, lista parametrilor este optionala. Daca subprogramul nu are parametri,
parantezele nu sunt necesare la declarare si la apelare.
O procedura care contine un parametru de tip OUT poate fi rescrisa sub forma unei functii.
Exemplu:
Sa se creeze o functie stocata care determina numarul operelor de arta realizate pe pnza, ce au fost
achizitionate la o anumita data.
CREATE OR REPLACE FUNCTION numar_opere
(v_a IN opera.data_achizitie%TYPE)
RETURN NUMBER AS
alfa NUMBER;
BEGIN
SELECT COUNT(ROWID)
INTO alfa
FROM opera
WHERE material = 'panza'
AND data_achizitie = v_a;
RETURN alfa;
END numar_opere;
/
Daca apare o eroare de compilare, utilizatorul o va corecta n fisierul editat si apoi va trimite
compilatorului (cu optiunea OR REPLACE) fisierul modificat.
Sintaxa pentru apelul unei functii este:
[ [schema.]nume_pachet.]nume_functie [@dblink] [(lista_parametri_actuali) ];
O functie stocata poate fi apelata n mai multe moduri. n continuare sunt prezentate trei exemple de
apelare.
1) Apelarea functiei si atribuirea valorii acesteia ntr-o variabila de legatura SQL*Plus:
VARIABLE val NUMBER
EXECUTE :val := numar_opere(SYSDATE)
PRINT val
Cnd este utilizata declaratia VARIABLE pentru variabilele host de tip NUMBER nu trebuie specificata
dimensiunea, iar pentru cele de tip CHAR sau VARCHAR2 valoarea implicita este 1 sau poate fi specificata o alta
valoare ntre paranteze. PRINT si VARIABLE sunt comenzi SQL*Plus.
2) Apelarea functiei ntr-o instructiune SQL:
SELECT numar_opere(SYSDATE)
FROM dual;
3) Aparitia numelui functiei ntr-o comanda din interiorul unui bloc PL/SQL (de exemplu, ntr-o
instructiune de atribuire):
SET SERVEROUTPUT ON
ACCEPT data PROMPT 'dati data achizitionare'
DECLARE


46

num NUMBER;
v_data opera.data_achizitie%TYPE := '&data';
BEGIN
num := numar_opere(v_data);
DBMS_OUTPUT.PUT_LINE('numarul operelor de arta
achizitionate la data ' || TO_CHAR(v_data) || ' este '
|| TO_CHAR(num));
END;
/
SET SERVEROUTPUT OFF
Exemplu:
Sa se creeze o procedura stocata care pentru un anumit tip de opera de arta (dat ca parametru) calculeaza
numarul operelor de tipul respectiv din muzeu, numarul de specialisti care au expertizat sau au restaurat aceste
opere, numarul de expozitii n care au fost expuse, precum si valoarea nominala totala a acestora.
CREATE OR REPLACE PROCEDURE date_tip_opera
(v_tip opera.tip%TYPE) AS
FUNCTION nr_opere (v_tip opera.tip%TYPE)
RETURN NUMBER IS
v_numar NUMBER(3);
BEGIN
SELECT COUNT(*)
INTO v_numar
FROM opera
WHERE tip = v_tip;
RETURN v_numar;
END nr_opere;
FUNCTION valoare_totala (v_tip opera.tip%TYPE)
RETURN NUMBER IS
v_numar opera.valoare%TYPE;
BEGIN
SELECT SUM(valoare)
INTO v_numar
FROM opera
WHERE tip = v_tip;
RETURN v_numar;
END valoare_totala;
FUNCTION nr_specialisti (v_tip opera.tip%TYPE)
RETURN NUMBER IS
v_numar NUMBER(3);
BEGIN
SELECT COUNT(DISTINCT studiaza.cod_specialist)
INTO v_numar
FROM studiaza, opera
WHERE studiaza.cod_opera = opera.cod_opera
AND opera.tip = v_tip;
RETURN v_numar;
END nr_specialisti;
FUNCTION nr_expozitii (v_tip opera.tip%TYPE)
RETURN NUMBER IS
v_numar NUMBER(3);
BEGIN
SELECT COUNT(DISTINCT figureaza_in.cod_expozitie)
INTO v_numar
FROM figureaza_in, opera
WHERE figureaza_in.cod_opera = opera.cod_opera


47

AND opera.tip = v_tip;
RETURN v_numar;
END nr_expozitii;
BEGIN
DBMS_OUTPUT.PUT_LINE('Numarul operelor de arta este '||
nr_opere(v_tip));
DBMS_OUTPUT.PUT_LINE('Valoarea operelor de arta este '||
valoare_totala(v_tip));
DBMS_OUTPUT.PUT_LINE('Numarul de specialisti este '||
nr_specialisti(v_tip));
DBMS_OUTPUT.PUT_LINE('Numarul de expozitii este '||
nr_expozitii(v_tip);
END date_tip_opera;
Instructiunea CALL
O instructiune specifica pentru Oracle9i este CALL, care permite apelarea subprogramelor PL/SQL
stocate (independente sau incluse n pachete) si a metodelor Java.
CALL este o comanda SQL care nu poate sa apara de sine statatoare ntr-un bloc PL/SQL. Ea poate fi
utilizata n PL/SQL doar dinamic, prin intermediul comenzii EXECUTE IMMEDIATE. Pentru executarea acestei
comenzi, utilizatorul trebuie sa aiba privilegiul EXECUTE asupra subprogramului. Instructiunea poate fi
executata interactiv din SQL*Plus.
Comanda CALL are sintaxa urmatoare:
CALL [schema.] [ {nume_tip_obiect | nume_pachet}. ] nume_subprogram
[ (lista_parametri actuali) ] [@dblink_nume] [I NTO :variabila_host]
Identificatorul nume_subprogram este numele unui subprogram sau al unei metode apelate. Clauza INTO
este folosita numai pentru variabilele de iesire ale unei functii. Daca lipseste clauza @dblink_nume, atunci apelul
se refera la baza de date locala, iar ntr-un sistem distribuit clauza specifica numele bazei de date ce contine
subprogramul.
Exemplu:
Sunt prezentate doua exemple prin care o functie PL/SQL este apelata din SQL*Plus, respectiv o
procedura externa C este apelata, folosind SQL dinamic, dintr-un bloc PL/SQL.
CREATE OR REPLACE FUNCTION apelfunctie(a IN VARCHAR2)
RETURN VARCHAR2 AS
BEGIN
DBMS_OUTPUT.PUT_LINE ('Apel functie cu ' || a);
RETURN a;
END apelfunctie;
/
SQL> --apel valid
SQL> VARIABLE v_iesire VARCHAR2(20)
SQL> CALL apelfunctie('Salut!') INTO :v_iesire
Apel functie cu Salut!
Call completed
SQL> PRINT v_iesire
v_iesire
Salut!
DECLARE
a NUMBER(7);
x VARCHAR2(10);
BEGIN
EXECUTE IMMEDIATE 'CALL alfa_extern_procedura (:aa, :xx)'
USING a, x;


48

END;
/
Modificarea si suprimarea subprogramelor PL/SQL
Pentru a lua n considerare modificarea unei proceduri sau functii, recompilarea acesteia se face prin
comanda:
ALTER {FUNCTI ON | PROCEDURE} [schema.]nume COMPI LE;
Ca si n cazul tabelelor, functiile si procedurile pot fi suprimate cu ajutorul comenzii DROP. Aceasta
presupune eliminarea subprogramelor din dictionarul datelor. DROP este o comanda ce apartine limbajului de
definire a datelor, astfel ca se executa un COMMIT implicit att nainte, ct si dupa comanda.
Atunci cnd este sters un subprogram prin comanda DROP, automat sunt revocate toate privilegiile
acordate referitor la acest subprogram. Daca este utilizata sintaxa CREATE OR REPLACE, privilegiile acordate
acestui subprogram ramn aceleasi.
Comanda DROP are urmatoarea sintaxa:
DROP {FUNCTI ON | PROCEDURE} [schema.]nume;
Transferarea valorilor prin parametri
Lista parametrilor unui subprogram este compusa din parametri de intrare (IN), de iesire (OUT) sau de
intrare/iesire (IN OUT), separati prin virgula.
Daca nu este specificat tipul parametrului, atunci implicit acesta este considerat de intrare (IN). Un
parametru formal cu optiunea IN poate primi valori implicite chiar n cadrul comenzii de declarare. Acest
parametru este read-only si deci, nu poate fi schimbat n corpul subprogramului. El actioneaza ca o constanta.
Parametrul actual corespunzator poate fi literal, expresie, constanta sau variabila initializata.
Un parametru formal cu optiunea OUT este neinitializat si prin urmare, are automat valoarea null. n
interiorul subprogramului, parametrilor cu optiunea OUT sau IN OUT trebuie sa li se asigneze o valoare
explicita. Daca nu se atribuie nici o valoare, atunci parametrul actual corespunzator va avea valoarea null.
Parametrul actual trebuie sa fie o variabila, nu poate fi o constanta sau o expresie.
Daca n timpul executiei procedurii apare o exceptie, atunci valorile parametrilor formali cu optiunile IN
OUT sau OUT nu sunt copiate n valorile parametrilor actuali.
Implicit, transmiterea parametrilor se face prin referinta n cazul parametrilor IN si prin valoare n cazul
parametrilor OUT sau IN OUT. Daca pentru realizarea unor performante se doreste transmiterea prin referinta si
a parametrilor IN OUT sau OUT, atunci se poate utiliza optiunea NOCOPY. Daca optiunea NOCOPY este
asociata unui parametru IN, atunci se va genera o eroare la compilare, deoarece acesti parametri se transmit de
fiecare data prin referinta.
Optiunea NOCOPY va fi ignorata, iar parametrul va fi transmis prin valoare daca:
parametrul actual este o componenta a unui tablou indexat (restrictia nu se aplica daca parametrul
este ntreg tabelul);
parametrul actual este constrns prin specificarea unei precizii, a unei marimi sau prin optiunea NOT
NULL;
parametrul formal si cel actual asociat sunt nregistrari care fie au fost declarate implicit ca variabile
contor ntr-un ciclu LOOP, fie au fost declarate explicit prin %ROWTYPE, dar constrngerile pe
cmpurile corespunzatoare difera;
transmiterea parametrului actual cere o conversie implicita a tipului;
subprogramul este o parte a unui apel de tip RPC (remote procedure call), iar n acest caz, parametrii
fiind transmisi n retea, transferul nu se poate face prin referinta.
Atunci cnd este apelata o procedura PL/SQL, sistemul Oracle furnizeaza doua metode pentru definirea
parametrilor actuali: specificarea explicita prin nume si specificarea prin pozitie.


49

Exemplu:
Sunt prezentate diferite moduri pentru apelarea procedurii p1.
CREATE PROCEDURE p1(a IN NUMBER, b IN VARCHAR2,
c IN DATE, d OUT NUMBER) AS ;
DECLARE
var_a NUMBER;
var_b VARCHAR2;
var_c DATE;
var_d NUMBER;
BEGIN
--specificare prin pozitie
p1(var_a,var_b,var_c,var_d);
--specificare prin nume
p1(b=>var_b,c=>var_c,d=>var_d,a=>var_a);
--specificare prin nume si pozitie
p1(var_a,var_b,d=>var_d,c=>var_c);
END;
Daca este utilizata specificatia prin pozitie, parametrii care au primit o valoare implicita trebuie sa fie
plasati la sfrsitul listei parametrilor actuali.
Exemplu:
Fie proces_data o procedura care proceseaza n mod normal data zilei curente, dar care optional poate
procesa si alte date. Daca nu se specifica parametrul actual corespunzator parametrului formal plan_data, atunci
acesta va lua automat valoarea data implicit (data curenta a sistemului).
PROCEDURE proces_data(data_in IN NUMBER,
plan_data IN DATE := SYSDATE) IS
Urmatoarele comenzi reprezinta apeluri corecte ale procedurii proces_data:
proces_data(10);
proces_data(10,SYSDATE+1);
proces_data(plan_data=>SYSDATE+1,data_in=>10);
O declaratie de subprogram (procedura sau functie) fara parametri este specificata fara paranteze. De
exemplu, daca procedura react_calc_dur si functia obt_date nu au parametri, atunci:
react_calc_dur; -- apel corect
react_calc_dur(); -- apel incorect
data_mea := obt_date; -- apel corect
Module overload
Doua sau mai multe module pot sa aiba aceleasi nume, dar sa difere prin lista parametrilor. Aceste
module sunt numite module overload (suprancarcate). Functia TO_CHAR este un exemplu de modul overload.
Exista o singura functie, TO_CHAR, pentru a converti date numerice si calendaristice n date de tip caracter.
n cazul unui apel, compilatorul compara parametrii actuali cu listele parametrilor formali pentru
modulele overload si executa modulul corespunzator. Toate programele overload trebuie sa fie definite n acelasi
bloc PL/SQL (bloc anonim, modul sau pachet).
Modulele overload pot sa apara n programele PL/SQL fie n sectiunea declarativa a unui bloc, fie n
interiorul unui pachet. Suprancarcarea subprogramelor nu se poate face pentru functii sau proceduri stocate, dar
este permisa pentru subprograme locale, subprograme care apar n pachete sau pentru metode.
Observatii:
Doua subprograme overload trebuie sa difere, cel putin prin tipul unuia dintre parametri. Doua
subprograme nu pot fi overload daca parametrii lor formali difera numai prin tipurile lor si daca
acestea sunt niste subtipuri care se bazeaza pe acelasi tip de date.


50

Nu este suficient ca lista parametrilor subprogramelor overload sa difere numai prin numele
parametrilor formali.
Nu este suficient ca lista parametrilor subprogramelor overload sa difere numai prin tipul acestora
(IN, OUT, IN OUT). PL/SQL nu poate face diferenta (la apelare) ntre tipurile IN si OUT.
Nu este suficient ca functiile overload sa difere doar prin tipul de date returnat (tipul de date
specificat n clauza RETURN a functiei).
Exemplu:
Urmatoarele subprograme nu pot fi overload.
1) FUNCTION alfa(par IN POSITIVE);
FUNCTION alfa(par IN BINARY_INTEGER);
2) FUNCTION alfa(par IN NUMBER);
FUNCTION alfa(parar IN NUMBER);
3) PROCEDURE beta(par IN VARCHAR2) IS;
PROCEDURE beta(par OUT VARCHAR2) IS;
Exemplu:
Sa se creeze doua functii (locale) cu acelasi nume care sa calculeze media valorilor operelor de arta de un
anumit tip. Prima functie va avea un argument reprezentnd tipul operelor de arta, iar cea de-a doua va avea
doua argumente, unul reprezentnd tipul operelor de arta, iar celalalt reprezentnd stilul operelor pentru care se
calculeaza valoarea medie (functia va calcula media valorilor operelor de arta de un anumit tip si care apartin
unui stil specificat).
DECLARE
medie1 NUMBER(10,2);
medie2 NUMBER(10,2);
FUNCTION valoare_medie (v_tip opera.tip%TYPE)
RETURN NUMBER IS
medie NUMBER(10,2);
BEGIN
SELECT AVG(valoare)
INTO medie
FROM opera
WHERE tip = v_tip;
RETURN medie;
END;
FUNCTION valoare_medie (v_tip opera.tip%TYPE,
v_stil opera.stil%TYPE)
RETURN NUMBER IS
medie NUMBER(10,2);
BEGIN
SELECT AVG(valoare)
INTO medie
FROM opera
WHERE tip = v_tip AND stil = v_stil;
RETURN medie;
END;
BEGIN
medie1 := valoare_medie('pictura');
DBMS_OUTPUT.PUT_LINE(Media valorilor picturilor din muzeu
este || medie1);
medie2 := valoare_medie(pictura , impresionism );
DBMS_OUTPUT.PUT_LINE(Media valorilor picturilor
impresioniste din muzeu este || medie2);
END;


51

Procedura versus functie
Dupa cum am mai subliniat, n general, procedura este utilizata pentru realizarea unor actiuni, iar functia
este folosita pentru calculul unei valori.
Pot fi marcate cteva deosebiri esentiale ntre functii si proceduri.
Procedura se executa ca o comanda PL/SQL, iar functia se invoca n cadrul unei expresii.
Procedura poate returna (sau nu) una sau mai multe valori, iar functia trebuie sa returneze o singura
valoare.
Procedura nu trebuie sa contina clauza RETURN expresie, iar functia trebuie sa contina aceasta
optiune.
De asemenea, pot fi remarcate cteva elemente esentiale, comune att functiilor ct si procedurilor.
Ambele pot:
accepta valori implicite;
contine sectiuni declarative, executabile si de tratare a erorilor;
utiliza specificarea prin nume sau pozitie a parametrilor;
accepta parametri NOCOPY.
Tratarea exceptiilor
Daca apare o eroare ntr-un subprogram, atunci este declansata o exceptie. Daca exceptia este tratata n
subprogram, atunci blocul se termina si controlul trece la sectiunea de tratare a erorilor din subprogramul
respectiv. Daca nu exista o tratare a exceptiei n subprogram, atunci controlul trece la programul apelant de nivel
imediat superior (n partea de tratare a erorilor), respectnd regulile de propagare a exceptiilor. n acest caz,
valorile parametrilor de tip OUT sau IN OUT nu sunt returnate parametrilor actuali. Acestia vor avea aceleasi
valori, ca si cum subprogramul nu ar fi fost apelat.
Recursivitate
Recursivitatea este o tehnica importanta pentru simplificarea modelarii algoritmilor. Un subprogram
recursiv presupune ca acesta se apeleaza pe el nsusi.
n Oracle, o problema delicata este legata de locul unde se plaseaza un apel recursiv. De exemplu, daca
apelul este n interiorul unei comenzi FOR specifice cursoarelor sau ntre comenzile OPEN si CLOSE, atunci la
fiecare apel este deschis alt cursor. n felul acesta, programul poate depasi limita admisa de cursoare deschise la
un moment dat (OPEN_CURSORS), setata n parametrul de initializare Oracle.
Exemplu:
Sa se calculeze recursiv al m-lea termen din sirul lui Fibonacci.
CREATE OR REPLACE FUNCTION fibo(m POSITIVE) RETURN INTEGER AS
BEGIN
IF (m = 1) OR (m = 2) THEN
RETURN 1;
ELSE
RETURN fibo(m-1) + fibo(m-2);
END IF;
END fibona;
Exemplu:
Sa se calculeze iterativ al m-lea termen din sirul lui Fibonacci.
CREATE OR REPLACE FUNCTION fibo(m POSITIVE) RETURN INTEGER AS
ter1 INTEGER := 1;
ter2 INTEGER := 0;
valoare INTEGER;
BEGIN
IF (m = 1) OR (m = 2) THEN


52

RETURN 1;
ELSE
valoare := ter1 + ter2;
FOR i IN 3..m LOOP
ter2 := ter1;
ter1 := valoare;
valoare := ter1 + ter2;
END LOOP;
RETURN valoare;
END IF;
END fibo;
Declaratii forward
Subprogramele se numesc reciproc recursive daca se apeleaza unul pe altul, n mod direct sau indirect.
Declaratiile forward permit definirea subprogramelor reciproc recursive.
n PL/SQL, un identificator trebuie declarat nainte de a fi folosit. De asemenea, un subprogram trebuie
declarat nainte de a fi apelat.
Exemplu:
PROCEDURE alfa ( ... ) IS
BEGIN
beta( ... ); -- apel incorect

END;
PROCEDURE beta ( ... ) IS
BEGIN

END;
n acest exemplu, procedura beta nu poate fi apelata deoarece nu este nca declarata. Problema se poate
rezolva simplu n acest caz, inversnd ordinea celor doua proceduri. Aceasta solutie nu este eficienta ntotdeauna
(de exemplu, daca si procedura beta contine un apel al procedurii alfa).
PL/SQL permite un tip special, numit forward, de declarare a unui subprogram. El consta dintr-o
specificare a antetului unui subprogram, terminata prin caracterul ;. O declaratie de tip forward pentru
procedura beta are forma:
PROCEDURE beta ( ... ); -- declaratie forward

PROCEDURE alfa ( ... ) IS
BEGIN
beta( ... );

END;
PROCEDURE beta ( ... ) IS
BEGIN

END;
Declaratiile forward pot fi folosite pentru a defini subprograme ntr-o anumita ordine logica, pentru a
defini subprograme reciproc recursive sau pentru a grupa subprograme ntr-un pachet.
Lista parametrilor formali din declaratia forward trebuie sa fie identica cu cea corespunzatoare corpului
subprogramului. Corpul subprogramului poate aparea oriunde dupa declaratia sa forward, dar sa ramna n
aceeasi unitate de program.
Utilizarea n expresii SQL a functiilor definite de utilizator
O functie stocata poate fi referita ntr-o comanda SQL la fel ca orice functie standard furnizata de sistem
(built-in function), dar cu anumite restrictii.


53

Functiile PL/SQL definite de utilizator pot fi apelate din orice expresie SQL n care se pot folosi functii
SQL standard.
Functiile PL/SQL pot sa apara n:
lista de cmpuri a comenzii SELECT;
conditia clauzelor WHERE si HAVING;
clauzele CONNECT BY, START WITH, ORDER BY si GROUP BY;
clauza VALUES a comenzii INSERT;
clauza SET a comenzii UPDATE.
Exemplu:
Sa se afiseze operele de arta (titlu, valoare, stare) a caror valoare este mai mare dect valoarea medie a
tuturor operelor de arta din muzeu.
CREATE OR REPLACE FUNCTION valoare_medie
RETURN NUMBER AS
v_val_mediu opera.valoare%TYPE;
BEGIN
SELECT AVG(valoare)
INTO v_val_mediu
FROM opera;
RETURN v_val_mediu;
END;
Referirea acestei functii ntr-o comanda SQL se poate face prin secventa:
SELECT titlu, valoare, stare
FROM opera
WHERE valoare >= valoare_medie;
Exista restrictii referitoare la folosirea functiilor definite de utilizator ntr-o comanda SQL. Cteva dintre
acestea, care s-au pastrat si pentru Oracle9i, vor fi enumerate n continuare:
functia definita de utilizator trebuie sa fie o functie stocata (procedurile stocate nu pot fi apelate n
expresii SQL), nu poate fi locala altui bloc;
functia definita de utilizator trebuie sa fie o functie linie si nu una grup (restrictia dispare n Oracle9i);
functia apelata dintr-o comanda SELECT sau din comenzi paralelizate INSERT, UPDATE si
DELETE nu poate modifica tabelele bazei de date;
functia apelata dintr-o comanda UPDATE sau DELETE nu poate interoga sau modifica tabele ale
bazei reactualizate chiar de aceste comenzi (table mutating);
functia apelata din comenzile SELECT, INSERT, UPDATE sau DELETE nu poate contine comenzi
LCD (COMMIT), ALTER SYSTEM, SET ROLE sau comenzi LDD (CREATE);
functia nu poate aparea n clauza CHECK a unei comenzi CREATE/ALTER TABLE;
functia nu poate fi folosita pentru a specifica o valoare implicita pentru o coloana n cadrul unei
comenzi CREATE/ALTER TABLE;
functia poate fi utilizata ntr-o comanda SQL numai de catre proprietarul functiei sau de utilizatorul
care are privilegiul EXECUTE asupra acesteia;
parametrii unei functii PL/SQL apelate dintr-o comanda SQL trebuie sa fie specificati prin pozitie
(specificarea prin nume nefiind permisa);
functia definita de utilizator, apelabila dintr-o comanda SQL, trebuie sa aiba doar parametri de tip IN,
cei de tip OUT si IN OUT nefiind acceptati;
parametrii formali ai unui subprogram functie trebuie sa fie de tip specific bazei de date (NUMBER,
CHAR, VARCHAR2, ROWID, LONG, LONGROW, DATE etc.) si nu de tipuri PL/SQL (BOOLEAN,
RECORD etc.);
tipul returnat de un subprogram functie trebuie sa fie un tip intern pentru server, nu un tip PL/SQL;
functia nu poate apela un subprogram care nu respecta restrictiile anterioare.
Exemplu:


54

CREATE OR REPLACE FUNCTION calcul (p_val NUMBER)
RETURN NUMBER IS
BEGIN
INSERT INTO opera(cod_opera, tip, data_achizitie, valoare)
VALUES (1358, 'gravura', SYSDATE, 700000);
RETURN (p_val*7);
END;
/
UPDATE opera
SET valoare = calcul (550000)
WHERE cod_opera = 7531;
Comanda UPDATE va returna o eroare deoarece tabelul opera este mutating. Operatia de reactualizare
este nsa permisa asupra oricarui alt tabel diferit de opera.
Informatii referitoare la subprograme
Informatiile referitoare la subprogramele PL/SQL si modul de acces la aceste informatii sunt urmatoarele:
codul sursa, utiliznd vizualizarea USER_SOURCE din dictionarul datelor;
informatii generale, utiliznd vizualizarea USER_OBJECTS din dictionarul datelor;
tipul parametrilor (IN, OUT, IN OUT), utiliznd comanda DESCRIBE din SQL*Plus;
p-code (nu este accesibil utilizatorilor);
erorile la compilare, utiliznd vizualizarea USER_ERRORS din dictionarul datelor sau comanda
SHOW ERRORS;
informatii de depanare, utiliznd pachetul DBMS_OUTPUT.
Atunci cnd este creat un subprogram stocat, informatiile referitoare la subprogram sunt depuse n
dictionarul datelor.
Vizualizarea USER_OBJECTS contine informatii generale despre toate obiectele prelucrate n baza de
date si, n particular, despre subprogramele stocate.
Vizualizarea USER_OBJECTS are urmatoarele cmpuri:
OBJECT_NAME numele obiectului;
OBJECT_TYPE tipul obiectului (PROCEDURE, FUNCTION etc.);
OBJECT_ID identificator intern al obiectului;
CREATED data la care a fost creat obiectul;
LAST_DDL_TIME data ultimei modificari a obiectului;
TIMESTAMP data si momentul ultimei recompilari;
STATUS starea de validitate a obiectului.
Pentru a verifica daca recompilarea explicita (ALTER) sau implicita a avut succes se poate verifica starea
subprogramelor utiliznd coloana STATUS din vizualizarea USER_OBJECTS.
Orice obiect are o stare (status) sesizata n dictionarul datelor, care poate fi:
VALID (obiectul a fost compilat si poate fi folosit atunci cnd este referit);
INVALID (obiectul trebuie compilat nainte de a fi folosit).
Exemplu:
Sa se listeze n ordine alfabetica, procedurile si functiile detinute de utilizatorul curent, precum si starea
acestora.
SELECT OBJECT_NAME, OBJECT_TYPE, STATUS
FROM USER_OBJECTS
WHERE OBJECT_TYPE IN ('PROCEDURE','FUNCTION')
ORDER BY OBJECT_NAME;
Dupa ce subprogramul a fost creat, codul sursa al acestuia poate fi obtinut consultnd vizualizarea


55

USER_SOURCE din dictionarul datelor. Vizualizarea are urmatoarele cmpuri: NAME (numele obiectului),
TYPE (tipul obiectului), LINE (numarul liniei din codul sursa), TEXT (textul liniilor codului sursa).
Exemplu:
Sa se afiseze codul complet pentru functia numar_opere.
SELECT TEXT
FROM USER_SOURCE
WHERE NAME = 'NUMAR_OPERE'
ORDER BY LINE;
Exemplu:
Sa se scrie o procedura care recompileaza toate obiectele invalide din schema personala.
CREATE OR REPLACE PROCEDURE recompileaza IS
CURSOR obj_curs IS
SELECT OBJECT_TYPE, OBJECT_NAME
FROM USER_OBJECTS
WHERE STATUS = 'INVALID'
AND OBJECT_TYPE IN
('PROCEDURE', 'FUNCTION', PACKAGE',
'PACKAGE BODY', 'VIEW');
BEGIN
FOR obj_rec IN obj_curs LOOP
DBMS_DDL.ALTER_COMPILE(obj_rec.OBJECT_TYPE,
USER, obj_rec.OBJECT_NAME);
END LOOP;
END recompileaza;
Atunci cnd se recompileaza un obiect PL/SQL, server-ul va recompila orice obiect invalid de care
depinde acesta.
Daca la recompilarea automata implicita a procedurilor locale dependente apar probleme, atunci starea
obiectului va ramne INVALID si server-ul Oracle va semnala o eroare. Prin urmare:
este preferabil ca recompilarea sa fie manuala, explicita utiliznd comanda ALTER (PROCEDURE,
FUNCTION, TRIGGER, PACKAGE) cu optiunea COMPILE;
este necesar ca dupa o schimbare referitoare la obiectele bazei, recompilarea sa se faca ct mai
repede.
Vizualizarea USER_ERRORS afiseaza textul tuturor erorilor de compilare. Cmpurile acesteia (NAME,
TYPE, SEQUENCE, LINE, POSITION, TEXT) sunt analizate n capitolul referitor la tratarea exceptiilor.
Pentru a obtine valori (de exemplu, valoarea contorului pentru un LOOP, valoarea unei variabile nainte
si dupa o atribuire etc.) si mesaje (de exemplu, parasirea unui subprogram, aparitia unei operatii etc.) dintr-un
bloc PL/SQL pot fi utilizate procedurile pachetului DBMS_OUTPUT. Aceste informatii se cumuleaza ntr-un
buffer care poate fi consultat ulterior.
Dependenta subprogramelor
Atunci cnd este compilat un subprogram, toate obiectele Oracle care sunt referite vor fi nregistrate n
dictionarul datelor. Subprogramul este dependent de aceste obiecte. Un subprogram care are erori la compilare
este marcat ca INVALID n dictionarul datelor. Un subprogram stocat poate deveni, de asemenea, invalid dupa
executia unei operatii LDD asupra unui obiect de care depinde.
Obiecte dependente Obiecte referite
View Table
Procedure View
Function Procedure
Package Specification Function
Package Body Synonym


56

Database Trigger Package Specification
Modificarea definitiei unui obiect referit poate sa influenteze (sau nu) functionarea normala a obiectului
dependent.
Exista doua tipuri de dependente:
dependenta directa, n care obiectul dependent (procedure sau function) face referinta direct la un
obiect de tip table, view, sequence, procedure, function;
dependenta indirecta, n care obiectul dependent (procedure sau function) face referinta indirect la un
obiect de tip table, view, sequence, procedure, function prin intermediul unui view, procedure sau
function.
n cazul dependentelor locale, atunci cnd un obiect referit este modificat, obiectele dependente sunt
invalidate. La urmatorul apel al obiectului invalidat, acesta va fi recompilat automat de catre server-ul Oracle.
n cazul dependentelor la distanta, procedurile stocate local si toate obiectele dependente vor fi
invalidate. Ele nu vor fi recompilate automat la urmatorul apel.
Exemplu:
Se presupune ca procedura filtru va referi direct tabelul opera si ca procedura adaug va reactualiza
tabelul opera prin intermediul unei vizualizari nou_opera. Pentru aflarea dependentelor directe se poate utiliza
vizualizarea USER_DEPENDENCIES din dictionarul datelor.
SELECT NAME, TYPE, REFENCED_NAME, REFENCED_TYPE
FROM USER_DEPENDENCIES
WHERE REFENCED_NAME IN ('opera', 'nou_opera');

NAME TYPE REFENCED_NAME REFENCED_TYPE
filtru Procedure opera Table
adaug Procedure nou_opera View
nou_opera View opera Table
Dependentele indirecte pot fi afisate utiliznd vizualizarile DEPTREE si IDEPTREE. Vizualizarea
DEPTREE afiseaza o reprezentare a tuturor obiectelor dependente (direct sau indirect). Vizualizarea
IDEPTREE afiseaza o reprezentare a aceleasi informatii, sub forma unui arbore.
Pentru a utiliza aceste vizualizari furnizate de sistemul Oracle trebuie:
executat scriptul UTLDTREE;
executata procedura DEPTREE_FILL (are trei argumente: tipul obiectului referit, schema obiectului
referit, numele obiectului referit).
Exemplu:
@UTLDTREE
EXECUTE DEPTREE_FILL ('TABLE', 'SCOTT', 'opera')
SELECT NESTED_LEVEL, TYPE, NAME
FROM DEPTREE
ORDER BY SEQ#;
NESTED_LEVEL TYPE NAME
0 Table opera
1 View nou_opera
2 Procedure adaug
1 Procedre filtru

SELECT *
FROM IDEPTREE;

DEPENDENCIES
TABLE nume_schema.opera
VIEW nume_schema.nou_opera
PROCEDURE nume_schema.adaug


57

PROCEDURE nume_schema.filtru
Dependentele la distanta sunt tratate printr-una dintre modalitatile alese de utilizator, modelul timestamp
(implicit) sau modelul signature.
Fiecare unitate PL/SQL are un timestamp (eticheta de timp) care este setat atunci cnd unitatea este
creata sau recompilata si care este depus n cmpul LAST_DDL_TIME din dictionarul datelor. Modelul
timestamp realizeaza compararea momentelor ultimei modificari a celor doua obiecte analizate. Daca obiectul
bazei are momentul ultimei modificari mai recent dect cel al obiectului dependent, atunci obiectul dependent va
fi recompilat.
Modelul signature (semnatura) determina momentul la care obiectele bazei distante trebuie recompilate.
Cnd este creat un subprogram, o signature este depusa n dictionarul datelor, alaturi de p-code. Aceasta
contine: numele constructiei PL/SQL (PROCEDURE, FUNCTION, PACKAGE), tipurile parametrilor, ordinea
parametrilor, numarul acestora si modul de transmitere (IN, OUT, IN OUT). Daca parametrii se schimba, atunci
evident signature se schimba.
Pentru a folosi modelul signature este necesara setarea parametrului
REMOTE_DEPENDENCIES_MODE la SIGNATURE. Aceasta se poate realiza prin:
1) comanda ALTER SESSION, care va afecta doar sesiunea curenta:
ALTER SESSION SET REMOTE_DEPENDENCIES_MODE = SIGNATURE;
2) comanda ALTER SYSTEM, care va afecta ntreaga baza de date (toate sesiunile), dar trebuie avut
privilegiul sistem pentru a utiliza aceasta instructiune:
ALTER SYSTEM SET REMOTE_DEPENDENCIES_MODE = SIGNATURE;
3) adaugarea n fisierul de initializare a unei linii de forma:
REMOTE_DEPENDENCIES_MODE = SIGNATURE;
Recompilarea procedurilor si a functiilor dependente este fara succes daca:
obiectul referit este suprimat (DROP) sau redenumit (RENAME);
tipul coloanei referite este schimbat;
o vizualizare referita este nlocuita printr-o vizualizare ce contine alte coloane;
lista parametrilor unei proceduri referite este modificata.
Recompilarea procedurilor si functiilor dependente este cu succes daca:
tabelul referit are noi coloane;
corpul PL/SQL al unei proceduri referite a fost modificat si recompilat cu succes.
Erorile datorate dependentelor pot fi minimizate:
utiliznd comenzi SELECT cu optiunea *;
incluznd lista coloanelor n comanda INSERT;
declarnd variabile cu atributul %TYPE;
declarnd nregistrari cu atributul %ROWTYPE;
Concluzii:
Daca procedura depinde de un obiect local, atunci se face recompilare automata la prima reexecutie.
Daca procedura depinde de o procedura distanta, atunci se face recompilare automata, dar la a doua
reexecutie. Este preferabila o recompilare manuala pentru prima reexecutie sau implementarea unei
strategii de reinvocare a ei (a doua oara).
Daca procedura depinde de un obiect distant, dar care nu este procedura, atunci nu se face
recompilare automata.
Rutine externe
PL/SQL a fost special conceput pentru Oracle si este specializat pentru procesarea tranzactiilor SQL.


58

Totusi, ntr-o aplicatie complexa pot sa apara cerinte si functionalitati care sunt mai eficient de implementat n C,
Java sau alt limbaj de programare. De exemplu, Java este un limbaj portabil cu un model de securitate bine
definit, care lucreaza excelent pentru aplicatii Internet.
Daca aplicatia trebuie sa efectueze anumite actiuni care nu pot fi implementate optim utiliznd PL/SQL,
atunci este preferabil sa fie utilizate alte limbaje care realizeaza performant actiunile respective. n acest caz este
necesara comunicarea ntre diferite module ale aplicatiei care sunt scrise n limbaje diferite.
Pna la versiunea Oracle8, modalitatea de comunicare ntre PL/SQL si alte limbaje (de exemplu, limbajul
C) a fost utilizarea pachetelor DBMS_PIPE si/sau DBMS_ALERT.
ncepnd cu Oracle8, comunicarea este simplificata prin utilizarea rutinelor externe. O rutina externa este
o procedura sau o functie scrisa ntr-un limbaj diferit de PL/SQL, dar apelabila dintr-un program scris n PL/SQL.
PL/SQL extinde functionalitatea server-ului Oracle, furniznd o interfata pentru apelarea rutinelor externe. Orice
bloc PL/SQL executat pe server sau pe client poate apela o rutina externa. Singurul limbaj acceptat pentru rutine
externe n Oracle8 este limbajul C.
Pentru a marca apelarea unei rutine externe n programul PL/SQL este definit un punct de intrare
(wrapper) care directioneaza spre codul extern (program PL/SQL ? wrapper ? cod extern). Pentru crearea
unui wrapper este utilizata o clauza speciala (AS EXTERNAL) n cadrul comenzii CREATE OR REPLACE
PROCEDURE. De fapt, clauza contine informatii referitoare la numele bibliotecii n care se gaseste
subprogramul extern (clauza LIBRARY), numele rutinei externe (clauza NAME) si corespondenta (C <>
PL/SQL) dintre tipurile de date (clauza PARAMETERS). n ultimele versiuni s-a renuntat la clauza AS
EXTERNAL.
Rutinele externe (scrise n C) sunt compilate, apoi depuse ntr-o biblioteca dinamica (DLL dynamic link
library) si sunt ncarcate doar atunci cnd este necesar. Daca se invoca o rutina externa scrisa n C, trebuie setata
conexiunea spre aceasta rutina. Un proces numit extproc este declansat automat de catre server. La rndul sau,
procesul extproc va ncarca biblioteca identificata prin clauza LIBRARY si va apela rutina respectiva.
Oracle8i permite utilizarea de rutine externe scrise n Java. De asemenea, un wrapper poate include
specificatii de apelare, prin utilizarea clauzei LANGUAGE. De fapt, aceste specificatii permit apelarea rutinelor
externe scrise n orice limbaj. De exemplu, o procedura scrisa ntr-un limbaj diferit de C sau Java poate fi
utilizata n SQL sau PL/SQL daca procedura respectiva este apelabila din C. n felul acesta, biblioteci standard
scrise n alte limbaje de programare pot fi apelate din programe PL/SQL.
Procedura PL/SQL executata pe server-ul Oracle poate apela o rutina externa scrisa n limbajul C,
depusa ntr-o biblioteca partajata. Procedura C se executa ntr-un spatiu adresa diferit de cel al server-ului
Oracle, n timp ce unitatile PL/SQL si metodele Java se executa n spatiul adresa al server-ului. JVM (Java
Virtual Machine) de pe server va executa metoda Java n mod direct, fara a fi necesar procesul extproc.
Maniera de ncarcare depinde de limbajul n care este scrisa rutina.
Pentru a apela rutine externe C, server-ul trebuie sa cunoasca pozitionarea bibliotecii dinamice DLL.
Acest lucru este furnizat de alias-ul bibliotecii din clauza AS LANGUAGE.
Pentru apelarea unei rutine externe Java se va ncarca clasa Java n baza de date. Este necesara doar
crearea unui wrapper care directioneaza catre codul extern. Spre deosebire de rutinele externe C, nu
este necesara nici biblioteca si nici setarea conexiunii spre rutina externa.
Clauza LANGUAGE din cadrul comenzii de creare a unui subprogram, specifica limbajul n care este
scrisa rutina (procedura externa C sau metoda Java) si are urmatoarea forma:
{IS | AS} LANGUAGE {C | J AVA}
Pentru o procedura C sunt date informatii referitoare la numele acesteia (clauza NAME); alias-ul
bibliotecii n care se gaseste (clauza LIBRARY); optiuni referitoare la tipul, pozitia, lungimea, modul de
transmitere (prin valoare sau prin referinta) al parametrilor (clauza PARAMETERS); posibilitatea ca rutina
externa sa acceseze informatii despre parametri, exceptii, alocarea memoriei utilizator (clauza WITH
CONTEXT).


59

LI BRARY nume_biblioteca [NAME nume_proc_c] [WI TH CONTEXT]
[PARAMETERS (parametru_extern [, parametru_extern ] ) ]
Pentru o metoda Java, n clauza trebuie specificata doar signatura metodei (lista tipurilor parametrilor n
ordinea aparitiei).
Exemplu:
CREATE OR REPLACE FUNCTION calc (x IN REAL) RETURN NUMBER
AS LANGUAGE C
LIBRARY biblioteca
NAME "c_calc"
PARAMETERS (x BY REFERENCES);
O rutina externa nu este apelata direct, ci se apeleaza subprogramul PL/SQL care refera rutina externa.
Apelarea poate sa apara n: blocuri anonime, subprograme independente sau care apartin unui pachet, metode ale
unui tip obiect, declansatori baza de date, comenzi SQL care apeleaza functii (n acest caz, trebuie utilizata
clauza PRAGMA RESTRICT_REFERENCES).
De remarcat ca o metoda Java poate fi apelata din orice bloc PL/SQL, subprogram sau pachet. JDBC
(Java Database Connectivity), care reprezinta interfata Java standard pentru conectare la baze de date
relationale, si SQLJ permit apelarea de blocuri PL/SQL din programe Java. SQLJ face posibila incorporarea
operatiilor SQL n codul Java. Standardul SQLJ acopera doar operatii SQL statice. Oracle9i SQLJ include
extensii pentru a suporta direct SQL dinamic.
O alta modalitate de a ncarca metode Java este folosirea interactiva n SQL*Plus a comenzii CREATE
JAVA instructiune.
Functii tabel
O functie tabel (table function) returneaza drept rezultat un set de linii (de obicei, sub forma unei
colectii). Aceasta functie poate fi interogata direct printr-o comanda SQL, ca si cum ar fi un tabel al bazei de
date. n felul acesta, functia poate fi utilizata n clauza FROM a unei cereri.
O functie tabel conducta (pipelined table function) este similara unei functii tabel, dar returneaza datele
iterativ, pe masura ce acestea sunt obtinute, nu toate deodata. Aceste functii sunt mai eficiente deoarece
informatia este returnata imediat cum este obtinuta.
Conceptul de functie tabel conducta a fost introdus n versiunea Oracle9i. Utilizatorul poate sa
defineasca astfel de functii. De asemenea, este posibila executia paralela a functiilor tabel (evident si a celor
clasice). n acest caz, functia trebuie sa contina n declaratie optiunea PARALLEL_ENABLE.
Functia tabel conducta accepta orice argument pe care l poate accepta o functie obisnuita si trebuie sa
returneze o colectie (nested table sau varray). Ea este declarata specificnd cuvntul cheie PIPELINED n
comanda CREATE OR REPLACE FUNCTION. Functia tabel conducta trebuie sa se termine printr-o comanda
RETURN simpla, care nu ntoarce nici o valoare.
Pentru a returna un element individual al colectiei este folosita comanda PIPE ROW, care poate sa apara
numai n corpul unei functii tabel conducta, n caz contrar generndu-se o eroare. Comanda poate fi omisa daca
functia tabel conducta nu returneaza nici o linie.
Dupa ce functia a fost creata, ea poate fi apelata dintr-o cerere SQL utiliznd operatorul TABLE. Cererile
referitoare la astfel de functii pot sa includa cursoare si referinte la cursoare, respectndu-se semantica de la
cursoarele clasice.
Functia tabel conducta nu poate sa apara n comenzile INSERT, UPDATE, DELETE. Totusi, pentru a
realiza o reactualizare, poate fi creata o vizualizare relativa la functia tabel si folosit un declansator INSTEAD
OF.
Exemplu:


60

Sa se obtina o instanta a unui tabel ce contine informatii referitoare la denumirea zilelor saptamnii.
Problema este rezolvata n doua variante. Prima reprezinta o solutie clasica, iar a doua varianta
implementeaza problema cu ajutorul unei functii tabel conducta.
Varianta 1:
CREATE TYPE t_linie AS OBJECT (
idl NUMBER, sir VARCHAR2(20));
CREATE TYPE t_tabel AS TABLE OF t_linie;
CREATE OR REPLACE FUNCTION calc1 RETURN t_tabel
AS
v_tabel t_tabel;
BEGIN
v_tabel := t_tabel (t_linie (1, 'luni'));
FOR j IN 2..7 LOOP
v_tabel.EXTEND;
IF j = 2
THEN v_tabel(j) := t_linie (2, 'marti');
ELSIF j = 3
THEN v_tabel(j) := t_linie (3, 'miercuri');
ELSIF j = 4
THEN v_tabel(j) := t_linie (4, 'joi');
ELSIF j = 5
THEN v_tabel(j) := t_linie (5, 'vineri');
ELSIF j = 6
THEN v_tabel(j) := t_linie (6, 'sambata');
ELSIF j = 7
THEN v_tabel(j) := t_linie (7, 'duminica');
END IF;
END LOOP;
RETURN v_tabel;
END calc1;
Functia calc1 poate fi invocata n clauza FROM a unei comenzi SELECT:
SELECT *
FROM TABLE (CAST (calc1 AS t_tabel));
Varianta 2:
CREATE OR REPLACE FUNCTION calc2 RETURN t_tabel PIPELINED
AS
v_linie t_linie;
BEGIN
FOR j IN 1..7 LOOP
v_linie :=
CASE j
WHEN 1 THEN t_linie (1, 'luni')
WHEN 2 THEN t_linie (2, 'marti')
WHEN 3 THEN t_linie (3, 'miercuri')
WHEN 4 THEN t_linie (4, 'joi')
WHEN 5 THEN t_linie (5, 'vineri')
WHEN 6 THEN t_linie (6, 'sambata')
WHEN 7 THEN t_linie (7, 'duminica')
END;
PIPE ROW (v_linie);
END LOOP;
RETURN;


61

END calc2;
Se observa ca tabelul este implicat doar n tipul rezultatului. Pentru apelarea functiei calc2 este folosita
sintaxa urmatoare:
SELECT *
FROM TABLE (calc2);
Functiile tabel sunt folosite frecvent pentru conversii de tipuri de date. Oracle9i introduce posibilitatea de
a crea o functie tabel care returneaza un tip PL/SQL (definit ntr-un bloc). Functia tabel care furnizeaza (la nivel
de pachet) drept rezultat un tip de date trebuie sa fie de tip conducta. Pentru apelare este utilizata sintaxa
simplificata (fara CAST).
Exemplu:
CREATE OR REPLACE PACKAGE exemplu IS
TYPE t_linie IS RECORD (idl NUMBER, sir VARCHAR2(20));
TYPE t_tabel IS TABLE OF t_linie;
END exemplu;
CREATE OR REPLACE FUNCTION calc3 RETURN exemplu.t_tabel
PIPELINED AS
v_linie exemplu.t_linie;
BEGIN
FOR j IN 1..7 LOOP
CASE j
WHEN 1 THEN v_linie.idl := 1; v_linie.sir := 'luni';
WHEN 2 THEN v_linie.idl := 2; v_linie.sir := 'marti';
WHEN 3 THEN v_linie.idl := 3; v_linie.sir := 'miercuri';
WHEN 4 THEN v_linie.idl := 4; v_linie.sir := 'joi';
WHEN 5 THEN v_linie.idl := 5; v_linie.sir := 'vineri';
WHEN 6 THEN v_linie.idl := 6; v_linie.sir := 'sambata';
WHEN 7 THEN v_linie.idl := 7; v_linie.sir := 'duminica';
END CASE;
PIPE ROW (v_linie);
END LOOP;
RETURN;
END calc3;
Procesarea tranzactiilor autonome
Tranzactia este o unitate logica de lucru, adica o secventa de comenzi care trebuie sa se execute ca un
ntreg pentru a mentine consistenta bazei de date. n mod uzual, o tranzactie poate sa cuprinda mai multe blocuri,
iar ntr-un bloc pot sa fie mai multe tranzactii.
O tranzactie autonoma este o tranzactie independenta lansata de alta tranzactie, numita tranzactie
principala. Tranzactia autonoma permite suspendarea tranzactiei principale, executarea de comenzi SQL,
permanentizarea (commit) si anularea (rollback) acestor operatii.
Odata nceputa, tranzactia autonoma este independenta, n sensul ca nu partajeaza blocari, resurse sau
dependente cu tranzactia principala. n felul acesta, o aplicatie nu trebuie sa cunoasca operatiile autonome ale
unei proceduri, iar procedura nu trebuie sa cunoasca nimic despre tranzactiile aplicatiei. Tranzactia autonoma are
totusi toate functionalitatile unei tranzactii obisnuite (permite cereri paralele, procesari distribuite etc.).
Pentru definirea unei tranzactii autonome se utilizeaza clauza PRAGMA
AUTONOMOUS_TRANSACTION care informeaza compilatorul PL/SQL ca trebuie sa marcheze o rutina ca fiind
autonoma. Prin rutina se ntelege: bloc anonim de cel mai nalt nivel (nu imbricat); procedura sau functie locala,
independenta sau mpachetata; metoda a unui tip obiect; declansator baza de date.
Codul PRAGMA AUTONOMOUS_TRANSACTION se specifica n partea declarativa a rutinei.
Codul PRAGMA AUTONOMOUS_TRANSACTION marcheaza numai rutine individuale ca fiind
independente. Nu pot fi marcate toate subprogramele unui pachet sau toate metodele unui tip obiect ca


62

autonome. Prin urmare, clauza nu poate sa apara n partea de specificatie a unui pachet.
Observatii:
Declansatorii autonomi, spre deosebire de cei clasici pot contine comenzi LCD (de exemplu,
COMMIT, ROLLBACK).
Exceptiile declansate n cadrul tranzactiilor autonome genereaza un rollback la nivel de tranzactie si
nu la nivel de instructiune.
Cnd se intra n sectiunea executabila a unei tranzactii autonome, tranzactia principala este
suspendata.
Cu toate ca o tranzactie autonoma este initiata de alta tranzactie, ea nu este o tranzactie imbricata
deoarece:
nu partajeaza resurse cu tranzactia principala;
nu depinde de tranzactia principala (de exemplu, daca tranzactia principala este anulata, atunci
tranzactiile imbricate sunt de asemenea anulate, n timp ce tranzactia autonoma nu este anulata);
schimbarile permanentizate din tranzactii autonome sunt vizibile imediat altor tranzactii, pe cnd cele
din tranzactii imbricate sunt vizibile doar dupa ce tranzactia principala este permanentizata.



6. Modularizarea aplicatiilor prin utilizarea
pachetelor


Pachetul (package) permite ncapsularea ntr-o unitate logica n baza de date a procedurilor, functiilor,
cursoarelor, tipurilor, constantelor, variabilelor si exceptiilor. Pachetele sunt unitati de program care sunt
compilate, depanate si testate. Ele reprezinta obiecte ale bazei de date care grupeaza tipuri, obiecte si
subprograme PL/SQL cu o legatura logica ntre ele.
Pachetele stocate permit gruparea procedurilor si functiilor nrudite, precum si partajarea variabilelor de
catre acestea. mpachetarea subprogramelor prezinta beneficii importante. Cnd este referit un pachet (cnd este
apelata pentru prima data o constructie a pachetului), ntregul pachet este ncarcat n zona globala sistem (SGA)
si este pregatit pentru executie. Plasarea pachetului n SGA prezinta avantajul vitezei de executie, deoarece
server-ul nu mai trebuie sa aduca informatia despre pachet de pe disc, aceasta fiind deja n memorie. Prin urmare,
apeluri ulterioare ale unor constructii din acelasi pachet nu solicita operatii I/O de pe disc.
Daca apar proceduri si functii nrudite care trebuie sa fie executate mpreuna, este convenabil ca acestea
sa fie grupate ntr-un pachet stocat. Este de remarcat ca n memorie exista o singura copie a unui pachet, pentru
toti utilizatorii.
Spre deosebire de subprograme, pachetele nu pot fi apelate, nu pot transmite parametri si nu pot fi
ncuibarite.
n mod uzual un pachet are doua parti, fiecare fiind stocata separat n dictionarul datelor.
Specificatia pachetului (package specification) reprezinta partea vizibila, adica interfata cu aplicatii
sau cu alte unitati program. n aceasta parte se descrie tot ce este necesar utilizatorului pentru a folosi
pachetul. Se declara tipuri, constante, variabile, exceptii, cursoare si subprograme.
Corpul pachetului (package body) reprezinta partea acunsa, mascata de restul aplicatiei, adica
realizarea specificatiei. Corpul defineste cursoare si subprograme, implementnd specificatia.
Obiectele continute n corpul pachetului sunt fie private, fie publice.
Prin urmare, specificatia defineste interfata utilizatorului cu pachetul, iar corpul pachetului contine codul
care implementeaza operatiile definite n specificatie. Crearea unui pachet se face n doua etape care corespund


63

crearii partilor acestuia.
Un pachet poate cuprinde fie doar partea de specificatie, fie att specificatia, ct si corpul pachetului. n
cazul n care contine doar specificatia, pachetul va cuprinde numai definitii de tipuri si declaratii de date. Corpul
pachetului poate fi schimbat fara a fi necesara schimbarea specificatiei pachetului. Daca specificatia este
schimbata, aceasta invalideaza automat corpul pachetului, deoarece corpul depinde de specificatie. Specificatia si
corpul pachetului sunt unitati compilate separat. Corpul unui pachet poate fi compilat doar dupa ce specificatia
acestuia a fost compilata cu succes.
Un pachet are urmatoarea forma generala:
CREATE PACKAGE nume_pachet {IS | AS} -- specificatia
/* interfata utilizator, care contine: declaratii de tipuri si obiecte
publice, specificatii de subprograme */
END [nume_pachet];
CREATE PACKAGE BODY nume_pachet {IS | AS} -- corpul
/* implementarea, care contine: declaratii de obiecte si tipuri
private, corpuri de subprograme specificate n partea de interfata */
[BEGI N]
/* instructiuni de initializare, executate o singura data, atunci cnd
pachetul este invocat prima oara ntr-o sesiune a unui utilizator */
END [nume_pachet];
Specificatia unui pachet
Specificatia unui pachet cuprinde declararea procedurilor, functiilor, constantelor, variabilelor si
exceptiilor care pot fi accesibile utilizatorilor prin intermediul pachetului, adica declararea obiectelor de tip
PUBLIC din pachet. Acestea pot fi utilizate n proceduri sau comenzi care nu apartin pachetului, dar care au
privilegiul EXECUTE asupra acestuia.
Variabilele declarate n specificatia unui pachet sunt globale pachetului si sesiunii. Ele sunt initializate
implicit cu valoarea null, daca nu este specificata explicit o alta valoare.
Crearea specificatiei se face prin comanda:
CREATE [OR REPLACE] PACKAGE [schema.]nume_pachet
[AUTHI D {CURRENT_USER | DEFINER} ]
{IS | AS}
specificatie_pachet;
Optiunea specificatie_pachet poate include declaratii de tipuri, variabile, cursoare, exceptii, functii,
proceduri, clauze PRAGMA etc. n sectiunea declarativa, un obiect trebuie sa fie declarat nainte de a fi referit.
Optiunea OR REPLACE se specifica daca exista deja corpul pachetului.
Clauza AUTHID specifica faptul ca subprogramele pachetului se executa cu drepturile proprietarului
(implicit) sau ale utilizatorului curent. De asemenea, aceasta clauza precizeaza daca referintele la obiecte sunt
rezolvate n schema proprietarului pachetului sau n cea a utilizatorului curent.
Corpul unui pachet
Corpul unui pachet contine codul PL/SQL pentru obiectele declarate n specificatia acestuia si obiectele
private ale pachetului. De asemenea, corpul poate include o sectiune declarativa n care sunt specificate definitii
locale de tipuri, variabile, constante, proceduri si functii. Obiectele private sunt vizibile numai n interiorul
corpului pachetului si pot fi accesate numai de catre functiile si procedurile din pachetul respectiv.
Corpul pachetului este optional si nu este necesar sa fie creat daca specificatia pachetului nu contine
declaratii de proceduri sau functii.


64

Este importanta ordinea n care subprogramele sunt definite n interiorul corpului pachetului. O variabila
trebuie declarata nainte de a fi referita de alta variabila sau subprogram, iar un subprogram privat trebuie
declarat sau definit nainte de a fi apelat de alte subprograme. Declaratiile publice (interfata operationala) sunt
vizibile aplicatiei, spre deosebire de declaratiile private (implementarea efectiva a corpului) care sunt mascate
aplicatiei.
Comanda care permite crearea corpului unui pachet are sintaxa:
CREATE [OR REPLACE] PACKAGE BODY [schema.]nume_pachet
{I S | AS}
corp_pachet;
Un pachet este instantiat atunci cnd este apelat prima data. Aceasta presupune ca pachetul este citit de
pe disc, adus n memorie si este executat codul compilat al subprogramului apelat. De asemenea, n acest
moment se aloca memorie tuturor variabilelor definite n pachet.
n multe cazuri este necesar sa se faca o initializare atunci cnd pachetul este instantiat prima data ntr-o
sesiune. Aceasta se realizeaza prin adaugarea unei sectiuni de initializare (optionala) n corpul pachetului,
sectiune ncadrata ntre cuvintele cheie BEGIN si END. Sectiunea contine un cod de initializare care este
executat atunci cnd pachetul este invocat pentru prima data.
Crearea pachetului face ca acesta sa fie disponibil utilizatorului care l-a creat si oricarui alt utilizator ce
detine privilegiul EXECUTE.
Referinta la un tip sau obiect specificat n pachet se face prefixnd numele acestuia cu numele pachetului.
n corpul pachetului, obiectele din specificatie pot fi referite fara a le prefixa cu numele pachetului.
Procesul de creare a specificatei si corpului unui pachet urmeaza un algoritm similar celui ntlnit n
crearea subprogramelor PL/SQL independente. n ambele cazuri sunt verificate erorile sintactice si semantice, iar
modulul este depus n dictionarul datelor. Dupa ce a fost depus corpul pachetului, sunt verificate instructiunile
SQL individuale, adica se cerceteaza daca obiectele referite exista si daca utilizatorul le poate accesa. Sunt
comparate declaratiile de subprograme din specificatia pachetului cu cele din corpul pachetului (daca au acelasi
numar si tip de parametri). Orice eroare detectata la compilarea specificatiei sau a corpului pachetului este
marcata n dictionarul datelor.
Dupa ce specificatia si corpul pachetului sunt compilate, ele devin obiecte n schema curenta. n
vizualizarea USER_OBJECTS din dictionarul datelor, vor fi create doua noi linii:
OBJECT_TYPE OBJECT NAME
PACKAGE nume_pachet
PACKAGE BODY nume_pachet
Modificarea si suprimarea pachetelor
Modificarea unui pachet presupune recompilarea sa (pentru a putea modifica metoda de acces si planul
de executie) si se realizeaza prin comanda:
ALTER PACKAGE [schema.]nume_pachet
COMPI LE [ {PACKAGE | BODY} ];
Schimbarea corpului pachetului nu cere recompilarea constructiilor dependente, n timp ce schimbarile n
specificatia acestuia solicita recompilarea fiecarui subprogram stocat care face referinta la pachetul respectiv. Din
acest motiv, este bine ca specificatia sa contina ct mai putine constructii.
Daca se doreste modificarea sursei, utilizatorul poate recrea pachetul (folosind optiunea OR REPLACE)
nlocuindu-l pe cel existent.
Suprimarea unui pachet se realizeaza prin comanda:
DROP PACKAGE [schema.]nume_pachet [ {PACKAGE | BODY} ];


65

Daca n cadrul comenzii apare optiunea BODY, este distrus doar corpul pachetului, n caz contrar sunt
suprimate att specificatia, ct si corpul acestuia. Daca pachetul este suprimat, toate obiectele dependente de
acesta devin invalide. Daca este distrus numai corpul, toate obiectele dependente de acesta ramn valide. n
schimb, nu pot fi apelate subprogramele declarate n specificatia pachetului sau cele dependente de acestea, pna
cnd nu este recreat corpul pachetului.
Pentru ca un utilizator sa poata suprima un pachet trebuie fie ca pachetul sa apartina schemei
utilizatorului, fie ca utilizatorul sa aiba privilegiul sistem DROP ANY PROCEDURE.
Apelarea unui pachet se realizeaza n functie de mediul (SQL sau PL/SQL) care solicita un obiect din
pachetul respectiv.
1) n PL/SQL se face prin referirea:
nume_pachet.nume_componenta [ (lista_de_argumente) ];
2) n SQL se face prin comanda:
EXECUTE nume_pachet.nume_componenta [ (lista_de_argumente) ].
Exemplu:
Sa se creeze un pachet ce include o procedura prin care se verifica daca o combinatie specificata dintre
atributele cod_artist si stil este o combinatie care exista n tabelul opera.
CREATE PACKAGE verif_pachet IS
PROCEDURE verifica
(p_idartist IN opera.cod_artist%TYPE,
p_stil IN opera.stil%TYPE);
END verif_pachet;
/
CREATE OR REPLACE PACKAGE BODY verif_pachet
IS
i NUMBER := 0;
CURSOR opera_cu IS
SELECT cod_artist, stil
FROM opera;
TYPE opera_table_tip IS TABLE OF opera_cu%ROWTYPE
INDEX BY BINARY INTEGER;
art_stil opera_table_tip;
PROCEDURE verifica
(p_idartist IN opera.cod_artist%TYPE,
p_stil IN opera.stil%TYPE)
IS
BEGIN
FOR k IN art_stil.FIRST..art_stil.LAST LOOP
IF p_idartist = art_stil(k).cod_artist
AND p_stil = art_stil(k).stil THEN
RETURN;
END IF;
END LOOP;
RAISE_APPLICATION_ERROR(-20777,'combinatie eronata');
END verifica;
BEGIN
FOR ope_in IN opera_cu LOOP
art_stil(i) := ope_in;
i := i+1;
END LOOP;
END verif_pachet;
/
Apelarea n PL/SQL a unui obiect (verifica) din pachet se face prin:
verif_pachet.verifica (7935, 'impresionism');


66

Utilizarea n SQL a unui obiect (verifica) din pachet se face prin:
EXECUTE verif_pachet.verifica (7935, 'impresionism');
Una dintre posibilitatile interesante oferite de pachetele PL/SQL este aceea de a crea proceduri/functii
overload. Acest tip de programare este folositor atunci cnd este necesar un singur subprogram care sa execute
aceeasi operatie pe obiecte de tipuri diferite (diferite tipuri de parametri de intrare). Atunci cnd este apelat un
subprogram overload sistemul decide, pe baza tipului si numarului de parametri, instanta care trebuie sa fie
executata. Numai subprogramele locale sau care apartin unui pachet pot fi overload.
Observatii:
Un declansator nu poate apela o procedura sau o functie ce contine una dintre comenzile COMMIT,
ROLLBACK, SAVEPOINT. Prin urmare, pentru flexibilitatea apelului (de catre declansatori)
subprogramelor continute n pachete, trebuie verificat ca nici una dintre procedurile sau functiile
pachetului nu contin aceste comenzi.
Procedurile si functiile continute ntr-un pachet pot fi referite din fisiere SQL*Plus, din subprograme
stocate PL/SQL, din aplicatii client (de exemplu, Oracle Forms sau Power Builder), din declansatori
(baza de date), din programe aplicatie scrise n limbaje LG3.
ntr-un pachet nu pot fi referite variabile gazda.
ntr-un pachet, mai exact n corpul acestuia, sunt permise declaratii forward. Posibilitatile oferite de
aceste declaratii au fost comentate n capitolul destinat subprogramelor.
Daca un subprogram ce apartine unui pachet este apelat de un subprogram stand-alone trebuie remarcat
ca:
daca se schimba doar corpul pachetului, subprogramul care refera o constructie a pachetului ramne
valid;
daca specificatia pachetului se schimba, atunci att subprogramul care refera o constructie a
pachetului, precum si corpul pachetului sunt invalidate.
Daca un subprogram stocat referit de un pachet se schimba, atunci ntregul corp al pachetului este
invalidat, dar specificatia pachetului ramne valida.
Pachete predefinite
PL/SQL contine pachete predefinite (care sunt deja compilate n baza de date) utilizate pentru
dezvoltarea de aplicatii. Aceste pachete adauga noi functionalitati limbajului, precum protocoale de comunicatie,
acces la fisierele sistemului etc. Apelarea unor proceduri din aceste pachete se face prin prefixarea numelui
procedurii cu numele pachetului.
Dintre cele mai importante pachete predefinite se remarca:
DBMS_OUTPUT (permite afisarea de informatii);
DBMS_DDL (furnizeaza accesul la anumite comenzi LDD care pot fi folosite n programe PL/SQL);
UTL_FILE (permite citirea din fisierele sistemului de operare, respectiv scrierea n astfel de fisiere);
UTL_HTTP (foloseste protocolul HTTP pentru accesarea din PL/SQL a datelor publicate pe
Internet);
UTL_TCP (permite aplicatiilor PL/SQL sa comunice cu server-e externe utiliznd protocolul
TCP/IP);
DBMS_SQL (acceseaza baza de date folosind SQL dinamic);
DBMS_JOB (permite planificarea pentru executie a programelor PL/SQL si executia acestora);
DBMS_PIPE (permite operatii de comunicare ntre doua sau mai multe procese conectate la aceeasi
instanta Oracle);
DBMS_LOCK (permite folosirea exclusiva sau partajata a unei resurse);
DBMS_SNAPSHOT (permite exploatarea cliseelor);
DBMS_UTILITY (ofera utilitati DBA, analizeaza obiectele unei scheme particulare, verifica daca
server-ul lucreaza n mod paralel etc.);


67

DBMS_LOB (realizeaza accesul la date de tip LOB, permitnd compararea datelor LOB, adaugarea
de date la un LOB, copierea datelor dintr-un LOB n altul, stergerea unor portiuni din date LOB,
deschiderea, nchiderea si regasirea de informatii din date BFILE etc).
DBMS_STANDARD este un pachet predefinit fundamental prin care se declara tipurile, exceptiile,
subprogramele care pot fi utilizate automat n toate programele PL/SQL. Continutul pachetului este vizibil
tuturor aplicatiilor. Pentru referirea componentelor sale nu este necesara prefixarea cu numele pachetului. De
exemplu, utilizatorul poate folosi n aplicatia sa ori de cte ori are nevoie functia ABS (x) din pachetul
DBMS_STANDARD, care reprezinta valoarea absoluta a numarului x, fara a prefixa numele functiei cu numele
pachetului.
Pachetul DBMS_OUTPUT
Un pachet frecvent utilizat este DBMS_OUTPUT, care permite afisarea de informatii atunci cnd se
executa un program PL/SQL. Pachetul este util pentru depanarea procedurilor stocate si a declansatorilor sau
pentru generarea de rapoarte.
DBMS_OUTPUT lucreaza cu un buffer (continut n SGA) n care poate fi scrisa informatie utiliznd
procedurile PUT, PUT_LINE si NEW_LINE. Aceasta informatie poate fi regasita folosind procedurile
GET_LINE si GET_LINES. Procedura DISABLE dezactiveaza toate apelurile la pachetul DBMS_OUTPUT (cu
exceptia procedurii ENABLE) si curata buffer-ul de orice informatie.
Inserarea n buffer a unui sfrsit de linie se face prin procedura NEW_LINE. Procedura PUT depune
(scrie) informatie n buffer, informatie care poate fi de tip NUMBER, VARCHAR2 sau DATE. PUT_LINE are
acelasi efect ca procedura PUT, dar insereaza si un sfrsit de linie. Procedurile PUT si PUT_LINE sunt overload,
astfel nct informatia poate fi scrisa n format nativ.
Procedura GET_LINE regaseste o singura linie de informatie (de dimensiune maxima 255) din buffer, dar
sub forma de sir de caractere. Procedura GET_LINES regaseste mai multe linii (nr_linii) din buffer si le depune
ntr-un tablou (nume_tab) PL/SQL care are elemente de tip sir de caractere. Valorile sunt plasate n tabel
ncepnd cu linia zero. Specificatia este urmatoarea:
TYPE string255_table IS TABLE OF VARCHAR2(255)
INDEX BY BINARY_INTEGER;
PROCEDURE GET_LINES
(nume_tab OUT string255_table, nr_linii IN OUT INTEGER);
Parametrul nr_linii este si parametru de tip OUT, deoarece numarul liniilor solicitate poate sa nu
corespunda cu numarul de linii din buffer. De exemplu, pot fi solicitate 10 linii, iar n buffer sa fie doar 6 linii.
Atunci doar primele 6 linii din tabel sunt returnate.
Dezactivarea referirilor la pachet se poate realiza prin procedura DISABLE, iar activarea referirilor se
face cu ajutorul procedurii ENABLE. Aceasta are un parametru de intrare optional, dimensiunea buffer-ului, care
implicit este 2000. Procedura ENABLE poate fi apelata de mai multe ori ntr-o sesiune, iar dimensiunea buffer-
ului va fi setata la cea mai mare valoare data de aceste apeluri. Prin urmare, dimensiunea nu este ntotdeauna
data de ultimul apel la procedura.
Exemplu:
Urmatorul exemplu plaseaza n buffer (apelnd de trei ori procedura PUT) toate informatiile ntr-o
singura linie.
DBMS_OUTPUT.PUT(:opera.valoare||:opera.cod_artist);
DBMS_OUTPUT.PUT(:opera.cod_opera);
DBMS_OUTPUT.PUT(:opera.cod_galerie);
Informatia respectiva va fi gasita printr-un singur apel GET_LINE, daca aceste trei comenzi sunt urmate
de:
DBMS_OUTPUT.NEW_LINE;


68

Altfel, nu se va vedea nici un efect al acestor comenzi deoarece PUT plaseaza informatia n buffer, dar nu
adauga sfrsit de linie.
Atunci cnd este utilizat pachetul DBMS_OUTPUT, pot sa apara erorile buffer overflow si line length
overflow. Tratarea acestor erori se face apelnd procedura RAISE_APPLICATION_ERROR din pachetul
DBMS_STANDARD.
Pachetul DBMS_SQL
Pachetul DBMS_SQL permite folosirea dinamica a comenzilor SQL n proceduri stocate sau n blocuri
anonime si analiza gramaticala a comenzilor LDD. Aceste comenzi nu sunt ncorporate n programul sursa, ci
sunt depuse n siruri de caractere. O comanda SQL dinamica este o instructiune SQL care contine variabile ce se
pot schimba n timpul executiei.
Pot fi utilizate instructiuni SQL dinamice pentru:
a crea o procedura care opereaza asupra unui tabel al carui nume nu este cunoscut dect n momentul
executiei;
a scrie si executa o comanda LDD;
a scrie si executa o comanda GRANT, ALTER SESSION etc.
n PL/SQL aceste comenzi nu pot fi executate static. Pachetul DBMS_SQL permite, de exemplu, ca ntr-
o procedura stocata sa apara comanda DROP TABLE. Evident, folosirea acestui pachet pentru a executa
comenzi LDD poate genera interblocari. De exemplu, daca pachetul este utilizat pentru a sterge o procedura
care, nsa, este folosita ulterior stergerii ei.
SQL dinamic suporta toate tipurile de date SQL, dar nu si pe cele specifice PL/SQL. Unica exceptie o
constituie faptul ca o nregistrare PL/SQL poate sa apara n clauza INTO a comenzii EXECUTE IMMEDIATE.
Orice comanda SQL (pentru a fi executata) trebuie sa treaca prin niste etape, cu observatia ca unele
dintre acestea pot fi evitate. Etapele presupun:
analizarea gramaticala a comenzii, adica verificarea sintactica a comenzii, validarea acesteia,
asigurarea ca toate referintele la obiecte sunt corecte si ca exista privilegiile referitoare la acele
obiecte (parse);
obtinerea de valori pentru variabilele de legatura din comanda SQL (binding variables);
executarea comenzii (execute);
selectarea liniilor rezultatului (se refera la cereri, nu la operatii de reactualizare);
ncarcarea acestor linii (fetch).
Dintre subprogramele pachetului DBMS_SQL, care permit implementarea etapelor amintite anterior se
remarca:
OPEN_CURSOR (deschide un nou cursor, adica se stabileste o zona de memorie n care este
procesata comanda SQL);
PARSE (stabileste validitatea comenzii SQL, adica instructiunea este verificata sintactic si asociata
cursorului deja deschis);
BIND VARIABLE (leaga valoarea data de variabila corespunzatoare din instructiunea SQL analizata);
EXECUTE (executa comanda SQL si returneaza numarul de linii procesate);
FETCH_ROWS (regaseste o linie pentru un cursor specificat, iar pentru mai multe linii se foloseste
un LOOP);
CLOSE_CURSOR (nchide cursorul specificat).
Exemplu:
Sa se construiasca o procedura care foloseste SQL dinamic pentru a sterge liniile unui tabel specificat
(num_tab). Subprogramul furnizeaza ca rezultat numarul liniilor sterse (nr_lin).
CREATE OR REPLACE PROCEDURE sterge_linii
(num_tab IN VARCHAR2, nr_lin OUT NUMBER) AS


69

nume_cursor INTEGER;
BEGIN
nume_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE (nume_cursor, 'DELETE FROM ' ||
num_tab, DBMS_SQL.V7);
nr_lin := DBMS_SQL.EXECUTE (nume_cursor);
DBMS_SQL.CLOSE_CURSOR (nume_cursor);
END;
Prin argumentul DBMS_SQL.V7 este specificata versiunea 7 a sistemului si el reprezinta modul n care
Oracle trateaza executia comenzilor SQL.
Stergerea efectiva a liniilor tabelului opera se realizeaza prin secventa:
VARIABLE linii_sterse NUMBER
EXECUTE sterge_linii ('opera', :linii_sterse)
PRINT linii_sterse
Pentru a executa o instructiune SQL dinamic poate fi utilizata si comanda EXECUTE IMMEDIATE, care
va fi analizata n capitolul urmator. Comanda poate fi folosita att pentru interogarile ce returneaza o singura
linie, ct si pentru cele care returneaza mai multe linii.
Pachetul DBMS_DDL
Pachetul DBMS_DDL furnizeaza accesul la anumite comenzi LDD care pot fi folosite n subprograme
PL/SQL stocate. n felul acesta pot fi accesate (n PL/SQL) comenzile ALTER sau ANALYZE.
Pachetul include procedura ALTER_COMPILE care permite recompilarea explicita a unui program
modificat (procedura, functie, declansator, pachet, corp de pachet). Ea are urmatoarea forma sintactica:
ALTER_COMPI LE (tip_obiect, nume_schema, nume_obiect);
Procedura este echivalenta cu instructiunea SQL:
ALTER PROCEDURE | FUNCTI ON | PACKAGE [schema.] nume
COMPI LE [BODY]
Cu ajutorul procedurii ANALYZE_OBJECT poate fi analizat un obiect de tip table, cluster sau index.
Procedura furnizeaza statistici referitoare la obiectele amintite. De exemplu, se pot obtine numarul liniilor sau
numarul de blocuri ale unui tabel, lungimea medie a unei linii, numarul valorilor distincte ale unei coloane,
numarul elementelor null dintr-o coloana, distributia datelor (histograma) etc.
Procedura are forma sintactica:
ANALYZE_OBJ ECT (tip_obiect, nume_schema, nume_obiect, metoda,
numar_linii_estimate, procent, optiune_metoda, nume_partitie);
Metodele care pot fi utilizate n procedura ANALYZE_OBJECT sunt COMPUTE, ESTIMATE sau
DELETE. Prin aceste metode se cuantifica distributia datelor si caracteristicile de stocare. DELETE determina
stergerea statisticilor (depuse n DD) referitoare la obiectul analizat. COMPUTE calculeaza statistici referitoare
la un obiect analizat si le depune n DD, iar ESTIMATE estimeaza statistici. Statisticile calculate sau estimate
sunt utilizate pentru optimizarea planului de executie a comenzilor SQL care acceseaza obiectele analizate.
Procedura este echivalenta cu instructiunea:
ANALYZE TABLE | CLUSTER | I NDEX [nume_schema.]nume_obiect
[metoda] STATI STI CS [SAMPLE n] [ROWS | PERCENT] ]
Daca nume_schema este null, atunci se presupune ca obiectul apartine schemei curente. Pentru tip_obiect
sunt permise variantele table, index sau cluster. Parametrul procent reprezinta procentajul liniilor de estimat si
este ignorat daca este specificat numarul liniilor de estimat. n mod implicit, ultimele patru argumente ale
procedurii iau valoarea null.
Argumentul optiune_metoda poate avea forma:


70

[FOR TABLE] [FOR ALL I NDEXES]
[FOR ALL [I NDEXED] COLUMNS] [SI ZE n]
Pentru metoda ESTIMATE trebuie sa fie prezenta una dintre aceste optiuni.
Exemplu:
Utiliznd pachetul DBMS_DDL si metoda COMPUTE, sa se creeze o procedura care analizeaza un
obiect furnizat ca parametru.
CREATE OR REPLACE PROCEDURE analiza
(p_obiect_tip IN VARCHAR2,
p_obiect_nume IN VARCHAR2)
IS
BEGIN
DBMS_DDL.ANALYZE_OBJECT(p_obiect_tip, USER,
UPPER(p_obiect_nume), 'COMPUTE');
END;
Procedura se testeaza (relativ la tabelul opera) n felul urmator:
EXECUTE analiza ('TABLE', 'opera')
SELECT LAST_ANALYZED
FROM USER_TABLES
WHERE TABLE_NAME = 'opera';
Pachetul DBMS_J OB
Pachetul DBMS_JOB este utilizat pentru planificarea programelor PL/SQL n vederea executiei. Cu
ajutorul acestui pachet se pot executa programe PL/SQL la momente determinate de timp, se pot sterge sau
suspenda programe din lista de planificari pentru executie, se pot rula programe de ntretinere n timpul
perioadelor de utilizare scazuta etc.
Dintre subprogramele acestui pachet se remarca:
SUBMIT adauga un nou job n coada de asteptare a job-urilor;
REMOVE sterge un job specificat din coada de asteptare a job-urilor;
RUN executa imediat un job specificat;
BROKEN dezactiveaza executia unui job care este marcat ca broken (implicit, orice job este not
broken, iar un job marcat broken nu se executa);
CHANGE modifica argumentele WHAT, NEXT_DATE, INTERVAL;
WHAT modifica descrierea unui job specificat;
NEXT_DATE modifica momentul urmatoarei executii a unui job;
INTERVAL modifica intervalul ntre diferite executii ale unui job.
Fiecare dintre subprogramele pachetului are argumente specifice. De exemplu, procedura
DBMS_JOB.SUBMIT are ca argumente:
JOB de tip OUT, un identificator pentru job (BINARY_INTEGER);
WHAT de tip IN, codul PL/SQL care va fi executat ca un job (VARCHAR2);
NEXT_DATE de tip IN, data urmatoarei executii a job-ului (implicit este SYSDATE);
INTERVAL de tip IN, functie care furnizeaza intervalul dintre executiile job-ului (VARCHAR2,
implicit este null);
NO_PARSE de tip IN, variabila logica care indica daca job-ul trebuie analizat gramatical
(BOOLEAN, implicit este FALSE).
Daca unul dintre parametri WHAT, INTERVAL sau NEXT_DATE are valoarea null, atunci este folosita
ultima valoare asignata acestora.
Exemplu:


71

Sa se utilizeze pachetul DBMS_JOB pentru a plasa pentru executie n coada de asteptare a job-urilor,
procedura verifica din pachetul verif_pachet.
VARIABLE num_job NUMBER
BEGIN
DBMS_JOB.SUBMIT(
job => :num_job,
what => 'verif_pachet.verifica(8973,''impresionism'');'
next_date => TRUNC(SYSDATE+1),
interval => 'TRUNC(SYSDATE+1)');
COMMIT;
END;
/
PRINT num_job
Vizualizarea DBA_JOBS din dictionarul datelor furnizeaza informatii referitoare la starea job-urilor din
coada de asteptare, iar vizualizarea DBA_JOBS_RUNNING contine informatii despre job-urile care sunt n curs
de executie. Vizualizarile pot fi consultate doar de utilizatorii care au privilegiul SYS.DBA_JOBS.
Exemplu:
SELECT JOB, LOG_USER, NEXT_DATE, BROKEN, WHAT
FROM DBA_JOBS;
Pachetul UTL_FI LE
Pachetul UTL_FILE permite programului PL/SQL citirea din fisierele sistemului de operare, respectiv
scrierea n aceste fisiere. El este utilizat pentru exploatarea fisierelor text.
Pachetul proceseaza fisierele ntr-o maniera clasica:
verifica daca fisierul este deschis (functia IS_OPEN);
daca fisierul nu este deschis, l deschide si returneaza un handler de fisier (de tip
UTL_FILE.FILE_TYPE) care va fi utilizat n urmatoarele operatii I/O (functia FOPEN);
proceseaza fisierul (citire/scriere din/n fisier);
nchide fisierul (procedura FCLOSE).
Prin procedura GET_LINE, pachetul UTL_FILE citeste o linie de text din fisierul deschis pentru citire si
o plaseaza ntr-un buffer de tip sir de caractere, iar prin procedurile PUT si PUT_LINE scrie un text din buffer n
fisierul deschis pentru scriere sau adaugare.
Utilizarea componentelor acestui pachet pentru procesarea fisierelor sistemului de operare poate declansa
urmatoarele exceptii:
INVALID_PATH numele sau locatia fisierului sunt invalide;
INVALID_MODE parametrul OPEN_MODE (prin care se specifica daca fisierul este deschis
pentru citire, scriere, adaugare) este invalid;
INVALID_FILEHANDLE handler-ul de fisier obtinut n urma deschiderii este invalid;
INVALID_OPERATION operatie invalida asupra fisierului;
READ_ERROR o eroare a sistemului de operare a aparut n timpul operatiei de citire;
WRITE_ERROR o eroare a sistemului de operare a aparut n timpul operatiei de scriere;
INTERNAL_ERROR o eroare nespecificata a aparut n PL/SQL.
Pachetele DBMS_PI PE si DBMS_ALERT
Pachetul DBMS_PIPE permite operatii de comunicare ntre doua sau mai multe sesiuni conectate la
aceeasi baza de date. De exemplu, pachetul poate fi utilizat pentru comunicarea dintre o procedura stocata si un
program Pro*C. Comunicarea se face prin conducte (pipe). O conducta este o zona de memorie utilizata de un
proces pentru a transmite informatie altui proces. Informatia trimisa prin conducta este depusa ntr-un buffer din


72

SGA. Toate informatiile din conducta sunt pierdute atunci cnd instanta este nchisa.
Conductele sunt asincrone, ele opernd independent de tranzactii. Daca un anumit mesaj a fost transmis,
nu exista nici o posibilitate de oprire a acestuia, chiar daca sesiunea care a trimis mesajul este derulata napoi
(rollback).
Pachetul DBMS_PIPE este utilizat pentru a trimite mesaje n conducta
(DBMS_PIPE.SEND_MESSAGE), mesaje ce constau din date de tip VARCHAR2, NUMBER, DATE, RAW sau
ROWID. Tipurile obiect definite de utilizator si colectiile nu sunt acceptate de acest pachet.
De asemenea, pachetul poate realiza primirea de mesaje din conducta n buffer-ul local
(DBMS_PIPE.RECEIVE_MESSAGE), accesarea urmatorului articol din buffer
(DBMS_PIPE.UNPACK_MESSAGE), crearea unei noi conducte (DBMS_PIPE. CREATE_PIPE) etc.
DBMS_ALERT este similar pachetului DBMS_PIPE, fiind utilizat tot pentru comunicarea dintre sesiuni
conectate la aceeasi baza de date. Exista totusi cteva deosebiri esentiale.
DBMS_ALERT asigura o comunicare sincrona.
Un mesaj trimis prin DBMS_PIPE este primit de catre un singur destinatar (cititor), chiar daca exista
mai multi pe conducta, pe cnd cel trimis prin DBMS_ALERT poate fi primit de mai multi cititori
simultan.
Daca doua mesaje sunt trimise printr-o conducta (nainte ca ele sa fie citite), ambele vor fi primite de
destinatar prin DBMS_PIPE. n cazul pachetului DBMS_ALERT, doar cel de al doilea mesaj va fi
primit.
Pachete predefinite furnizate de Oracle9i
Oracle9i furnizeaza o varietate de pachete predefinite care simplifica administrarea bazei de date si ofera
noi functionalitati legate de noile caracteristici ale sistemului. Dintre pachetele introduse n versiunea Oracle9i se
remarca:
DBMS_REDEFINITION permite reorganizarea online a tabelelor;
DBMS_LIBCACHE permite extragerea de comenzi SQL si PL/SQL dintr-o instanta distanta ntr-
una una locala (vor fi compilate local, dar nu executate);
DBMS_LOGMNR_CDC_PUBLISH realizeaza captarea schimbarilor din tabelele bazei de date
(identifica datele adaugate, modificate sau sterse si editeaza aceste informatii ntr-o forma utilizabila
n aplicatii);
DBMS_LOGMNR_CDC_SUBSCRIBE face posibila vizualizarea si interogarea schimbarilor din
datele care au fost captate cu pachetul DBMS_LOGMNR_CDC_PUBLISH;
DBMS_METADATA furnizeaza informatii despre obiectele bazei de date;
DBMS_RESUMABLE permite setarea limitelor de spatiu si timp pentru o operatie specificata,
operatia fiind suspendata daca sunt depasite aceste limite;
DBMS_XMLQUERY, DBMS_XMLSAVE, DBMS_XMLGEN permit prelucrarea si conversia
datelor XML (XMLGEN converteste rezultatul unei cereri SQL n format XML, XMLQUERY este
similara lui XMLGEN, doar ca este scrisa n C, iar XMLSAVE face conversia din format XML n date
ale bazei);
DBMS_LDAP furnizeaza functii si proceduri pentru accesarea datelor de pe server-e LDAP
(Lightweight Directory Access Protocol);
UTL_ENCODE permite codificarea si decodificarea ntr-un format standard a unei date de tip
RAW, astfel nct datele sa poata fi transferate ntre diferite gazde (host-uri);
UTL_INADDR returneaza numele unei gazde locale sau distante a carei adresa IP este cunoscuta si
reciproc, returneaza adresa IP a unei gazde careia i se cunoaste numele (de exemplu,
www.oracle.com);
DBMS_AQELM furnizeaza proceduri si functii pentru gestionarea configuratiei cozilor de mesaje
asincrone prin e-mail si HTTP;


73

DBMS_FGA asigura ntretinerea unor functii de securitate;
DBMS_FLASHBACK permite trecerea la o versiune a bazei de date corespunzatoare unei unitati de
timp specificate sau unui SCN (system change number) dat, n felul acesta putnd fi recuperate linii
sterse sau mesaje e-mail distruse;
DBMS_TRANSFORM furnizeaza subprograme ce permit transformarea unui obiect (expresie SQL
sau functie PL/SQL) de un anumit tip (sursa) ntr-un obiect avnd un tip (destinatie) specificat;
DBMS_WM ofera functionalitati legate de Workspace Manager. Gestiunea spatiului de lucru se
refera la abilitatea bazei de date de a pastra diferite versiuni ale aceleasi linii n unul sau mai multe
spatii de lucru (virtuale), utilizatorii bazei putnd modifica independent aceste versiuni. De exemplu,
pachetul ofera posibilitatea de a modifica descrierea unui punct de salvare, de a sterge puncte de
salvare, de a lista privilegiile pe care le are utilizatorul curent referitor la un anumit spatiu de lucru, de
a determina daca exista conflicte ntre spatii de lucru etc.



8. Folosirea dinamica a comenzilor SQL n PL/SQL


SQL dinamic este o parte integranta a limbajului SQL care permite folosirea dinamica a comenzilor sale n
proceduri stocate sau n blocuri anonime. Spre deosebire de comenzile statice, care nu se schimba n timp real,
comenzile dinamice se schimba de la o executie la alta. Comenzile dinamice SQL pot depinde de anumite valori
de intrare furnizate de utilizator sau de procesarea realizata n programul aplicatie. Ele nu sunt incorporate n
programul sursa, ci sunt depuse n siruri de caractere.
SQL dinamic este o tehnica de programare care permite construirea dinamica a comenzilor la momentul
executiei. Textul comenzii nu este cunoscut la compilare. De exemplu, se creeaza o procedura care opereaza
asupra unui tabel al carui nume este cunoscut doar cnd se executa procedura. n momentul compilarii este
cunoscuta definitia tabelelor, dar nu si numele acestora. Exista aplicatii (de exemplu, legate de data warehouse)
n care la fiecare unitate de timp (de exemplu, sfert de ora) sunt generate noi tabele, toate avnd aceeasi
structura.
Utilitatea tehnicii SQL dinamic este justificata de motive majore, dintre care se remarca:
necesitatea de a executa n PL/SQL, comenzi SQL care nu pot fi apelate n codul PL/SQL (de
exemplu, CREATE, DROP, GRANT, REVOKE, ALTER SESSION, SET ROLE);
necesitatea unei flexibilitati n tratarea comenzilor (de exemplu, posibilitatea de a avea diferite conditii
n clauza WHERE a comenzii SELECT);
necunoasterea completa, la momentul implementarii, a comenzii SQL care trebuie executata.
Pentru executia dinamica a comenzilor SQL n PL/SQL exista doua tehnici:
utilizarea pachetului DBMS_SQL;
SQL dinamic nativ.
Daca s-ar face o comparatie ntre SQL dinamic nativ si functionalitatea pachetului DBMS_SQL, se poate
sublinia ca SQL dinamic nativ este mai usor de utilizat, mai rapid si are avantajul ca suporta tipuri definite de
utilizator.
Pachetul DBMS_SQL, n raport cu SQL dinamic nativ:
poate fi folosit n programe client-side;
suporta comenzi SQL mai mari de 32 KB;
permite ncarcarea nregistrarilor (procedura FETCH_ROWS);
accepta comenzi cu clauza RETURNING pentru reactualizarea si stergerea de linii multiple;
suporta posibilitatile oferite de comanda DESCRIBE (procedura DESCRIBE_COLUMNS);
analizeaza validitatea unei comenzi SQL o singura data (procedura PARSE), permitnd ulterior mai


74

multe utilizari ale comenzii pentru diferite multimi de argumente.
SQL dinamic nativ a fost introdus n Oracle8i, asigurnd plasarea de comenzi SQL dinamic n codul
PL/SQL. Comanda de baza utilizata pentru procesarea dinamica nativa a comenzilor SQL si a blocurilor PL/SQL
este EXECUTE IMMEDIATE, care are urmatoarea sintaxa:
EXECUTE I MMEDI ATE sir_dinamic
[I NTO {def_variabila [, def_variabila ] | record} ]
[USI NG [I N | OUT | I N OUT] argument_bind
[, [I N | OUT | I N OUT] argument_bind ] ]
[ {RETURNI NG | RETURN}
I NTO argument_bind [, argument_bind ] ];
Atributul sir_dinamic este o expresie (sir de caractere) care reprezinta o comanda SQL (fara caracter de
terminare) sau un bloc PL/SQL (avnd caracter de continuare); def_variabila reprezinta variabila n care se
stocheaza valoarea coloanei selectate; record reprezinta nregistrarea n care se depune o linie selectata;
argument_bind, daca se refera la valori de intrare (IN) este o expresie (comanda SQL sau bloc PL/SQL), iar daca
se refera la valori de iesire (OUT) este o variabila ce va contine valoarea selectata de comanda SQL sau de blocul
PL/SQL.
Clauza INTO este folosita pentru cereri care ntorc o singura linie, iar clauza USING pentru a retine
argumentele de legatura. Pentru procesarea unei cereri care returneaza mai multe linii sunt necesare instructiunile
OPENFOR, FETCH si CLOSE. Prin clauza RETURNING sunt precizate variabilele care contin rezultatele.
Observatii:
SQL dinamic suporta toate tipurile SQL, dar nu accepta tipuri de date specifice PL/SQL (unica
exceptie este tipul RECORD care poate sa apara n clauza INTO).
n subprogramele PL/SQL pot sa fie executate dinamic comenzi SQL care se refera la obiecte
apartinnd unei baze de date distante.
n anumite situatii, o comanda LDD poate crea o interblocare. De exemplu, o procedura poate genera
o interblocare daca n corpul procedurii exista o comanda care sterge chiar procedura respectiva. Prin
urmare, niciodata nu pot fi utilizate comenzile ALTER sau DROP referitoare la un subprogram sau
pachet n timp ce se lucreaza cu pachetul sau subprogramul respectiv.
Exemplu:
Sa se construiasca o procedura care poate sterge orice tabel din baza de date. Numele tabelului sters este
transmis ca parametru acestei proceduri.
CREATE PROCEDURE sterge_tabel (nume_tabel IN VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE :tab'
USING nume_tabel;
END;
La executia acestei proceduri va fi semnalata o eroare, deoarece nu pot fi utilizate variabile de legatura ca
argument, pentru a transmite numele obiectelor dintr-o schema. Prin urmare comanda corecta este:
EXECUTE IMMEDIATE 'DROP TABLE ' || nume_tabel;
Exemplu:
Valoarea null nu poate sa apara n clauza USING. Comanda urmatoare este incorecta.
EXECUTE IMMEDIATE 'UPDATE opera SET valoare = :x'
USING null;
Totusi, daca este necesara folosirea valorii null, pot fi utilizate variabile neinitializate. O solutie pentru
corectarea erorii anterioare este data de secventa:
DECLARE


75

val_null NUMBER;
BEGIN
EXECUTE IMMEDIATE 'UPDATE opera SET valoare = :x'
USING val_null;
END;
Exemplu:
Sa se obtina numarul operelor de arta din muzeu a caror valoare depaseste o limita precizata (cerere
single-row).
CREATE OR REPLACE FUNCTION numar_opera (val_opera NUMBER)
RETURN NUMBER AS
sir_cerere VARCHAR2(500);
num_oper NUMBER;
BEGIN
sir_cerere :=
'SELECT COUNT(*) FROM opera ' ||
'WHERE valoare >= :alfa';
EXECUTE IMMEDIATE sir_cerere
INTO num_oper
USING val_opera;
RETURN num_oper;
END;
Exemplu:
Exemplul care urmeaza furnizeaza o modalitate de utilizare corecta a argumentelelor n clauza USING.
a) Pentru comenzi SQL, asocierea cu argumentele de legatura (bind) din clauza USING este prin pozitie.
sql_com := 'INSERT INTO alfa VALUES (:x, :x, :y, :x)';
EXECUTE IMMEDIATE sql_com USING a, a, b, a
b) Pentru blocuri PL/SQL executate dinamic, asocierea cu argumentele bind din clauza USING se face
prin nume.
DECLARE
x NUMBER := 7;
y NUMBER := 23;
v_bloc VARCHAR2(200);
BEGIN
v_bloc := 'BEGIN calcule(:a, :a, :b, :a); END;';
EXECUTE IMMEDIATE v_bloc USING x, y;
END;
n exemplul care urmeaza va fi ilustrat modul de utilizare a comenzii EXECUTE IMMEDIATE pentru
executarea comenzilor LDD, comenzilor LMD de reactualizare si a blocurilor PL/SQL anonime. Sirul care
trebuie executat poate fi un literal inclus ntre apostrofuri (de exemplu, CREATE TABLE sau DROP TABLE) sau
poate fi un sir de caractere (de exemplu, blocuri anonime). Caracterul ; nu trebuie inclus dect pentru blocurile
anonime.
Exemplu:
DECLARE
v_sql_sir VARCHAR2(200);
v_plsql_bloc VARCHAR2(200);
BEGIN
-- creare tabel
EXECUTE IMMEDIATE
'CREATE TABLE model_tabel (col1 VARCHAR(30))';
FOR contor IN 1..10 LOOP
v_sql_sir :=
'INSERT INTO model_tabel


76

VALUES (''Linia ' || contor || ''')';
EXECUTE IMMEDIATE v_sql_sir;
END LOOP;
-- tipareste continut tabel utilizand un bloc anonim
v_plsql_bloc :=
'BEGIN
FOR cont IN (SELECT * FROM model_tabel) LOOP
DBMS_OUTPUT.PUT_LINE (cont.col1);
END LOOP;
END;';
-- executie bloc anonim
EXECUTE IMMEDIATE v_plsql_bloc;
-- sterge tabel
EXECUTE IMMEDIATE 'DROP TABLE model_tabel';
END;
Comanda EXECUTE IMMEDIATE poate fi utilizata si pentru executia unor comenzi n care intervin
variabile bind. Exemplul urmator ilustreaza aceasta situatie, marcnd si modul de folosire a clauzei USING.
Exemplu:
DECLARE
v_sql_sir VARCHAR2(200);
v_plsql_bloc VARCHAR2(200);
BEGIN
v_sql_sir :=
'INSERT INTO opera (cod_opera, titlu, valoare)
VALUES (:cod, :descriere, :val)';
EXECUTE IMMEDIATE v_sql_sir USING
'c17', 'Modista', 15;
v_plsql_bloc :=
'BEGIN
UPDATE artist SET nume = ''Gauguin''
WHERE cod_artist = :xx;
END;';
EXECUTE IMMEDIATE v_plsql_bloc USING 'a37';
END;
Pentru executarea cererilor multiple este necesara o abordare similara celei descrise n cazul cursoarelor
dinamice, prin utilizarea comenzilor OPENFOR, FETCH si CLOSE. n exemplul care urmeaza este prezentata
maniera n care pot fi executate diferite cereri, utiliznd SQL dinamic nativ.
Exemplu:
CREATE OR REPLACE PACKAGE nativ AS
TYPE t_ref IS REF CURSOR;
FUNCTION opera_cerere (p_clauza IN VARCHAR2)
RETURN t_ref;
FUNCTION opera_alta_cerere (p_stil IN VARCHAR2)
RETURN t_ref;
END nativ;
CREATE OR REPLACE PACKAGE BODY nativ AS
FUNCTION opera_cerere (p_clauza IN VARCHAR2)
RETURN t_ref IS
v_retur_cursor t_ref;
v_sql_comanda VARCHAR2(500);
BEGIN
v_sql_comanda := 'SELECT * FROM opera ' || p_clauza;
OPEN v_retur_cursor FOR v_sql_comanda;
RETURN v_retur_cursor;


77

END opera_cerere;
FUNCTION opera_alta_cerere (p_stil IN VARCHAR2)
RETURN t_ref IS
v_retur_cursor t_ref;
v_sql_comanda VARCHAR2(500);
BEGIN
v_sql_comanda := 'SELECT * FROM opera WHERE stil = :s';
OPEN v_retur_cursor FOR v_sql_comanda USING p_stil;
RETURN v_retur_cursor;
END opera_alta_cerere;
END nativ;
DECLARE
v_opera opera%ROWTYPE;
v_opera_cursor nativ.t_ref;
BEGIN
-- deschide cursor
v_opera_cursor :=
nativ.opera_cerere ('WHERE valoare < 1000000');
-- parcurge cursor si tipareste rezultate
DBMS_OUTPUT.PUT_LINE ('Urmatoarele opere au valoarea mai
mica de un milion de dolari');
LOOP
FETCH v_opera_cursor INTO v_opera;
EXIT WHEN v_opera_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_opera.titlu || ' ' ||
v_opera.stil);
END LOOP;
CLOSE v_opera_cursor;
-- se procedeaza similar pentru functia opera_alta_cerere
-- deschide cursor
v_opera_cursor :=
nativ.opera_alta_cerere ('impresionism');
-- parcurge cursor si tipareste rezultate
DBMS_OUTPUT.PUT_LINE ('Urmatoarele opere de arta apartin
impresionismului');
LOOP
FETCH v_opera_cursor INTO v_opera;
EXIT WHEN v_opera_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (v_opera.titlu || ' ' ||
v_opera.valoare);
END LOOP;
CLOSE v_opera_cursor;
END;
Comanda EXECUTE IMMEDIATE poate fi utilizata pentru cereri care ntorc o singura linie, cu sau fara
variabile bind. Urmatorul exemplu prezinta, n trei cazuri, modul de implementare a acestei posibilitati.
Exemplu:
DECLARE
v_sql_cerere VARCHAR2(200);
v_galerie galerie%ROWTYPE;
v_nume_galerie galerie.nume_galerie%TYPE;
BEGIN
-- selectare in variabila
v_sql_cerere :=
'SELECT nume_galerie ' ||


78

'FROM galerie ' ||
'WHERE cod_galerie = ''g73''';
EXECUTE IMMEDIATE v_sql_cerere INTO v_nume_galerie;
DBMS_OUTPUT.PUT_LINE (v_nume_galerie);
-- selectare in inregistrare, utilizand variabile bind
v_sql_cerere :=
'SELECT * ' ||
'FROM galerie ' ||
'WHERE nume_galerie = :nume_galerie';
EXECUTE IMMEDIATE v_sql_cerere INTO v_galerie
USING v_nume_galerie;
DBMS_OUTPUT.PUT_LINE (v_galerie.cladire);
-- incarcarea unei noi linii va genera eroarea ORA-1422
v_sql_cerere :=
'SELECT * FROM galerie';
EXECUTE IMMEDIATE v_sql_cerere INTO v_galerie;
END;
Structura Bulk bind permite executarea cererilor care returneaza mai multe linii, toate liniile returnate
putnd fi obtinute printr-o singura operatie. Puterea acestei structuri poate fi combinata cu facilitatile oferite de
SQL dinamic. Utiliznd comenzile FETCH, EXECUTE IMMEDIATE, FORALL, clauzele RETURNING INTO,
COLLECT INTO si atributul cursor %BULK_ROWCOUNT se pot construi comenzi SQL care se executa
dinamic utiliznd tehnica bulk bind. Comenzile vor avea o structura adaptata pentru rezolvarea dinamica a
comenzilor SQL.
n acest caz, comanda FETCH are forma sintactica:
FETCH cursor_dinamic
BULK COLLECT I NTO variabila [, variabila ];
De remarcat ca daca numarul variabilelor este mai mare dect numarul de coloane, Oracle va declansa o
eroare.
Comanda FORALL va avea urmatoarea structura modificata:
FORALL index I N lim_inf .. lim_sup
EXECUTE I MMEDI ATE sir_dinamic
USI NG argument_bind | argument_bind(index)
[, argument_bind | argument_bind(index) ]
[ {RETURNI NG | RETURN} BULK COLLECT
I NTO argument_bind [, argument_bind ] ];
Atributul sir_dinamic accepta comenzile INSERT, UPDATE si DELETE, dar nu comanda SELECT.
Liniile de valori returnate de comanda EXECUTE IMMEDIATE pot fi depuse ntr-o colectie. Comanda
si modifica structura n urmatoarea maniera:
EXECUTE I MMEDI ATE sir_dinamic
[ [BULK COLLECT] I NTO variabila [, variabila ]
[USI NG argument_bind [, argument_bind ] ]
[ {RETURNI NG | RETURN}
BULK COLLECT I NTO argument_bind [, argument_bind ] ];
Exemplu:
Exemplul care urmeaza arata modul de utilizare a clauzei BULK COLLECT n comenzile FETCH si
EXECUTE IMMEDIATE.
DECLARE
TYPE opera_ref IS REF CURSOR;
TYPE tab_num IS TABLE OF NUMBER;


79

TYPE car_tab IS TABLE OF VARCHAR2(30);
oper opera_ref;
num1 tab_num;
car1 car_tab;
num2 tab_num;
BEGIN
OPEN oper FOR 'SELECT cod_opera, valoare FROM opera';
FETCH oper BULK COLLECT INTO car1, num1;
CLOSE oper;
EXECUTE IMMEDIATE 'SELECT valoare FROM opera'
BULK COLLECT INTO num2;
END;
Numai comenzile INSERT, UPDATE si DELETE pot avea variabile bind ca argumente OUT.
Exemplu:
DECLARE
TYPE tab IS TABLE OF VARCHAR2(60);
v_val tab;
bun NUMBER := 100000;
com_sql VARCHAR2(200);
BEGIN
com_sql := 'UPDATE opera SET valoare = :1
RETURNING titlu INTO :2';
EXECUTE IMMEDIATE com_sql
USING bun RETURNING BULK COLLECT INTO v_val;
END;
Pentru a utiliza variabile bind ca intrari ntr-o comanda SQL (diferita de SELECT) se pot folosi comanda
FORALL si clauza USING.
Exemplu:
DECLARE
TYPE num IS TABLE OF NUMBER;
TYPE car IS TABLE OF VARCHAR2(30);
num1 num;
car1 car;
BEGIN
num1 := num(1, 2, 3, 4, 5);
FORALL i IN 1..5
EXECUTE IMMEDIATE
'UPDATE opera SET val = val*1.1
WHERE cod_opera = :1
RETURNING titlu INTO :2'
USING num1(i) RETURNING BULK COLLECT INTO car1;

END;
SQL dinamic poate fi apelat si din cadrul altor limbaje. Limbajele C/C++ apeleaza SQL dinamic prin OCI
(Oracle Call Interface) sau poate fi utilizat un precompilator Pro*C/C++ pentru adaugarea comenzilor
dinamice SQL la codul C. Limbajul Cobol poate utiliza comenzi dinamice SQL folosind un precompilator
Pro*Cobol, iar limbajul Java prin intermediul lui JDBC (interfata pentru conectarea limbajului la baze de date
relationale).



9. Gestiunea declansatorilor



80


Un declansator (trigger) este un bloc PL/SQL care se executa automat ori de cte ori are loc un anumit
eveniment declansator. Evenimentul poate consta din modificarea unui tabel sau a unei vizualizari, din actiuni
sistem sau chiar anumite actiuni utilizator. Blocul PL/SQL poate fi asociat unui tabel, unei vizualizari, unei
scheme sau unei baze de date. La fel ca si pachetele, declansatorii nu pot fi locali unui bloc sau unui pachet, ei
trebuie depusi ca obiecte independente n baza de date.
Folosirea declansatorilor garanteaza faptul ca atunci cnd o anumita operatie este efectuata, automat sunt
executate niste actiuni asociate. Evident, nu trebuie introdusi declansatori care ar putea sa substituie
functionalitati oferite deja de sistem. De exemplu, nu are sens sa fie definiti declansatori care sa implementeze
regulile de integritate ce pot fi definite, mai simplu, prin constrngeri declarative.
Tipuri de declansatori
Declansatorii pot fi:
la nivel de baza de date (database triggers);
la nivel de aplicatie (application triggers).
Declansatorii baza de date se executa automat ori de cte ori are loc:
o actiune (comanda LMD) asupra datelor unui tabel;
o actiune (comanda LMD) asupra datelor unei vizualizari;
o comanda LDD (CREATE, ALTER, DROP) referitoare la anumite obiecte ale schemei sau ale bazei;
un eveniment sistem (SHUTDOWN, STARTUP);
o actiune a utilizatorului (LOGON, LOGOFF);
o eroare (SERVERERROR, SUSPEND).
Declansatorii baza de date sunt de trei tipuri:
declansatori LMD activati de comenzi LMD (INSERT, UPDATE sau DELETE) executate asupra
unui tabel al bazei de date;
declansatori INSTEAD OF activati de comenzi LMD executate asupra unei vizualizari (relationale
sau obiect);
declansatori sistem activati de un eveniment sistem (oprirea sau pornirea bazei), de comenzi LDD
(CREATE, ALTER, DROP), de conectarea (deconectarea) unui utilizator. Ei sunt definiti la nivel de
schema sau la nivel de baza de date.
Declansatorii asociati unui tabel (stocati n baza de date) vor actiona indiferent de aplicatia care a efectuat
operatia LMD. Daca operatia LMD se refera la o vizualizare, declansatorul INSTEAD OF defineste actiunile care
vor avea loc, iar daca aceste actiuni includ comenzi LMD referitoare la tabele, atunci declansatorii asociati
acestor tabele sunt si ei, la rndul lor, activati. Daca declansatorii sunt asociati unei baze de date, ei se
declanseaza pentru fiecare eveniment, pentru toti utilizatorii. Daca declansatorii sunt asociati unei scheme sau
unui tabel, ei se declanseaza numai daca evenimentul declansator implica acea schema sau acel tabel. Un
declansator se poate referi la un singur tabel sau la o singura vizualizare.
Declansatorii aplicatie se executa implicit ori de cte ori apare un eveniment particular ntr-o aplicatie. De
exemplu, o aplicatie dezvoltata cu Developer Suite. Form Builder utilizeaza frecvent acest tip de declansatori
(form builder triggers). Ei pot fi declansati prin apasarea unui buton, prin navigarea pe un cmp etc. n acest
capitol se va face referinta doar la declansatorii baza de date.
Atunci cnd un pachet sau un subprogram este depus n dictionarul datelor, alaturi de codul sursa este
depus si p-codul compilat. n mod similar se ntmpla si pentru declansatori. Prin urmare, un declansator poate fi
apelat fara recompilare. Declansatorii pot fi invalidati n aceeeasi maniera ca pachetele si subprogramele. Daca
declansatorul este invalidat, el va fi recompilat la urmatoarea activare.
Crearea declansatorilor LMD
Declansatorii LMD sunt creati folosind comanda CREATE TRIGGER care are urmatoarea sintaxa


81

generala:
CREATE [OR REPLACE] TRI GGER [schema.]nume_declansator
{BEFORE | AFTER}
{DELETE | I NSERT | UPDATE [OF coloana[, coloana ] ] }
[OR {DELETE | I NSERT | UPDATE [OF coloana[, coloana ] ] }
ON [schema.]nume_tabel
[REFERENCING {OLD [AS] vechi NEW [AS] nou
| NEW [AS] nou OLD [AS] vechi } ]
[FOR EACH ROW]
[WHEN (conditie) ]
corp_declansator (bloc PL/SQL sau apelul unei proceduri);
Numele declansatorului trebuie sa fie unic printre numele declansatorilor din cadrul aceleasi scheme, dar
poate sa coincida cu numele altor obiecte ale acesteia (de exemplu, tabele, vizualizari sau proceduri).
La crearea unui declansator este obligatorie una dintre optiunile BEFORE sau AFTER, prin care se
precizeaza momentul n care este executat corpul declansatorului. Acesta nu poate depasi 32KB.
Pna la versiunea Oracle8i, corpul unui declansator trebuia sa fie un bloc PL/SQL. n ultimele versiuni,
corpul poate consta doar dintr-o singura comanda CALL. Procedura apelata poate fi un subprogram PL/SQL
stocat, o rutina C sau o metoda Java. n acest caz, CALL nu poate contine clauza INTO care este specifica
functiilor, iar pentru a referi coloanele tabelului asociat declansatorului, acestea trebuie prefixate de atributele
:NEW sau :OLD. De asemenea, n expresia parametrilor nu pot sa apara variabile bind.
Declararea unui declansator trebuie sa cuprinda tipul comenzii SQL care duce la executarea
declansatorului si tabelul asociat acestuia. n ceea ce priveste tipul comenzii SQL care va duce la executarea
declasatorului, sunt incluse urmatoarele tipuri de optiuni: DELETE, INSERT, UPDATE sau o combinare a
acestora cu operatorul logic OR. Cel putin una dintre aceste optiuni este obligatorie.
n declararea declansatorului este specificat tabelul asupra caruia va fi executat declansatorul. Oracle9i
admite tablouri imbricate. Daca declansatorul este de tip UPDATE, atunci pot fi enumerate coloanele pentru care
acesta se va executa.
n corpul fiecarui declansator pot fi cunoscute valorile coloanelor att nainte de modificarea unei linii,
ct si dupa modificarea acesteia. Valoarea unei coloane nainte de modificare este referita prin atributul OLD, iar
dupa modificare, prin atributul NEW. Prin intermediul clauzei optionale REFERENCING din sintaxa comenzii de
creare a declansatorilor, atributele NEW si OLD pot fi redenumite.
Un declansator poate activa alt declansator, iar acesta la rndul sau poate activa alt declansator etc.
Aceasta situatie (declansatori n cascada) poate avea nsa efecte imprevizibile. Sistemul Oracle permite maximum
32 declansatori n cascada. Numarul acestora poate fi limitat (utiliznd parametrul de initializare
OPEN_CURSORS), deoarece pentru fiecare executie a unui declansator trebuie deschis un nou cursor.
Declansatorii la nivel de baze de date pot fi de doua feluri:
la nivel de instructiune (statement level trigger);
la nivel de linie (row level trigger).
Declansatori la nivel de instructiune
Declansatorii la nivel instructiune sunt executati o singura data pentru instructiunea declansatoare,
indiferent de numarul de linii afectate (chiar daca nici o linie nu este afectata). Un declansator la nivel de
instructiune este util daca actiunea declansatorului nu depinde de informatiile din liniile afectate.
Exemplu:
Programul de lucru la administratia muzeului este de luni pna vineri, n intervalul (8:00 a.m. - 10:00
p.m.). Sa se construiasca un declansator la nivel de instructiune care mpiedica orice activitate asupra unui tabel
al bazei de date, n afara acestui program.


82

CREATE OR REPLACE PROCEDURE verifica IS
BEGIN
IF ((TO_CHAR(SYSDATE,'D') BETWEEN 2 AND 6)
AND
TO_DATE(TO_CHAR(SYSDATE,'hh24:mi'), 'hh24:mi')
NOT BETWEEN TO_DATE('08:00','hh24:mi')
AND TO_DATE('22:00','hh24:mi'))
THEN
RAISE_APPLICATION_ERROR (-27733, 'nu puteti reactualiza
acest tabel deoarece sunteti in afara programului');
END verifica;
/
CREATE OR REPLACE TRIGGER BIUD_tabel1
BEFORE INSERT OR UPDATE OR DELETE ON tabel1
BEGIN
verifica;
END;
/
Declansatori la nivel de linie
Declansatorii la nivel de linie sunt creati cu optiunea FOR EACH ROW. n acest caz, declansatorul este
executat pentru fiecare linie din tabelul afectat, iar daca evenimentul declansator nu afecteaza nici o linie, atunci
declansatorul nu este executat. Daca optiunea FOR EACH ROW nu este inclusa, declansatorul este considerat
implicit la nivel de instructiune.
Declansatorii la nivel linie nu sunt performanti daca se fac frecvent reactualizari pe tabele foarte mari.
Restrictiile declansatorilor pot fi incluse prin specificarea unei expresii booleene n clauza WHEN. Acesta
expresie este evaluata pentru fiecare linie afectata de catre declansator. Declansatorul este executat pentru o
linie, doar daca expresia este adevarata pentru acea linie. Clauza WHEN este valida doar pentru declansatori la
nivel de linie.
Exemplu:
Sa se implementeze cu ajutorul unui declansator constrngerea ca valorile operelor de arta nu pot fi
reduse (trei variante).
Varianta 1:
CREATE OR REPLACE TRIGGER verifica_valoare
BEFORE UPDATE OF valoare ON opera
FOR EACH ROW
WHEN (NEW.valoare < OLD.valoare)
BEGIN
RAISE_APPLICATION_ERROR (-20222, 'valoarea unei opere de
arta nu poate fi micsorata');
END;
Varianta 2:
CREATE OR REPLACE TRIGGER verifica_valoare
BEFORE UPDATE OF valoare ON opera
FOR EACH ROW
BEGIN
IF (:NEW.valoare < :OLD.valoare) THEN
RAISE_APPLICATION_ERROR (-20222, 'valoarea unei opere de
arta nu poate fi micsorata');
END IF;
END;
Varianta 3:


83

CREATE OR REPLACE TRIGGER verifica_valoare
BEFORE UPDATE OF valoare ON opera
FOR EACH ROW
WHEN (NEW.valoare < OLD.valoare)
CALL procedura -- care va face actiunea RAISE
/
Accesul la vechile si noile valori ale coloanelor liniei curente, afectata de evenimentul declansator, se face
prin: OLD.nume_coloana (vechea valoare), respectiv prin NEW.nume_coloana (noua valoare). n cazul celor
trei comenzi LMD, aceste valori devin:
INSERT : NEW.nume_coloana noua valoare
(: OLD.nume_coloana NULL );
UPDATE : NEW.nume_coloana noua valoare
: OLD.nume_coloana vechea valoare;
DELETE (: NEW.nume_coloana NULL )
: OLD.nume_coloana vechea valoare.
Exemplu:
Se presupune ca pentru fiecare galerie exista doua cmpuri (min_valoare si max_valoare) n care se retin
limitele minime si maxime ale valorile operelor din galeria respectiva. Sa se implementeze cu ajutorul unui
declansator constrngerea ca, daca aceste limite s-ar modifica, orice opera de arta trebuie sa aiba valoarea
cuprinsa ntre noile limite.
CREATE OR REPLACE TRIGGER verifica_limite
BEFORE UPDATE OF min_valoare, max_valoare ON galerie
FOR EACH ROW
DECLARE
v_min_val opera.valoare%TYPE;
v_max_val opera.valoare%TYPE;
e_invalid EXCEPTION;
BEGIN
SELECT MIN(valoare), MAX(valoare)
INTO v_min_val, v_max_val
FROM opera
WHERE cod_galerie = :NEW.cod_galerie;
IF (v_min_val < :NEW.min_valoare) OR
(v_max_val > :NEW.max_valoare) THEN
RAISE e_invalid;
END IF;
EXCEPTION
WHEN e_invalid THEN
RAISE_APPLICATION_ERROR (-20567, 'Exista opere de arta
ale caror valori sunt in afara domeniului permis');
END verifica_limite;
/
Ordinea de executie a declansatorilor
PL/SQL permite definirea a 12 tipuri de declansatori care sunt obtinuti prin combinarea proprietatii de
moment (timp) al declansarii (BEFORE, AFTER), cu proprietatea nivelului la care actioneaza (nivel linie, nivel
intructiune) si cu tipul operatiei atasate declansatorului (INSERT, UPDATE, DELETE).
De exemplu, BEFORE INSERT actioneaza o singura data, naintea executarii unei instructiuni INSERT,
iar BEFORE INSERT FOR EACH ROW actioneaza nainte de inserarea fiecarei noi nregistrari.
Declansatorii sunt activati cnd este executata o comanda LMD. La aparitia unei astfel de comenzi se
executa cteva actiuni care vor fi descrise n continuare.


84

1) Se executa declansatorii la nivel de instructiune BEFORE.
2) Pentru fiecare linie afectata de comanda LMD:
2.1) se executa declansatorii la nivel de linie BEFORE;
2.2) se blocheaza si se modifica linia afectata (se executa comanda LMD), se verifica constrngerile
de integritate (blocarea ramne valabila pna n momentul n care tranzactia este
permanentizata);
2.3) se executa declansatorii la nivel de linie AFTER.
3) Se executa declansatorii la nivel de instructiune AFTER.
ncepnd cu versiunea Oracle8i algoritmul anterior se schimba, n sensul ca verificarea constrngerii
referentiale este amnata dupa executarea declansatorului la nivel linie.
Obsevatii:
n expresia clauzei WHEN nu pot fi incluse functii definite de utilizator sau subcereri SQL.
n clauza ON poate fi specificat un singur tabel sau o singura vizualizare.
n interiorul blocului PL/SQL, coloanele tabelului prefixate cu OLD sau NEW sunt considerate
variabile externe si deci, trebuie precedate de caracterul :.
Conditia de la clauza WHEN poate contine coloane prefixate cu OLD sau NEW, dar n acest caz,
acestea nu trebuie precedate de :.
Declansatorii baza de date pot fi definiti numai pe tabele (exceptie, declansatorul INSTEAD OF care
este definit pe o vizualizare). Totusi, daca o comanda LMD este aplicata unei vizualizari, pot fi
activati declansatorii asociati tabelelor care definesc vizualizarea.
Corpul unui declansator nu poate contine o interogare sau o reactualizare a unui tabel aflat n plin
proces de modificare, pe timpul actiunii declansatorului (mutating table).
Blocul PL/SQL care descrie actiunea declansatorului nu poate contine comenzi pentru gestiunea
tranzactiilor (COMMIT, ROLLBACK, SAVEPOINT). Controlul tranzactiilor este permis, nsa, n
procedurile stocate. Daca un declansator apeleaza o procedura stocata care executa o comanda
referitoare la controlul tranzactiilor, atunci va aparea o eroare la executie si tranzactia va fi anulata.
Comenzile LDD nu pot sa apara dect n declansatorii sistem.
Corpul declansatorului poate sa contina comenzi LMD.
n corpul declansatorului pot fi referite si utilizate coloane LOB, dar nu pot fi modificate valorile
acestora.
n corpul declansatorului se pot insera date n coloanele de tip LONG si LONGRAW, dar nu pot fi
declarate variabile de acest tip.
Daca un tabel este suprimat (se sterge din dictionarul datelor), automat sunt distrusi toti declansatorii
asociati tabelului.
Nu este indicata crearea declansatorilor recursivi.
Este necesara limitarea dimensiunii unui declansator. Daca acesta solicita mai mult de 60 linii de cod,
atunci este preferabil ca o parte din cod sa fie inclusa ntr-o procedura stocata si aceasta sa fie apelata
din corpul declansatorului.
Sunt doua diferente esentiale ntre declansatori si procedurile stocate:
declansatorii se invoca implicit, iar procedurile explicit;
instructiunile LCD (COMMIT, ROLLBACK, SAVEPOINT) nu sunt permise n corpul unui
declansator.
Predicate conditionale
n interiorul unui declansator care poate fi executat pentru diferite tipuri de instructiuni LMD se pot
folosi trei functii booleene prin care se stabileste tipul operatiei executate. Aceste predicate conditionale
(furnizate de pachetul standard DBMS_STANDARD) sunt INSERTING, UPDATING si DELETING.


85

Functiile booleene nu solicita prefixarea cu numele pachetului si determina tipul operatiei (INSERT,
DELETE, UPDATE). De exemplu, predicatul INSERTING ia valoarea TRUE daca instructiunea declansatoare
este INSERT. Similar sunt definite predicatele UPDATING si DELETING. Utiliznd aceste predicate, n corpul
declansatorului se pot executa secvente de instructiuni diferite, n functie de tipul operatiei LMD.
n cazul n care corpul declansatorului este un bloc PL/SQL complet (nu o comanda CALL), pot fi
utilizate att predicatele INSERTING, UPDATING, DELETING, ct si identificatorii :OLD, :NEW, :PARENT.
Exemplu:
Se presupune ca n tabelul galerie se pastreaza (ntr-o coloana numita total_val) valoarea totala a
operelor de arta expuse n galeria respectiva.
UPDATE galerie
SET total_val =
(SELECT SUM(valoare)
FROM opera
WHERE opera.cod_galerie = galerie.cod_galerie);
Reactualizarea acestui cmp poate fi implementata cu ajutorul unui declansator n urmatoarea maniera:
CREATE OR REPLACE PROCEDURE creste
(v_cod_galerie IN galerie.cod_galerie%TYPE,
v_val IN galerie.total_val%TYPE) AS
BEGIN
UPDATE galerie
SET total_val = NVL (total_val, 0) + v_val
WHERE cod_galerie = v_cod_galerie;
END creste;
/
CREATE OR REPLACE TRIGGER calcul_val
AFTER INSERT OR DELETE OR UPDATE OF valoare ON opera
FOR EACH ROW
BEGIN
IF DELETING THEN
creste (:OLD.cod_galerie, -1*:OLD.valoare);
ELSIF UPDATING THEN
creste (:NEW.cod_galerie, :NEW.valoare - :OLD.valoare);
ELSE /* inserting */
creste (:NEW.cod_galerie, :NEW.valoare);
END IF;
END;
/
Declansatori I NSTEAD OF
PL/SQL permite definirea unui nou tip de declansator, numit INSTEAD OF, care ofera o modalitate de
actualizare a vizualizarilor obiect si a celor relationale.
Sintaxa acestui tip de declansator este similara celei pentru declansatori LMD, cu doua exceptii:
clauza {BEFORE | AFTER} este nlocuita prin INSTEAD OF;
clauza ON [schema.]nume_tabel este nlocuita printr-una din clauzele ON [schema.]nume_view sau
ON NESTED TABLE (nume_coloana) OF [schema.]nume_view.
Declansatorul INSTEAD OF permite reactualizarea unei vizualizari prin comenzi LMD. O astfel de
modificare nu poate fi realizata n alta maniera, din cauza regulilor stricte existente pentru reactualizarea
vizualizarilor. Declansatorii de tip INSTEAD OF sunt necesari, deoarece vizualizarea pe care este definit
declansatorul poate, de exemplu, sa se refere la join-ul unor tabele, si n acest caz, nu sunt actualizabile toate


86

legaturile.
O vizualizare nu poate fi modificata prin comenzi LMD daca vizualizarea contine operatori pe multimi,
functii grup, clauzele GROUP BY, CONNECT BY, START WITH, operatorul DISTINCT sau join-uri.
Declansatorul INSTEAD OF este utilizat pentru a executa operatii LMD direct pe tabelele de baza ale
vizualizarii. De fapt, se scriu comenzi LMD relative la o vizualizare, iar declansatorul, n locul operatiei originale,
va opera pe tabelele de baza.
De asemenea, acest tip de declansator poate fi definit asupra vizualizarilor ce au drept cmpuri tablouri
imbricate, declansatorul furniznd o modalitate de reactualizare a elementelor tabloului imbricat. n acest caz, el
se declanseaza doar n cazul n care comenzile LMD opereaza asupra tabloului imbricat (numai cnd elementele
tabloului imbricat sunt modificate folosind clauzele THE() sau TABLE()) si nu atunci cnd comanda LMD
opereaza doar asupra vizualizarii. Declansatorul permite accesarea liniei parinte ce contine tabloul imbricat
modificat.
Observatii:
Spre deosebire de declansatorii BEFORE sau AFTER, declansatorii INSTEAD OF se executa n locul
instructiunii LMD (INSERT, UPDATE, DELETE) specificate.
Optiunea UPDATE OF nu este permisa pentru acest tip de declansator.
Declansatorii INSTEAD OF se definesc pentru o vizualizare, nu pentru un tabel.
Declansatorii INSTEAD OF actioneaza implicit la nivel de linie.
Daca declansatorul este definit pentru tablouri imbricate, atributele :OLD si :NEW se refera la liniile
tabloului imbricat, iar pentru a referi linia curenta din tabloul parinte s-a introdus atributul
:PARENT.
Exemplu:
Se considera nou_opera, respectiv nou_artist, copii ale tabelelor opera, respectiv artist si vi_op_ar o
vizualizare definita prin compunerea naturala a celor doua tabele. Se presupune ca pentru fiecare artist exista un
cmp (sum_val) ce reprezinta valoarea totala a operelor de arta expuse de acesta n muzeu.
Sa se defineasca un declansator prin care reactualizarile executate asupra vizualizarii vi_op_ar se vor
transmite automat tabelelor nou_opera si nou_artist.
CREATE TABLE nou_opera AS
SELECT cod_opera, cod_artist, valoare, tip
FROM opera;
CREATE TABLE nou_artist AS
SELECT cod_artist, nume, sum_val
FROM artist;
CREATE VIEW vi_op_ar AS
SELECT cod_opera, o.cod_artist, valoare, tip, nume, sum_val
FROM opera o, artist a
WHERE o.cod_artist = a.cod_artist
CREATE OR REPLACE TRIGGER react
INSTEAD OF INSERT OR DELETE OR UPDATE ON vi_op_ar
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO nou_opera
VALUES (:NEW.cod_opera, :NEW.cod_artist, :NEW.valoare,
:NEW.tip);
UPDATE nou_artist
SET sum_val = sum_val + :NEW.valoare
WHERE cod_artist = :NEW.cod_artist;
ELSIF DELETING THEN


87

DELETE FROM nou_opera
WHERE cod_opera = :OLD.cod_opera;
UPDATE nou_artist
SET sum_val = sum_val - :OLD.valoare
WHERE cod_artist = :OLD.cod_artist;
ELSIF UPDATING ('valoare') THEN
UPDATE nou_opera
SET valoare = :NEW.valoare
WHERE cod_opera = :OLD.cod_opera;
UPDATE nou_artist
SET sum_val = sum_val + (:NEW.valoare - :OLD.valoare)
WHERE cod_artist = :OLD.cod_artist;
ELSIF UPDATING ('cod_artist') THEN
UPDATE nou_opera
SET cod_artist = :NEW.cod_artist
WHERE cod_opera = :OLD.cod_opera;
UPDATE nou_artist
SET sum_val = sum_val - :OLD.valoare
WHERE cod_artist = :OLD.cod_artist;
UPDATE nou_artist
SET sum_val = sum_val + :NEW.valoare
WHERE cod_artist = :NEW.cod_artist;
END IF;
END;
/
Declansatori sistem
Declansatorii sistem sunt activati de comenzi LDD (CREATE, DROP, ALTER) si de anumite evenimente
sistem (STARTUP, SHUTDOWN, LOGON, LOGOFF, SERVERERROR, SUSPEND). Un declansator sistem
poate fi definit la nivelul bazei de date sau la nivelul schemei.
Sintaxa pentru crearea unui astfel de declansator este urmatoarea:
CREATE [OR REPLACE] TRI GGER [schema.]nume_declansator
{BEFORE | AFTER}
{lista_evenimente_LDD | lista_evenimente_baza}
ON {DATABASE | SCHEMA}
[WHEN (conditie) ]
corp_declansator;
Cuvintele cheie DATABASE sau SCHEMA specifica nivelul declansatorului.
Exista restrictii asupra expresiilor din conditia clauzei WHEN. De exemplu, declansatorii LOGON si LOGOFF
pot verifica doar identificatorul (userid) si numele utilizatorului (username), iar declansatorii LDD pot verifica
tipul si numele obiectelor definite, identificatorul si numele utilizatorului.
Evenimentele amintite anterior pot fi asociate clauzelor BEFORE sau AFTER. De exemplu, un
declansator LOGON (AFTER) se activeaza dupa ce un utilizator s-a conectat la baza de date, un declansator
CREATE (BEFORE sau AFTER) se activeaza nainte sau dupa ce a fost creat un obiect al bazei, un declansator
SERVERERROR (AFTER) se activeaza ori de cte ori apare o eroare (cu exceptia erorilor: ORA-01403, ORA-
01422, ORA-01423, ORA-01034, ORA-04030).
Declansatorii LDD se activeaza numai daca obiectul creat este de tip table, cluster, function, index,
package, role, sequence, synonym, tablespace, trigger, type, view sau user.
Pentru declansatorii sistem se pot utiliza functii speciale care permit obtinerea de informatii referitoare la
evenimentul declansator. Ele sunt functii PL/SQL stocate care trebuie prefixate de numele proprietarului (SYS).
Printre cele mai importante functii care furnizeaza informatii referitoare la evenimentul declansator, se
remarca:


88

SYSEVENT returneaza evenimentul sistem care a activat declansatorul (este de tip VARCHAR2(20)
si este aplicabila oricarui eveniment);
DATABASE_NAME returneaza numele bazei de date curente (este de tip VARCHAR2(50) si este
aplicabila oricarui eveniment);
SERVER_ERROR returneaza codul erorii a carei pozitie n stiva erorilor este data de argumentul de
tip NUMBER al functiei (este de tip NUMBER si este aplicabila evenimentului SERVERERROR);
LOGIN_USER returneaza identificatorul utilizatorului care activeaza declansatorul (este de tip
VARCHAR2(30) si este aplicabila oricarui eveniment);
DICTIONARY_OBJ_NAME returneaza numele obiectului la care face referinta comanda LDD ce a
activat declansatorul (este de tip VARCHAR2(30) si este aplicabila evenimentelor CREATE, ALTER,
DROP).
Exemplu:
CREATE OR REPLACE TRIGGER logutiliz
AFTER CREATE ON SCHEMA
BEGIN
INSERT INTO ldd_tab(user_id, object_name, creation_date)
VALUES (USER, SYS.DICTIONARY_OBJ_NAME, SYSDATE);
END logutiliz;
Evenimentul SERVERERROR poate fi utilizat pentru a urmari erorile care apar n baza de date. Codul
erorii este furnizat, prin intermediul declansatorului, de functia SERVER_ERROR, iar mesajul asociat erorii poate
fi obtinut cu procedura DBMS_UTILITY.FORMAT_ERROR_STACK.
Exemplu:
CREATE TABLE erori (
moment DATE,
utilizator VARCHAR2(30),
nume_baza VARCHAR2(50),
stiva_erori VARCHAR2(2000) );
/
CREATE OR REPLACE TRIGGER logerori
AFTER SERVERERROR ON DATABASE
BEGIN
INSERT INTO erori
VALUES (SYSDATE, SYS.LOGIN_USER, SYS.DATABASE_NAME,
DBMS_UTILITY.FORMAT_ERROR_STACK);
END logerori;
/
Modificarea si suprimarea declansatorilor
Optiunea OR REPLACE din cadrul comenzii CREATE TRIGGER recreeaza declansatorul, daca acesta
exista. Clauza permite schimbarea definitiei unui declansator existent fara suprimarea acestuia.
Similar procedurilor si pachetelor, un declansator poate fi suprimat prin:
DROP TRI GGER [schema.]nume_declansator;
Uneori actiunea de suprimare a unui declansator este prea drastica si este preferabila doar dezactivarea sa
temporara. n acest caz, declansatorul va continua sa existe n dictionarul datelor. Modificarea unui declansator
poate consta din recompilarea (COMPILE), redenumirea (RENAME), activarea (ENABLE) sau dezactivarea
(DISABLE) acestuia si se realizeaza prin comanda:
ALTER TRI GGER [schema.]nume declansator


89

{ENABLE | DI SABLE | COMPI LE | RENAME TO nume_nou}
{ALL TRI GGERS}
Daca un declansator este activat, atunci sistemul Oracle l executa ori de cte ori au loc operatiile
precizate n declansator asupra tabelului asociat si cnd conditia de restrictie este ndeplinita. Daca declansatorul
este dezactivat, atunci sistemul Oracle nu l va mai executa. Dupa cum s-a mai subliniat, dezactivarea unui
declansator nu implica stergerea acestuia din dictionarul datelor.
Toti declansatorii asociati unui tabel pot fi activati sau dezactivati utiliznd optiunea ALL TRIGGERS
(ENABLE ALL TRIGGERS, respectiv DISABLE ALL TRIGGERS). Declansatorii sunt activati n mod implicit
atunci cnd sunt creati.
Activarea si dezactivarea declansatorilor asociati unui tabel se poate realiza si cu ajutorul comenzii
ALTER TABLE.
Un declansator este compilat n mod automat la creare. Daca un site este neutilizabil atunci cnd
declansatorul trebuie compilat, sistemul Oracle nu poate valida comanda de accesare a bazei distante si
compilarea esueaza.
Informatii despre declansatori
n dictionarul datelor exista vizualizari ce contin informatii despre declansatori si despre starea acestora
(USER_TRIGGERS, USER_TRIGGER_COL, ALL_TRIGGERS, DBA_TRIGGERS etc.). Aceste vizualizari sunt
actualizate ori de cte ori un declansator este creat sau suprimat.
Atunci cnd declansatorul este creat, codul sau sursa este stocat n vizualizarea USER_TRIGGERS.
Vizualizarea ALL_TRIGGERS contine informatii despre toti declansatorii din baza de date. Pentru a detecta
dependentele declansatorilor poate fi consultata vizualizarea USER_DEPENDENCIES, iar
ALL_DEPENDECIES contine informatii despre dependentele tuturor obiectelor din baza de date. Erorile
rezultate din compilarea declansatorilor pot fi analizate din vizualizarea USER_ERRORS, iar prin comanda
SHOW ERRORS se vor afisa erorile corespunzatoare ultimului declansator compilat.
n operatiile de gestiune a bazei de date este necesara uneori reconstruirea instructiunilor CREATE
TRIGGER, atunci cnd codul sursa original nu mai este disponibil. Aceasta se poate realiza utiliznd vizualizarea
USER_TRIGGERS. Vizualizarea include numele declansatorului (TRIGGER_NAME), tipul acestuia
(TRIGGER_TYPE), evenimentul declansator (TRIGGERING_EVENT), numele proprietarului tabelului
(TABLE_OWNER), numele tabelului pe care este definit declansatorul (TABLE_NAME), clauza WHEN
(WHEN_CLAUSE), corpul declansatorului (TRIGGER_BODY), antetul (DESCRIPTION), starea acestuia
(STATUS) care poate sa fie ENABLED sau DISABLED si numele utilizate pentru a referi parametrii OLD si
NEW (REFERENCING_NAMES). Daca obiectul de baza nu este un tabel sau o vizualizare, atunci
TABLE_NAME este null.
Exemplu:
Presupunnd ca nu este disponibil codul sursa pentru declansatorul alfa, sa se reconstruiasca
instructiunea CREATE TRIGGER corespunzatoare acestuia.
SELECT CREATE OR REPLACE TRIGGER || DESCRIPTION ||
TRIGGER_BODY
FROM USER_TRIGGERS
WHERE TRIGGER_NAME = 'ALFA';
Cu aceasta interogare se pot reconstrui numai declansatorii care apartin contului utilizator curent. O
interogare a vizualizarilor ALL_TRIGGERS sau DBA_TRIGGERS permite reconstruirea tuturor declansatorilor
din sistem, daca se dispune de privilegii DBA.
Privilegii sistem
Sistemul furnizeaza urmatoarele privilegii sistem pentru gestiunea declansatorilor:
CREATE TRIGGER (permite crearea declansatorilor n schema personala);


90

CREATE ANY TRIGGER (permite crearea declansatorilor n orice schema cu exceptia celei
corespunzatoare lui SYS);
ALTER ANY TRIGER (permite activarea, dezactivarea sau compilarea declansatorilor n orice schema
cu exceptia lui SYS);
DROP ANY TRIGGER (permite suprimarea declansatorilor la nivel de baza de date n orice schema
cu exceptia celei corespunzatoate lui SYS);
ADMINISTER DATABASE TRIGGER (permite crearea sau modificarea unui declansator sistem
referitor la baza de date);
EXECUTE (permite referirea, n corpul declansatorului, a procedurilor, functiilor sau pachetelor din
alte scheme).
Tabele mutating
Asupra tabelelor si coloanelor care pot fi accesate de corpul declansatorului exista anumite restrictii.
Pentru a analiza aceste restrictii este necesara definirea tabelelor n schimbare (mutating) si constrnse
(constraining).
Un tabel constraining este un tabel pe care evenimentul declansator trebuie sa-l consulte fie direct, printr-
o instructiune SQL, fie indirect, printr-o constrngere de integritate referentiala declarata. Tabelele nu sunt
considerate constraining n cazul declansatorilor la nivel de instructiune.
Un tabel mutating este tabelul modificat de instructiunea UPDATE, DELETE sau INSERT, sau un tabel
care va fi actualizat prin efectele actiunii integritatii referentiale ON DELETE CASCADE. Chiar tabelul pe care
este definit declansatorul este un tabel mutating, ca si orice tabel referit printr-o constrngere FOREING KEY.
Tabelele nu sunt considerate mutating pentru declansatorii la nivel de instructiune, iar vizualizarile nu sunt
considerate mutating n declansatorii INSTEAD OF.
Principalele reguli care trebuie respectate la utilizarea declansatoriilor sunt:
comenzile SQL din corpul unui declansator nu pot consulta sau modifica valorile coloanelor care sunt
declarate chei primare, externe sau unice (PRIMARY KEY, FOREIGN KEY, UNIQUE KEY ) ntr-un
tabel constraining;
comenzile SQL din corpul unui declansator nu pot consulta sau modifica date dintr-un tabel mutating.
Daca o comanda INSERT afecteaza numai o nregistrare, declansatorii la nivel de linie (BEFORE sau
AFTER) pentru nregistrarea respectiva nu trateaza tabelul ca fiind mutating. Acesta este unicul caz n care un
declansator la nivel de linie poate citi sau modifica tabelul. Comanda INSERT INTO tabel SELECT considera
tabelul mutating chiar daca cererea returneaza o singura linie.
Exemplu:
CREATE OR REPLACE TRIGGER cascada
AFTER UPDATE OF cod_artist ON artist
FOR EACH ROW
BEGIN
UPDATE opera
SET opera.cod_artist = :NEW.cod_artist
WHERE opera.cod_artist = :OLD.cod_artist
END;
/
UPDATE artist
SET cod_artist = 71
WHERE cod_artist = 23;
La executia acestei secvente este semnalata o eroare. Tabelul artist referentiaza tabelul opera printr-o
constrngere de cheie externa. Prin urmare, tabelul opera este constraining, iar declansatorul cascada ncearca
sa schimbe date n tabelul constraining, ceea ce nu este permis. Exemplul va functiona corect daca nu este
definita sau activata constrngerea referentiala ntre cele doua tabele.


91

Exemplu:
Sa se implementeze cu ajutorul unui declansator restrictia ca ntr-o sala pot sa fie expuse maximum 10
opere de arta.
CREATE OR REPLACE TRIGGER TrLimitaopere
BEFORE INSERT ON opera
FOR EACH ROW
DECLARE
v_Max_opere CONSTANT NUMBER := 10;
v_opere_curente NUMBER;
BEGIN
SELECT COUNT(*) INTO v_opere_curente
FROM opera
WHERE cod_sala = :NEW.cod_sala;
IF v_opere_curente + 1 > v_Max_opere THEN
RAISE_APPLICATION_ERROR(-20000,'Prea multe opere de arta
in sala avand codul ' || :NEW.cod_sala);
END IF;
END TrLimitaopere;
Cu toate ca declansatorul pare sa produca lucrul dorit, totusi dupa o reactualizare a tabelului opera n
urmatoarea maniera:
INSERT INTO opera (cod_opera, cod_sala)
VALUES (756893, 10);
se obtine urmatorul mesaj de eroare:
ORA-04091: tabel opera is mutating, trigger/function may not see it
ORA-04088: error during execution of trigger TrLimitaopere
Eroarea ORA-04091 apare deorece declansatorul TrLimitaopere consulta chiar tabelul (opera) la care
este asociat declansatorul (mutating).
Tabelul opera este mutating doar pentru un declansator la nivel de linie. Aceasta nseamna ca tabelul
poate fi consultat n interiorul unui declansator la nivel de instructiune. Totusi, limitarea numarului operelor de
arta nu poate fi facuta n interiorul unui declansator la nivel de instructiune, din moment ce este necesara
valoarea :NEW.cod_sala n corpul declansatorului.
Solutia pentru acesta problema este crearea a doi declansatori, unul la nivel de linie si altul la nivel de
instructiune. n declansatorul la nivel de linie se nregistreaza valoarea lui :NEW.cod_sala, dar nu va fi interogat
tabelul opera.
Interogarea va fi facuta n declansatorul la nivel de instructiune si va folosi valoarea nregistrata n
declansatorul la nivel de linie.
O modalitate pentru a nregistra valoarea lui :NEW.cod_sala este utilizarea unui tablou indexat n
interiorul unui pachet.
CREATE OR REPLACE PACKAGE PSalaDate AS
TYPE t_cod IS TABLE OF sala.cod_sala%TYPE
INDEX BY BINARY_INTEGER;
v_cod_sala t_cod;
v_NrIntrari BINARY_INTEGER := 0;
END PSalaDate;
CREATE OR REPLACE TRIGGER TrLLimitaOpere
BEFORE INSERT ON sala
FOR EACH ROW
BEGIN
PSalaDate.v_NrIntrari := PSalaDate.v_NrIntrari + 1;
PSalaDate.v_cod_sala (PSalaDate.v_NrIntrari) :=
:NEW.cod_sala;


92

END TrLLimitaTOpere;
CREATE OR REPLACE TRIGGER TrILimitaopere
BEFORE INSERT ON opera
DECLARE
v_Max_opere CONSTANT NUMBER := 10;
v_opere_curente NUMBER;
v_cod_sala sala.cod_sala%TYPE;
BEGIN
/* Parcurge fiecare opera inserata sau actualizata si
verifica daca se incadreaza in limita stabilita */
FOR v_LoopIndex IN 1..PsalaDate.v_NrIntrari LOOP
v_cod_sala := PsalaDate.v_cod_sala(v_LoopIndex);
SELECT COUNT(*)
INTO v_opere_curente
FROM opera
WHERE cod_sala = v_cod_sala;
IF v_opere_curente > v_Max_opere THEN
RAISE_APPLICATION_ERROR(-20000, 'Prea multe opere de
arta in sala avand codul: ' || v_cod_sala);
END IF;
END LOOP;
/* Reseteaza contorul deoarece urmatoarea executie
va folosi date noi */
PSalaDate.v_NrIntrari := 0;
END TrILimitaopere;
Exemplu:
Sa se creeze un declansator care:
a) daca este eliminata o sala, va sterge toate operele expuse n sala respectiva;
b) daca se schimba codul unei sali, va modifica aceasta valoare pentru fiecare opera de arta expusa n
sala respectiva.
CREATE OR REPLACE TRIGGER sala_cascada
BEFORE DELETE OR UPDATE OF cod_sala ON sala
FOR EACH ROW
BEGIN
IF DELETING THEN
DELETE FROM opera
WHERE cod_sala = :OLD.cod_sala;
END IF;
IF UPDATING AND :OLD.cod_sala != :NEW.cod_sala THEN
UPDATE opera
SET cod_sala = :NEW.cod_sala
WHERE cod_sala = :OLD.cod_sala;
END IF;
END sala_cascada;
Declansatorul anterior realizeaza constrngerea de integritate UPDATE sau ON DELETE CASCADE,
adica stergerea sau modificarea cheii primare a unui tabel parinte se va reflecta si asupra nregistrarilor
corespunzatoare din tabelul copil.
Executarea acestuia, pe tabelul sala (tabelul parinte), va duce la efectuarea a doua tipuri de operatii pe
tabelul opera (tabelul copil).
La eliminarea unei sali din tabelul sala, se vor sterge toate operele de arta corespunzatoare acestei sali.
DELETE FROM sala
WHERE cod_sala = 773;
La modificarea codului unei sali din tabelul sala, se va actualiza codul salii att n tabelul sala, ct si n


93

tabelul opera.
UPDATE sala
SET cod_sala = 777
WHERE cod_sala = 333;
Se presupune ca asupra tabelului opera exista o constrngere de integritate:
FOREIGN KEY (cod_sala) REFERENCES sala(cod_sala)
n acest caz sistemul Oracle va afisa un mesaj de eroare prin care se precizeaza ca tabelul sala este
mutating, iar constrngerea definita mai sus nu poate fi verificata.
ORA-04091: table MASTER.SALA is mutating, trigger/function may not see it
Pachetele pot fi folosite pentru ncapsularea detaliilor logice legate de declansatori. Exemplul urmator
arata un mod simplu de implementare a acestei posibilitati. Este permisa apelarea unei proceduri sau functii
stocate din blocul PL/SQL care reprezinta corpul declansatorului.
Exemplu:
CREATE OR REPLACE PACKAGE pachet IS
PROCEDURE procesare_trigger(pvaloare IN NUMBER,
pstare IN VARCHAR2);
END pachet;
CREATE OR REPLACE PACKAGE BODY pachet IS
PROCEDURE procesare_trigger(pvaloare IN NUMBER,
pstare IN VARCHAR2) IS
BEGIN

END procesare_trigger;
END pachet;
CREATE OR REPLACE TRIGGER gama
AFTER INSERT ON opera
FOR EACH ROW
BEGIN
pachet.procesare_trigger(:NEW.valoare,:NEW.stare)
END;



10. Tratarea erorilor n PL/SQL


Mecanismul de gestiune a erorilor permite utilizatorului sa defineasca si sa controleze comportamentul
programului atunci cnd acesta genereaza o eroare. n acest fel, aplicatia nu este oprita, revenind ntr-un regim
normal de executie.
ntr-un program PL/SQL pot sa apara erori la compilare sau erori la executie. Erorile care apar n timpul
compilarii sunt detectate de motorul PL/SQL si sunt comunicate programatorului care va face corectia acestora.
Programul nu poate trata aceste erori deoarece nu a fost nca executat. Erorile care apar n timpul executiei nu
mai sunt tratate interactiv. n program trebuie prevazuta aparitia unei astfel de erori si specificat modul concret
de tratare a acesteia. Atunci cnd apare eroarea este declansata o exceptie, iar controlul trece la o sectiune
separata a programului, unde va avea loc tratarea erorii.
Gestiunea erorilor n PL/SQL face referire la conceptul de exceptie. Exceptia este un eveniment particular
(eroare sau avertisment) generat de server-ul Oracle sau de aplicatie, care necesita o tratare speciala. n PL/SQL
mecanismul de tratare a exceptiilor permite programului sa si continue executia si n prezenta anumitor erori.
Exceptiile pot fi definite, activate, tratate la nivelul fiecarui bloc din program (program principal, functii si
proceduri, blocuri interioare acestora). Executia unui bloc se termina ntotdeauna atunci cnd apare o exceptie,


94

dar se pot executa actiuni ulterioare aparitiei acesteia, ntr-o sectiune speciala de tratare a exceptiilor.
Posibilitatea de a da nume fiecarei exceptii, de a izola tratarea erorilor ntr-o sectiune particulara, de a
declansa automat erori (n cazul exceptiilor interne) mbunatateste lizibilitatea si fiabilitatea programului. Prin
utilizarea exceptiilor si rutinelor de tratare a exceptiilor, un program PL/SQL devine robust si capabil sa trateze
att erorile asteptate, ct si cele neasteptate ce pot aparea n timpul executiei.
Sectiunea de tratare a erorilor
Pentru a gestiona exceptiile, utilizatorul trebuie sa scrie cteva comenzi care preiau controlul derularii
blocului PL/SQL. Aceste comenzi sunt situate n sectiunea de tratare a erorilor dintr-un bloc PL/SQL si sunt
cuprinse ntre cuvintele cheie EXCEPTION si END, conform urmatoarei sintaxe generale:
EXCEPTI ON
WHEN nume_exceptie1 [OR nume_exceptie2 ] THEN
secventa_de_instructiuni_1;
[WHEN nume_exceptie3 [OR nume_exceptie4 ] THEN
secventa_de_instructiuni_2;]

[WHEN OTHERS THEN
secventa_de_instructiuni_n;]
END;
De remarcat ca WHEN OTHERS trebuie sa fie ultima clauza si trebuie sa fie unica. Toate exceptiile care
nu au fost analizate vor fi tratate prin aceasta clauza. Evident, n practica nu se utilizeaza forma WHEN OTHERS
THEN NULL.
n PL/SQL exista doua tipuri de exceptii:
exceptii interne, care se produc atunci cnd un bloc PL/SQL nu respecta o regula Oracle sau
depaseste o limita a sistemului de operare;
exceptii externe definite de utilizator (user-defined error), care sunt declarate n sectiunea declarativa
a unui bloc, subprogram sau pachet si care sunt activate explicit n partea executabila a blocului
PL/SQL.
Exceptiile interne PL/SQL sunt de doua tipuri:
exceptii interne predefinite (predefined Oracle Server error);
exceptii interne nepredefinite (non-predefined Oracle Server error).
Functii pentru identificarea exceptiilor
Indiferent de tipul exceptiei, aceasta are asociate doua elemente:
un cod care o identifica;
un mesaj cu ajutorul caruia se poate interpreta exceptia respectiva.
Cu ajutorul functiilor SQLCODE si SQLERRM se pot obtine codul si mesajul asociate exceptiei
declansate. Lungimea maxima a mesajului este de 512 caractere.
De exemplu, pentru eroarea predefinita ZERO_DIVIDE, codul SQLCODE asociat este -1476, iar mesajul
corespunzator erorii, furnizat de SQLERRM, este divide by zero error.
Codul erorii este:
un numar negativ, n cazul unei erori sistem;
numarul +100, n cazul exceptiei NO_DATA_FOUND;
numarul 0, n cazul unei executii normale (fara exceptii);
numarul 1, n cazul unei exceptii definite de utilizator.
Functiile SQLCODE si SQLERRM nu se pot utiliza direct ca parte a unei instructiuni SQL. Valorile
acestora trebuie atribuite unor variabile locale. Rezultatul functiei SQLCODE poate fi asignat unei variabile de
tip numeric, iar cel al functiei SQLERRM unei variabile de tip caracter. Variabilele locale astfel definite pot fi


95

utilizate n comenzi SQL.
Exemplu:
Sa se scrie un bloc PL/SQL prin care sa se exemplifice situatia comentata.
DECLARE
eroare_cod NUMBER;
eroare_mesaj VARCHAR2(100);
BEGIN

EXCEPTION

WHEN OTHERS THEN
eroare_cod := SQLCODE;
eroare_mesaj := SUBSTR(SQLERRM,1,100);
INSERT INTO erori
VALUES (eroare_cod, eroare_mesaj);
END;
Mesajul asociat exceptiei declansate poate fi furnizat si de functia
DBMS_UTILITY.FORMAT_ERROR_STACK.
Exceptii interne
Exceptiile interne se produc atunci cnd un bloc PL/SQL nu respecta o regula Oracle sau depaseste o
limita a sistemului de exploatare.
Aceste exceptii pot fi independente de structura bazei de date sau pot sa apara datorita nerespectarii
constrngerilor statice implementate n structura (PRIMARY KEY, FOREIGN KEY, NOT NULL, UNIQUE,
CHECK).
Atunci cnd apare o eroare Oracle, exceptia asociata ei se declanseaza implicit. De exemplu, daca apare
eroarea ORA-01403 (deoarece o comanda SELECT nu returneaza nici o linie), atunci implicit PL/SQL activeaza
exceptia NO_DATA_FOUND. Cu toate ca fiecare astfel de exceptie are asociat un cod specific, ele trebuie
referite prin nume.
Exceptii interne predefinite
Exceptiile interne predefinite nu trebuie declarate n sectiunea declarativa si sunt tratate implicit de catre
server-ul Oracle. Ele sunt referite prin nume (CURSOR_ALREADY_OPEN, DUP_VAL_ON_INDEX,
NO_DATA_FOUND etc.). PL/SQL declara aceste exceptii n pachetul DBMS_STANDARD.
Exceptii predefinite
Nume exceptie Cod eroare
Oracle
Descriere
ACCES_INTO_NULL ORA-06530 Asignare de valori atributelor unui obiect
neinitializat.
CASE_NOT_FOUND ORA-06592 Nu este selectata nici una din clauzele WHEN
ale lui CASE si nu exista nici clauza ELSE
(exceptie specifica lui Oracle9i).
COLLECTION_IS_NULL ORA-06531 Aplicarea unei metode (diferite de EXISTS)
unui tabel imbricat sau unui vector
neinitializat.
CURSOR_ALREADY_OPEN ORA-06511 Deschiderea unui cursor care este deja dechis.
DUP_VAL_ON_INDEX ORA-00001 Detectarea unei dubluri ntr-o coloana unde
acestea sunt interzise.
INVALID_CURSOR ORA-01001 Operatie ilegala asupra unui cursor.
INVALID_NUMBER ORA-01722 Conversie nepermisa de la tipul sir de
caractere la numar.


96

LOGIN_DENIED ORA-01017 Nume sau parola incorecte.
NO_DATA_FOUND ORA-01403 Comanda SELECT nu returneaza nici o
nregistrare.
NOT_LOGGED_ON ORA-01012 Programul PL/SQL apeleaza baza fara sa fie
conectat la Oracle.
SELF_IS_NULL ORA-30625 Apelul unei metode cnd instanta este NULL.
PROGRAM_ERROR ORA-06501 PL/SQL are o problema interna.
ROWTYPE_MISMATCH ORA-06504 Incompatibilitate ntre parametrii actuali si
formali, la deschiderea unui cursor
parametrizat.
STORAGE_ERROR ORA-06500 PL/SQL are probleme cu spatiul de memorie.
SUBSCRIPT_BEYOND_COUN
T
ORA-06533 Referire la o componenta a unui nested table
sau varray, folosind un index mai mare dect
numarul elementelor colectiei respective.
SUBSCRIPT_OUTSIDE_LIMIT ORA-06532 Referire la o componenta a unui tabel
imbricat sau vector, folosind un index care
este n afara domeniului (de exemplu, -1).
SYS_INVALID_ROWID ORA-01410 Conversia unui sir de caractere ntr-un
ROWID nu se poate face deoarece sirul nu
reprezinta un ROWID valid.
TIMEOUT_ON_RESOURCE ORA-00051 Expirarea timpului de asteptare pentru
eliberarea unei resurse.
TRANSACTION_BACKED_OU
T
ORA-00061 Tranzactia a fost anulata datorita unei
interblocari.
TOO_MANY_ROWS ORA-01422 SELECTINTO ntoarce mai multe linii.
VALUE_ERROR ORA-06502 Aparitia unor erori n conversii, constrngeri
sau erori aritmetice.
ZERO_DIVIDE ORA-01476 Sesizarea unei mpartiri la zero.
Exemplu:
Sa se scrie un bloc PL/SQL prin care sa se afiseze numele artistilor de o anumita nationalitate care au
opere de arta expuse n muzeu.
1) Daca rezultatul interogarii returneaza mai mult dect o linie, atunci sa se trateze exceptia si sa se
insereze n tabelul mesaje textul mai multi creatori.
2) Daca rezultatul interogarii nu returneaza nici o linie, atunci sa se trateze exceptia si sa se insereze n
tabelul mesaje textul nici un creator.
3) Daca rezultatul interogarii este o singura linie, atunci sa se insereze n tabelul mesaje numele artistului
si pseudonimul acestuia.
4) Sa se trateze orice alta eroare, insernd n tabelul mesaje textul alte erori au aparut.
SET VERIFY OFF
ACCEPT national PROMPT 'Introduceti nationalitatea:'
DECLARE
v_nume_artist artist.nume%TYPE;
v_pseudonim artist.pseudonim%TYPE;
v_national artist.national%TYPE:='&national';
BEGIN
SELECT nume, pseudonim
INTO v_nume_artist, v_pseudonim
FROM artist
WHERE national = v_national;
INSERT INTO mesaje (rezultate)
VALUES (v_nume_artist||'-'||v_pseudonim);
EXCEPTION
WHEN NO_DATA_FOUND THEN


97

INSERT INTO mesaje (rezultate)
VALUES ('nici un creator');
WHEN TOO_MANY_ROWS THEN
INSERT INTO mesaje (rezultate)
VALUES ('mai multi creatori');
WHEN OTHERS THEN
INSERT INTO mesaje (rezultate)
VALUES ('alte erori au aparut');
END;
/
SET VERIFY ON
Aceeasi exceptie poate sa apara n diferite circumstante. De exemplu, exceptia NO_DATA_FOUND
poate fi generata fie pentru ca o interogare nu ntoarce un rezultat, fie pentru ca se refera un element al unui
tablou PL/SQL care nu a fost definit (nu are atribuita o valoare). Daca ntr-un bloc PL/SQL apar ambele situatii,
este greu de stabilit care dintre ele a generat eroarea si este necesara restructurarea blocului, astfel nct acesta sa
poata diferentia cele doua situatii.
Exceptii interne nepredefinite
Exceptiile interne nepredefinite sunt declarate n sectiunea declarativa si sunt tratate implicit de catre
server-ul Oracle. Ele pot fi gestionate prin clauza OTHERS, n sectiunea EXCEPTION.
Diferentierea acestor erori este posibila doar cu ajutorul codului. Dupa cum s-a mai specificat, codul unei
exceptii interne este un numar negativ, n afara de exceptia NO_DATA_FOUND, care are codul +100.
O alta metoda pentru tratarea unei erori interne nepredefinite (diferita de folosirea clauzei OTHERS drept
detector universal de exceptii) este utilizarea directivei de compilare (pseudo-instructiune) PRAGMA
EXCEPTION_INIT. Aceasta directiva permite asocierea numelui unei exceptii cu un cod de eroare intern. n
felul acesta, orice exceptie interna poate fi referita printr-un nume si se pot scrie rutine speciale pentru tratarea
acesteia. Directiva este procesata n momentul compilarii, si nu la executie. Directiva trebuie sa apara n partea
declarativa a unui bloc, pachet sau subprogram, dupa definirea numelui exceptiei. PRAGMA EXCEPTION_INIT
poate sa apara de mai multe ori ntr-un program. De asemenea, pot fi asignate mai multe nume pentru acelasi cod
de eroare.
n acest caz, tratarea erorii se face n urmatoarea maniera:
1) se declara numele exceptiei n partea declarativa sub forma:
nume_exceptie EXCEPTI ON;
2) se asociaza numele exceptiei cu un cod eroare standard Oracle, utiliznd comanda:
PRAGMA EXCEPTI ON_I NI T (nume_exceptie, cod_eroare);
3) se refera exceptia n sectiunea de gestiune a erorilor (exceptia este tratata automat, fara a fi necesara
comanda RAISE).
Exemplu:
Daca exista opere de arta create de un anumit artist, sa se tipareasca un mesaj prin care utilizatorul este
anuntat ca artistul respectiv nu poate fi sters din baza de date (violarea constrngerii de integritate avnd codul
eroare Oracle -2292).
SET VERIFY OFF
DEFINE p_nume = Monet
DECLARE
opera_exista EXCEPTION;
PRAGMA EXCEPTION_INIT(opera_exista,-2292);
BEGIN
DELETE FROM artist WHERE nume = '&p_nume';
COMMIT;


98

EXCEPTION
WHEN opera_exista THEN
DBMS_OUTPUT.PUT_LINE ('nu puteti sterge artistul cu
numele ' || '&p_nume' || ' deoarece exista in muzeu
opere de arta create de acesta');
END;
/
SET VERIFY ON
Exceptii externe
PL/SQL permite utilizatorului sa defineasca propriile sale exceptii. Aceste exceptii pot sa apara n toate
sectiunile unui bloc, subprogram sau pachet. Exceptiile externe sunt definite n partea declarativa a blocului, deci
posibilitatea de referire la ele este asigurata. n mod implicit, toate exceptiile externe au asociat acelasi cod (+1)
si acelasi mesaj (USER DEFINED EXCEPTION).
Tratarea unei astfel de erori se face ntr-o maniera similara modului de tratare descris anterior. Activarea
exceptiei externe este facuta explicit, folosind comanda RAISE nsotita de numele exceptiei. Comanda opreste
executia normala a blocului PL/SQL si transfera controlul administratorului exceptiilor.
Declararea si prelucrarea exceptiilor externe respecta urmatoarea sintaxa:
DECLARE
nume_exceptie EXCEPTI ON; -- declarare exceptie
BEGI N

RAI SE nume_exceptie; --declansare exceptie
-- codul care urmeaza nu mai este executat

EXCEPTI ON
WHEN nume_exceptie THEN
-- definire mod de tratare a erorii

END;
Exceptiile trebuie privite ca niste variabile, n sensul ca ele sunt active n sectiunea n care sunt declarate.
Ele nu pot sa apara n instructiuni de atribuire sau n comenzi SQL.
Este recomandat ca fiecare subprogram sa aiba definita o zona de tratare a exceptiilor. Daca pe parcursul
executiei programului intervine o eroare, atunci acesta genereaza o exceptie si controlul se transfera blocului de
tratare a erorilor.
Exemplu:
Sa se scrie un bloc PL/SQL care afiseaza numarul creatorilor operelor de arta din muzeu care au valoarea
mai mare sau mai mica cu 100000$ dect o valoare specificata. Sa se tipareasca un mesaj adecvat, daca nu exista
nici un artist care ndeplineste aceasta conditie.
VARIABLE g_mesaj VARCHAR2(100)
SET VERIFY OFF
ACCEPT p_val PROMPT 'va rog specificati valoarea:'
DECLARE
v_val opera.valoare%TYPE := &p_val;
v_inf opera.valoare%TYPE := v_val - 100000;
v_sup opera.valoare%TYPE := v_val + 100000;
v_numar NUMBER(7);
e_nimeni EXCEPTION;
e_mai_mult EXCEPTION;


99

BEGIN
SELECT COUNT(DISTINCT cod_autor)
INTO v_numar
FROM opera
WHERE valoare BETWEEN v_inf AND v_sup;
IF v_numar = 0 THEN
RAISE e_nimeni;
ELSIF v_numar > 0 THEN
RAISE e_mai_mult;
END IF;
EXCEPTION
WHEN e_nimeni THEN
:g_mesaj:='nu exista nici un artist cu valoarea
operelor cuprinsa intre '||v_inf ||' si '||v_sup;
WHEN e_mai_mult THEN
:g_mesaj:='exista '||v_numar||' artisti cu valoarea
operelor cuprinsa intre '||v_inf||' si '||v_sup;
WHEN OTHERS THEN
:g_mesaj:='au aparut alte erori';
END;
/
SET VERIFY ON
PRINT g_mesaj
Activarea unei exceptii externe poate fi facuta si cu ajutorul procedurii RAISE_APPLICATION_ERROR,
furnizata de pachetul DBMS_STANDARD.
RAISE_APPLICATION_ERROR poate fi folosita pentru a returna un mesaj de eroare unitatii care o
apeleaza, mesaj mai descriptiv dect identificatorul erorii. Unitatea apelanta poate fi SQL*Plus, un subprogram
PL/SQL sau o aplicatie client.
Procedura are urmatorul antet:
RAI SE_APPLI CATI ON_ERROR (numar_eroare IN NUMBER,
mesaj_eroare IN VARCHAR2, [ {TRUE | FALSE} ] );
Atributul numar_eroare este un numar cuprins ntre 20000 si 20999, specificat de utilizator pentru
exceptia respectiva, iar mesaj_eroare este un text asociat erorii, care poate avea maximum 2048 octeti.
Parametrul boolean este optional. Daca acest parametru este TRUE, atunci noua eroare se va adauga
listei erorilor existente, iar daca este FALSE (valoare implicita) atunci noua eroare va nlocui lista curenta a
erorilor.
O aplicatie poate apela RAISE_APPLICATION_ERROR numai dintr-un subprogram stocat (sau
metoda). Daca procedura RAISE_APPLICATION_ERROR este apelata, atunci subprogramul se termina si sunt
returnate codul si mesajul asociate erorii respective.
Procedura RAISE_APPLICATION_ERROR poate fi folosita n sectiunea executabila, n sectiunea de
tratare a erorilor si chiar simultan n ambele sectiuni.
n sectiunea executabila:
DELETE FROM opera WHERE material = 'carton';
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20201,'info incorecta');
END IF;
n sectiunea de tratare a erorilor:
EXCEPTION
WHEN NO_DATA_FOUND THEN


100

RAISE_APPLICATION_ERROR(-20202,'info invalida');
END;
n ambele sectiuni:
DECLARE
e_material EXCEPTION;
PRAGMA EXCEPTION_INIT (e_material, -20777);
BEGIN

DELETE FROM opera WHERE valoare < 100001;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20777,
'nu exista opera cu aceasta valoare');
END IF;
EXCEPTION
WHEN e_material THEN
-- trateaza eroarea aceasta

END;
RAISE_APPLICATION_ERROR faciliteaza comunicatia dintre client si server, transmitnd aplicatiei
client erori specifice aplicatiei de pe server (de obicei, un declansator). Prin urmare, procedura este doar un
mecanism folosit pentru comunicatia server client a unei erori definite de utilizator, care permite ca procesul
client sa trateze exceptia.
Exemplu:
Sa se implementeze un declansator care nu permite acceptarea n muzeu a operelor de arta avnd
valoarea mai mica de 100000$.
CREATE OR REPLACE TRIGGER minim_valoare
BEFORE INSERT ON opera
FOR EACH ROW
BEGIN
IF :NEW.valoare < 100000 THEN
RAISE_APPLICATION_ERROR
(-20005,operele de arta trebuie sa aiba valoare
mai mare de 100000$);
END IF;
END;
Pe statia client poate fi scris un program care detecteaza si trateaza eroarea.
DECLARE
/* declarare exceptie */
nu_accepta EXCEPTION;
/* asociaza nume,codului eroare folosit in trigger */
PRAGMA EXCEPTION_INIT(nu_accepta,-20005);
BEGIN
/* incearca sa inserezi */
INSERT INTO opera ;
EXCEPTION
/* tratare exceptie */
WHEN nu_accepta THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
/* SQLERRM va returna mesaj din RAISE_APPLICATION_ERROR */
END;
Cazuri speciale n tratarea exceptiilor


101

Daca se declanseaza o exceptie ntr-un bloc simplu, atunci se face saltul la partea de tratare (handler) a
acesteia, iar dupa ce este terminata tratarea erorii se iese din bloc (instructiunea END).
Prin urmare, daca exceptia se propaga spre blocul care include blocul curent, restul actiunilor executabile
din subbloc sunt pierdute. Daca dupa o eroare se doreste totusi continuarea prelucrarii datelor, este suficient
ca instructiunea care a declansat exceptia sa fie inclusa ntr-un subbloc. Dupa ce subblocul a fost terminat, se
continua secventa de instructiuni din blocul principal.
Exemplu:
BEGIN
DELETE
SELECT --poate declansa exceptia A
--nu poate fi efectuat INSERT care urmeaza
INSERT INTO
EXCEPTION
WHEN A THEN
END;
Deficienta anterioara se poate rezolva incluznd ntr-un subbloc comanda SELECT care a declansat
exceptia.
BEGIN
DELETE
BEGIN
SELECT

EXCEPTION
WHEN A THEN
/* dupa ce se trateaza exceptia A, controlul este
transferat blocului de nivel superior, de fapt
comenzii INSERT */
END;
INSERT INTO

EXCEPTION

END;
Uneori este dificil de aflat care comanda SQL a determinat o anumita eroare, deoarece exista o singura
sectiune pentru tratarea erorilor unui bloc. Sunt sugerate doua solutii pentru rezolvarea acestei probleme.
1) Introducerea unui contor care sa identifice instructiunea SQL.
DECLARE
v_sel_cont NUMBER(2):=1;
BEGIN
SELECT
v_sel_cont:=2;
SELECT
v_sel_cont:=3;
SELECT
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO log_table(info)
VALUES ('comanda SELECT ' || TO_CHAR(v_sel_cont) ||
' nu gaseste date');
END;
2) Introducerea fiecarei instructiuni SQL ntr-un subbloc.


102

BEGIN
BEGIN
SELECT
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO log_table(info)
VALUES('SELECT 1 nu gaseste date');
END;
BEGIN
SELECT
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO log_table(info)
VALUES('SELECT 2 nu gaseste date');
END;

END;
Activarea exceptiilor
Pentru activarea unei exceptii exista doua metode:
activarea explicita a exceptiei (definite de utilizator sau predefinite) n interiorul blocului, cu ajutorul
comenzii RAISE;
activarea automata a exceptiei asociate unei erori Oracle.
Exceptiile pot fi sesizate n sectiunea executabila, declarativa sau n cea de tratare a exceptiilor. La aceste
niveluri ale programului, o exceptie poate fi gestionata n moduri diferite.
Pentru a reinvoca o exceptie, dupa ce a fost tratata n blocul curent, se foloseste instructiunea RAISE, dar
fara a fi nsotita de numele exceptiei. n acest fel, dupa executarea instructiunilor corespunzatoare tratarii
exceptiei, aceasta se transmite si blocului parinte. Pentru a fi recunoscuta ca atare de catre blocul parinte,
exceptia trebuie sa nu fie definita n blocul curent, ci n blocul parinte (sau chiar mai sus n ierarhie), n caz
contrar ea putnd fi captata de catre blocul parinte doar la categoria OTHERS.
Pentru a executa acelasi set de actiuni n cazul mai multor exceptii nominalizate explicit, n sectiunea de
prelucrare a exceptiilor se poate utiliza operatorul OR.
Pentru a evita tratarea fiecarei erori n parte, se foloseste sectiunea WHEN OTHERS care va cuprinde
actiuni pentru fiecare exceptie care nu a fost tratata, adica pentru captarea exceptiilor neprevazute sau
necunoscute. Aceasta sectiune trebuie utilizata cu atentie deoarece poate masca erori critice sau poate mpiedica
aplicatia sa raspunda n mod corespunzator.
Propagarea exceptiilor
Daca este declansata o eroare n sectiunea executabila si blocul curent are un handler pentru tratarea ei,
atunci blocul se termina cu succes, iar controlul este dat blocului imediat exterior.
Daca se produce o exceptie care nu este tratata n blocul curent, atunci exceptia se propaga spre blocul
parinte, iar blocul PL/SQL curent se termina fara succes. Procesul se repeta pna cnd fie se gaseste ntr-un
bloc modalitatea de tratare a erorii, fie se opreste executia si se semnaleaza situatia aparuta (unhandled exception
error).
Daca este declansata o eroare n partea declarativa a blocului, aceasta este propagata catre blocul imediat
exterior, chiar daca exista un handler al acesteia n blocul corespunzator sectiunii declarative.
La fel se ntmpla daca o eroare este declansata n sectiunea de tratare a erorilor. La un moment dat, ntr-
o sectiune EXCEPTION, poate fi activa numai o singura exceptie.
Instructiunea GOTO nu permite:


103

saltul la sectiunea de tratare a unei exceptii;
saltul de la sectiunea de tratare a unei exceptii, n blocul curent.
Comanda GOTO permite totusi saltul de la sectiunea de tratare a unei exceptii la un bloc care include
blocul curent.
Exemplu:
Exemplul urmator marcheaza un salt ilegal n blocul curent.
DECLARE
v_var NUMBER(10,3);
BEGIN
SELECT dim2/NVL(valoare,0)
INTO v_var
FROM opera
WHERE dim1 > 100;
<<eticheta>>
INSERT INTO politaasig(cod_polita, valoare)
VALUES (7531, v_var);
EXCEPTION
WHEN ZERO_DIVIDE THEN v_var:=0;
GOTO <<eticheta>>; --salt ilegal in blocul curent
END;
n continuare, vor fi analizate modalitatile de propagare a exceptiilor n cele trei cazuri comentate:
exceptii sesizate n sectiunea declarativa, n sectiunea executabila si n sectiunea de tratare a erorilor.
Exceptie sesizata n sectiunea executabila
Exceptia este sesizata si tratata n subbloc. Dupa aceea, controlul revine blocului exterior.
DECLARE
A EXCEPTION;
BEGIN

BEGIN
RAISE A; -- exceptia A sesizata in subbloc
EXCEPTION
WHEN A THEN -- exceptia tratata in subbloc

END;
-- aici este reluat controlul
END;
Exceptia este sesizata n subbloc, dar nu este tratata n acesta si atunci se propaga spre blocul exterior.
Regula poate fi aplicata de mai multe ori.
DECLARE
A EXCEPTION;
B EXCEPTION;
BEGIN
BEGIN
RAISE B; --exceptia B sesizata in subbloc
EXCEPTION
WHEN A THEN
--exceptia B nu este tratata in subbloc
END;
EXCEPTION
WHEN B THEN
/* exceptia B s-a propagat spre blocul exterior unde a fost
tratata, apoi controlul trece in exteriorul blocului */
END;


104

Exceptie sesizata n sectiunea declarativa
Daca n sectiunea declarativa este generata o exceptie, atunci aceasta se propaga catre blocul exterior,
unde are loc tratarea acesteia. Chiar daca exista un handler pentru exceptie n blocul curent, acesta nu este
executat.
Exemplu:
Sa se realizeze un program prin care sa se exemplifice propagarea erorilor aparute n sectiunea
declarativa a unui bloc PL/SQL. Programul calculeaza numarul creatorilor de opere de arta care au lucrari
expuse n muzeu.
BEGIN
DECLARE
nr_artisti NUMBER(3) := 'XYZ';
BEGIN
SELECT COUNT (DISTINCT cod_autor)
INTO nr_artisti
FROM opera;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Eroare bloc intern:' || SQLERRM);
END;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Eroare bloc extern:' || SQLERRM );
END;
Deoarece la initializarea variabilei nr_artisti apare o neconcordanta ntre tipul declarat si cel asignat, este
generata eroarea interna VALUE_ERROR. Cum eroarea a aparut n partea declarativa a blocului intern, desi
acesta contine un handler OTHERS care ar fi putut capta eroarea, handler-ul nu este executat, eroarea fiind
propagata catre blocul extern unde este tratata n handler-ul OTHERS asociat. Aceasta se poate remarca
deoarece la executie se obtine mesajul: Eroare bloc extern: ORA-06502: PL/SQL: numeric or value error.
Exceptie sesizata n sectiunea EXCEPTI ON
Daca exceptia este sesizata n sectiunea EXCEPTION, ea se propaga imediat spre blocul exterior.
BEGIN
DECLARE
A EXCEPTION;
B EXCEPTION;
BEGIN
RAISE A; --sesizare exceptie A
EXCEPTION
WHEN A THEN
RAISE B; --sesizare exceptie B
WHEN B THEN
/* exceptia este propagata spre blocul exterior
cu toate ca exista aici un handler pentru ea */
END;
EXCEPTION
WHEN B THEN
--exceptia B este tratata in blocul exterior
END;
Informatii despre erori
Pentru a obtine textul corespunzator erorilor la compilare, poate fi utilizata vizualizarea USER_ERRORS
din dictionarul datelor. Pentru informatii aditionale referitoare la erori pot fi consultate vizualizarile


105

ALL_ERRORS sau DBA_ERRORS.
Vizualizarea USER_ERRORS are cmpurile: NAME (numele obiectului), TYPE (tipul obiectului),
SEQUENCE (numarul secventei), LINE (numarul liniei din codul sursa n care a aparut eroarea), POSITION
(pozitia n linie unde a aparut eroarea) si TEXT (mesajul asociat erorii).
Exemplu:
Sa se afiseze erorile de compilare din procedura alfa.
SELECT LINE, POSITION, TEXT
FROM USER_ERRORS
WHERE NAME = 'ALFA';
LINE specifica numarul liniei n care apare eroarea, dar acesta nu corespunde liniei efective din fisierul
text (se refera la codul sursa depus n USER_SOURCE). Daca nu sunt erori, apare mesajul NO ROWS
SELECTED.

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