Sunteți pe pagina 1din 83

SINTEZE SGBD

AN III SEM. II MATEMATICA-INFORMATICA

TEMATICA CURSULUI 1. 2. 3. 4. 5. 6. 7. 8. PL/SQL CONCEPTE GENERALE BLOCURI PL/SQL; INSTRUCTIUNI TIPURI DE DATE IN PL/SQL GESTIUNEA CURSOARELOR IN PL/SQL SUBPROGRAME IN PL/SQL PACHETE IN PL/SQL DECLANSATORI IN PL/SQL TRATAREA ERORILOR

1. PL/SQL CONCEPTE GENERALE


Procedural Language/Structured Query Language (PL/SQL) este extensia procedural a limbajului SQL. PL/SQL este un limbaj de programare sofisticat care asigur accesarea datelor unei baze de date relaionale orientate obiect i permite gruparea unei mulimi de comenzi ntr-un bloc unic de tratare a datelor. Programul este format din unul sau mai multe blocuri care pot conine blocuri ncuibrite. PL/SQL include att instruciuni SQL pentru manipularea datelor i pentru gestiunea tranzaciilor, ct i instruciuni proprii. Limbajul combin construciile procedurale ale unui limbaj LG3 cu puterea i flexibilitatea lui SQL (LG4). Combinaia a generat un limbaj puternic pentru modelarea aplicaiilor complexe. PL/SQL extinde SQL prin construcii specifice limbajelor (definirea variabilelor, declararea tipurilor, utilizarea structurilor implementarea procedurilor i funciilor, introducerea tipurilor obiect etc.). PL/SQL ofer posibiliti moderne de tratare a informaiei: procedurale de control, i metodelor ncapsularea

datelor, analiza special a erorilor, mascarea informaiei, orientarea obiect. Posibilitile lui SQL sunt folosite pentru un acces rafinat la date, iar facilitile oferite de PL/SQL sunt folosite pentru fluxul controlului procesrii datelor. Dintre funcionalitile limbajului PL/SQL care determin ca acesta s fie frecvent utilizat se remarc urmtoarele faciliti: integrarea comenzilor SQL de baz; integrarea cu server-ul Oracle i cu utilitare Oracle; oferirea unui suport pentru programarea orientat obiect; asigurarea securitii informaiei; definirea i gestiunea blocurilor de instruciuni; gestiunea variabilelor, constantelor i a cursoarelor; modularizarea programelor (subprograme, pachete); implementarea i utilizarea declanatorilor; utilizarea structurilor de control fundamentale; detectarea i gestiunea erorilor de execuie i a situaiilor excepionale; dezvoltarea de aplicaii Web. PL/SQL este o tehnologie utilizat de server-ul Oracle i de anumite utilitare Oracle. Blocurile PL/SQL sunt transmise unui motor PL/SQL i procesate (compilate i executate) de acesta. Motorul PL/SQL poate s se afle pe server-ul Oracle sau ntr-un utilitar, iar utilizarea sa depinde de unde se invoc PL/SQL. Multe utilitare Oracle (inclusiv Developer/2000) au propriul lor motor PL/SQL care este independent de motorul prezent pe server-ul Oracle. Blocurile PL/SQL pot fi executate pe staia client fr interaciune cu serverul sau n ntregime pe server. Cnd blocurile PL/SQL sunt referite dintr-un program PRO*, din iSQL*Plus, sau de ctre Server Manager, motorul PL/SQL de pe server-ul Oracle va procesa aceste blocuri. Acesta descompune blocul n instruciuni SQL i le trimite executorului de instruciuni SQL (SQL Statement Executor) de pe server-ul Oracle. Fr PL/SQL, instruciunile SQL ar fi procesate separat, fiecare la un moment dat, fiecare implicnd un apel la server-ul Oracle. Restul comenzilor (procedurale) sunt procesate de ctre executorul instruciunilor procedurale (PSE Procedural Statement Executor) care este n motorul PL/SQL. PSE poate procesa datele care sunt locale aplicaiei, reducnduse astfel activitatea de transfer spre server-ul Oracle i numrul de cursoare solicitate. n felul acesta, este necesar un singur transfer pentru a trimite blocul din

aplicaie ctre server. O aplicaie baz de date poate fi structurat n trei pri: interfaa utilizator (utilizatorul introduce anumite informaii i obine nite rezultate n urma executrii aplicaiei); aplicaia logic efectiv; baza de date. Exist dou modele pentru proiectarea unei aplicaii baz de date: modelul client-server (two-tier); modelul three-tier. Multe dintre aplicaiile baze de date sunt construite folosind modelul clasic client-server, descris succint anterior pentru PL/SQL. Modelul este caracterizat de cele dou componente: client i server. Client-ul mnuiete interfaa, iar server-ul conine baza de date. Aplicaia logic este scindat ntre client i server. De remarcat aceast caracteristic fundamental a modelului c aplicaia comunic direct cu server-ul. Exist un motor PL/SQL pe server, iar n anumite cazuri i pe client. Dac motorul PL/SQL este pe server, atunci aplicaia (care poate fi scris n Pro*C, JDBC, OCI sau alte limbaje) care rezid 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 i trimise ca o singur entitate server-ului. Vom considera un scenariu n care exist dou motoare PL/SQL, unul pe staia client (local) i un motor PL/SQL pe server. De exemplu, un declanator ce se execut pe staia client i care apeleaz un subprogram stocat n baza de date. n acest caz, blocurile anonime sunt trimise motorului PL/SQL de pe staia client, care proceseaz local comenzile procedurale. Comenzile neprocedurale din interiorul blocului sunt trimise executorului de instruciuni SQL de pe server. De asemenea, apelurile procedurilor care sunt stocate pe server sunt trimise tot motorului de pe server pentru procesare.

2. BLOCURI PL/SQL Controlul execuiei unui bloc PL/SQL


PL/SQL este un limbaj cu structur de bloc, adic programele sunt compuse din blocuri care pot fi complet separate sau imbricate. Structura unui bloc poate fi obinut combinnd subprograme, pachete, blocuri imbricate. Blocurile pot fi folosite n utilitarele Oracle. Pentru modularizarea unui program este necesar: gruparea logic a instruciunilor n blocuri; imbricarea de subblocuri n blocuri mai mari; descompunerea unei probleme complexe ntr-o mulime de module logice i implementarea acestora cu ajutorul blocurilor; plasarea n biblioteci a codului PL/SQL reutilizabil, de unde poate fi folosit de aplicaii; depunerea codului ntr-un server Oracle, de unde este accesibil oricrei aplicaii care interacioneaz 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 fr nume, care sunt construite dinamic i sunt executate o singur dat. Acest tip de bloc nu are argumente i nu returneaz un rezultat. Ele sunt declarate ntr-un punct al aplicaiei, unde vor fi executate (trimise motorului PL/SQL). n blocurile anonime pot fi declarate proceduri i funcii PL/SQL. Blocurile anonime pot s apar ntr-un program ce lucreaz cu precompilator sau n SQL*Plus. De obicei, blocurile anonime sunt plasate ntr-un fiier, iar apoi fiierul este executat din SQL*Plus. De asemenea, declanatorii din componentele Developer Suite constau din astfel de blocuri. Blocurile neanonime sunt fie blocuri cu nume (etichetate) construite static sau dinamic i executate o singur dat, fie subprograme, pachete sau declanatori. Subprogramele sunt proceduri sau funcii depuse n baza de date. Aceste blocuri sunt executate de mai multe ori i, n general, nu mai sunt modificate dup ce au fost construite. Procedurile i funciile stocate sunt depuse pe server-ul Oracle, accept parametri i pot fi apelate prin nume. Procedurile i funciile aplicaie sunt depuse ntr-o aplicaie Developer Suite sau ntr-o bibliotec. Pachetele (stocate sau aplicaie) sunt blocuri neanonime care grupeaz

proceduri, funcii, cursoare, tipuri, constante, variabile ntr-o unitate logic, n baza de date. Declanatorii sunt blocuri PL/SQL neanonime depuse n baza de date, care pot fi asociai bazei, iar n acest caz sunt executai implicit ori de cte ori apare un anumit eveniment declanator (de exemplu, instruciuni INSERT, UPDATE sau DELETE ce se execut asupra unui tabel al bazei de date) sau pot fi asociai unei aplicaii (de exemplu, declanator SQL*Forms), ceea ce presupune c se execut automat, n funcie de anumite condiii sistem. Structura unui bloc PL/SQL Un bloc PL/SQL este compus din trei seciuni distincte. Seciunea declarativ (opional) conine declaraii pentru toate variabilele, constantele, cursoarele i erorile definite de utilizator la care se face referin n seciunea executabil sau chiar n cea declarativ. De asemenea, pot fi declarate subprograme locale care sunt vizibile doar n blocul respectiv. Seciunea executabil conine instruciuni neprocedurale SQL pentru prelucrarea datelor din baza de date i instruciuni PL/SQL pentru prelucrarea datelor n cadrul blocului. Seciunea pentru tratarea erorilor (opional) specific aciunile ce vor fi efectuate atunci cnd n execuia blocului apar erori sau condiii anormale. Blocul PL/SQL are urmtoarea structur general: [<<nume_bloc>>] [DECLARE instruciuni de declarare] BEGIN instruciuni executabile (SQL sau PL/SQL) [EXCEPTION tratarea erorilor] END [nume_bloc]; Dac blocul PL/SQL este executat fr erori, invariant va aprea mesajul: PL/SQL procedure successfully completed Exemplu (SELECT cu clauza INTO) S se creeze un bloc anonim n care se declar o variabil v_job de tip job_title (%TYPE) a crei valoare va fi titlul jobului salariatului avnd codul 200.

SQL> SET SERVEROUTPUT ON SQL> DECLARE 2 v_job jobs.job_title%TYPE; 3 BEGIN 4 SELECT job_title 5 INTO v_job 6 FROM employees e, jobs j 7 WHERE e.job_id=j.job_id 8 AND employee_id=200; 9 DBMS_OUTPUT.PUT_LINE('jobul este '|| v_job); 10 END; 11 / jobul este Administration Assistant PL/SQL procedure successfully completed. Varianta 2 S se rezolve problema anterioar utiliznd variabile de legtur. S se afieze rezultatul att din bloc, ct i din exteriorul acestuia.

SQL> VARIABLE rezultat VARCHAR2(35) SQL> BEGIN 2 SELECT job_title 3 INTO :rezultat 4 FROM employees e, jobs j 5 WHERE e.job_id=j.job_id AND employee_id=200; 6 DBMS_OUTPUT.PUT_LINE('rezultatul este '|| :rezultat); 7 END; 8 / rezultatul este Administration Assistant PL/SQL procedure successfully completed. SQL> PRINT REZULTAT REZULTAT ------------------------------

Administration Assistant Compatibilitate SQL Din punct de vedere al compatibilitii dintre PL/SQL i SQL, se remarc urmtoarele reguli de baz: PL/SQL furnizeaz toate comenzile LMD ale lui SQL, comanda SELECT cu clauza INTO, comenzile LCD, funciile, pseudocoloanele i operatorii SQL; PL/SQL nu furnizeaz comenzile LDD. Totui, n ultimele sale versiuni, Oracle permite folosirea dinamic a comenzilor SQL, utiliznd tehnica oferit de SQL dinamic. n felul acesta, orice comand SQL (inclusiv comand LDD) poate s fie utilizat n PL/SQL. Majoritatea funciilor SQL sunt disponibile n PL/SQL. Exist ns funcii specifice PL/SQL, cum sunt funciile SQLCODE i SQLERRM. De asemenea, exist funcii SQL care nu sunt disponibile n instruciuni procedurale (DECODE, funciile grup), dar care sunt disponibile n instruciunile SQL dintr-un bloc PL/SQL. SQL nu poate folosi funcii sau atribute specifice PL/SQL. Funciile grup trebuie folosite cu atenie, deoarece clauza GROUP BY nu are sens s apar n instruciunea SELECT INTO. Oracle9i introduce clauza OVER, care permite ca funcia grup creia i este asociat s fie considerat o funcie analitic (poate returna mai multe linii pentru fiecare grup). Urmtoarele funcii 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. Instruciuni PL/SQL Orice program poate fi scris utiliznd structuri de control de baz care sunt combinate n diferite moduri pentru rezolvarea problemei propuse. PL/SQL dispune de comenzi ce permit controlul execuiei unui bloc. Instruciunile limbajului pot fi: iterative (LOOP, WHILE, FOR), de atribuire (:=), condiionale (IF, CASE), de salt (GOTO, EXIT) i instruciunea vid (NULL). Observaii: Comentariile sunt ignorate de compilatorul PL/SQL. Exist comentarii pe o singur linie, prefixate de simbolurile --, care ncep n orice punct al liniei i se termin la sfritul acesteia. De asemenea, exist comentarii pe mai multe linii, care sunt delimitate de simbolurile /* i */. Nu se

admit comentarii imbricate. Caracterul ; este separator pentru instruciuni. Att operatorii din PL/SQL, ct i ordinea de execuie 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 i n toate subblocurile, procedurile i funciile imbricate n acesta. Dac blocul nu gsete identificatorul declarat local, atunci l caut n seciunea declarativ a blocurilor care includ blocul respectiv i niciodat nu caut n blocurile ncuibrite n acesta. Comenzile SQL*Plus nu pot s apar ntr-un bloc PL/SQL. n comanda SELECT trebuie specificate variabilele care recupereaz rezultatul aciunii acestei comenzi. n clauza INTO, care este obligatorie, pot fi folosite variabile PL/SQL sau variabile de legtur. Referirea la o variabil de legtur se face prin prefixarea acesteia cu simbolul :. Cererea dintr-o comand SELECT trebuie s returneze o singur linie drept rezultat. Atunci cnd comanda SELECT ntoarce mai multe linii, apare eroarea TOO_MANY_ROWS, iar n cazul n care comanda nu gsete date se genereaz eroarea NO_DATA_FOUND. Un bloc PL/SQL nu este o unitate tranzacional. ntr-un bloc pot fi mai multe tranzacii sau blocul poate face parte dintr-o tranzacie. Aciunile COMMIT, SAVEPOINT i ROLLBACK sunt independente de blocuri, dar instruciunile asociate acestor aciuni pot fi folosite ntr-un bloc. PL/SQL nu suport comenzile GRANT i REVOKE, utilizarea lor fiind posibil doar prin SQL dinamic. Fluxul secvenial de execuie a comenzilor unui program PL/SQL poate fi modificat cu ajutorul structurilor de control: IF, CASE, LOOP, FOR, WHILE, GOTO, EXIT. Instruciunea de atribuire Instruciunea de atribuire se realizeaz cu ajutorul operatorului de asignare (:=) i are forma general clasic (variabila := expresie). Comanda respect proprietile instruciunii de atribuire din clasa LG3. De remarcat c nu poate fi asignat valoarea null unei variabile care a fost declarat NOT NULL.

Exemplu: Urmtorul exemplu prezint modul n care acioneaz instruciunea de atribuire n cazul unor tipuri de date particulare. DECLARE alfa INTERVAL YEAR TO MONTH; BEGIN alfa := INTERVAL '200-7' YEAR TO MONTH; DBMS_OUTPUT.PUT_LINE(alfa); -- 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; SQL> declare 2 alfa interval year to month; 3 begin 4 alfa :=interval '1 - 7' year to month; 5 DBMS_OUTPUT.PUT_LINE('alfa = '|| alfa); 6 end; 7 / alfa = +01-07 PL/SQL procedure successfully completed. SQL> declare 2 alfa interval year to month; 3 begin 4 alfa :=interval '7' month; 5 DBMS_OUTPUT.PUT_LINE('alfa = '|| alfa); 6 end; 7 / alfa = +00-07 PL/SQL procedure successfully completed. SQL> declare 2 alfa interval year to month;

3 begin 4 alfa := '10 - 8'; 5 DBMS_OUTPUT.PUT_LINE('alfa = '|| alfa); 6 end; 7 / alfa = +10-08 PL/SQL procedure successfully completed. DECLARE beta opera%ROWTYPE; gama opera%ROWTYPE; cursor epsilon IS SELECT * FROM opera; delta epsilon%ROWTYPE; BEGIN beta := gama; -- corect gama := delta; -- incorect???-testati! END; Instruciunea IF Un program PL/SQL poate executa diferite poriuni de cod, n funcie de rezultatul unui test (predicat). Instruciunile care realizeaz acest lucru sunt cele condiionale (IF, CASE). Structura instruciunii IF n PL/SQL este similar instruciunii IF din alte limbaje procedurale, permind efectuarea unor aciuni n mod selectiv, n funcie de anumite condiii. Instruciunea IF-THEN-ELSIF are urmtoarea form sintactic: IF condiie1 THEN secvena_de_comenzi_1 [ELSIF condiie2 THEN secvena_de_comenzi_2] [ELSE secvena_de_comenzi_n] END IF;

O secven de comenzi din IF este executat numai n cazul n care condiia asociat este TRUE. Atunci cnd condiia este FALSE sau NULL, secvena nu este executat. Dac pe ramura THEN se dorete verificarea unei alternative, se folosete ramura ELSIF (atenie, nu ELSEIF) cu o nou condiie. Este permis un numr arbitrar de opiuni ELSIF, dar poate aprea cel mult o clauz ELSE. Aceasta se refer la ultimul ELSIF. Exemplu: S se specifice dac o galerie este mare, medie sau mica dup cum numrul operelor de art expuse n galeria respectiv este mai mare dect 200, cuprins ntre 100 i 200 sau mai mic dect 100. 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; /

Exemplu: S se specifice dac un angajat dat are salariu mare, mediu sau mic dup cum este mai mare dect 20000, cuprins ntre 10000 i 20000 sau mai mic dect 10000.

SQL> DEFINE p_cod_em = 201 SQL> DECLARE 2 v_cod_ang EMPLOYEES. EMPLOYEE_ID%TYPE := &p_cod_em; 3 v_sal EMPLOYEES.salary%type; 4 v_comentariu VARCHAR2(10); 5 BEGIN 6 SELECT salary 7 INTO v_sal 8 FROM EMPLOYEES 9 WHERE EMPLOYEE_ID = v_cod_ang; 10 IF v_sal < 10000 THEN 11 v_comentariu := 'mic'; 12 ELSIF v_sal BETWEEN 10000 AND 20000 THEN 13 v_comentariu := 'mediu'; 14 ELSE 15 v_comentariu:= 'mare'; 16 END IF; 17 DBMS_OUTPUT.PUT_LINE('salariatul avand codul '|| v_cod_ang ||' are salariu '|| v_sal || ' considerat '|| v_comentariu); 18 END; 19 / old 2: v_cod_ang EMPLOYEES. EMPLOYEE_ID%TYPE := &p_cod_em; new 2: v_cod_ang EMPLOYEES. EMPLOYEE_ID%TYPE := 201; salariatul avand codul 201 are salariu 13000 considerat mediu PL/SQL procedure successfully completed./

Instruciunea CASE Oracle9i furnizeaz o nou comand (CASE) care permite implementarea unor condiii multiple. Instruciunea are urmtoarea form sintactic: [<<eticheta>>] CASE test_var WHEN valoare_1 THEN secvena_de_comenzi_1; WHEN valoare_2 THEN secvena_de_comenzi_2; WHEN valoare_k THEN secvena_de_comenzi_k; [ELSE alt_secven;] END CASE [eticheta];

Se va executa secvena_de_comenzi_p, dac valoarea selectorului test_var este valoare_p. Dup ce este executat secvena de comenzi, controlul va trece la urmtoarea instruciune dup CASE. Selectorul test_var poate fi o variabil sau o expresie complex care poate conine chiar i apeluri de funcii. Clauza ELSE este opional. Dac aceast clauz este necesar n implementarea unei probleme, dar totui lipsete, iar test_var nu ia nici una dintre valorile ce apar n clauzele WHEN, atunci se declaneaz eroarea predefinit CASE_NOT_FOUND (ORA - 06592). Comanda CASE poate fi etichetat i, n acest caz, eticheta poate s apar la sfritul clauzei END CASE. De remarcat c eticheta dup END CASE este permis numai n cazul n care comanda CASE este etichetat. Selectorul test_var poate s lipseasc din structura comenzii CASE, care n acest caz va avea urmtoarea form sintactic: [<<eticheta>>] CASE WHEN condiie_1 THEN secvena_de_comenzi_1; WHEN condiie_2 THEN secvena_de_comenzi_2; WHEN condiie_k THEN secvena_de_comenzi_k; [ELSE alt_secven;] END CASE [eticheta]; Fiecare clauz WHEN conine o expresie boolean. Dac valoarea lui condiie_p este TRUE, atunci este executat secvena_de_comenzi_p. Exemplu: n funcie de o valoare introdus de utilizator, care reprezint abrevierea zilelor unei sptmni, s se afieze (n cele dou variante) un mesaj prin care este specificat ziua sptmnii corespunztoare 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(' Astazi este Luni'); WHEN 'M' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Marti'); WHEN 'MI' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Miercuri'); WHEN 'J' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Joi'); WHEN 'V' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Vineri');

WHEN 'S' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Sambata'); WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este 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('Astazi este Luni'); WHEN v_zi = 'M' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Marti'); WHEN v_zi = 'MI' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Miercuri'); WHEN v_zi = 'J' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Joi'); WHEN v_zi = 'V' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Vineri'); WHEN v_zi = 'S' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Sambata'); WHEN v_zi = 'D' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Duminica'); ELSE DBMS_OUTPUT.PUT_LINE('Este o eroare!'); END CASE; END; / SET SERVEROUTPUT OFF n Oracle9i poate fi utilizat o construcie CASE ntr-o comand SQL a unui bloc PL/SQL. Expresia CASE are sintaxa similar comenzii CASE, dar clauzele WHEN nu se termin prin caracterul ;, clauza END nu include cuvntul cheie CASE i nu se fac atribuiri n clauza WHEN. Expresia CASE returneaza null daca nu exista clauza ELSE si daca nici o

conditie nu este indeplinita. SELECT last_name, SALARY, (CASE WHEN salary <5000 THEN WHEN salary <10000 THEN WHEN salary <15000 THEN ELSE END) AS calificare FROM EMPLOYEES;

' ' ' '

LOW' MEDIUM' GOOD' EXCELLENT'

LAST_NAME SALARY CALIFICARE ------------------------- ---------- -------------------------King 24000 EXCELLENT Kochhar 17000 EXCELLENT De Haan 17000 EXCELLENT Hunold 9000 MEDIUM Ernst 6000 MEDIUM Austin 4800 LOW Pataballa 4800 LOW Lorentz 4200 LOW Greenberg 12000 GOOD Faviet 9000 MEDIUM Chen 8200 MEDIUM

Instruciuni iterative Exist trei tipuri de comenzi iterative: ciclarea simpl LOOP, ciclarea WHILE i ciclarea FOR. Acestea permit repetarea (condiionat sau necondiionat) execuiei uneia sau mai multor instruciuni. Ciclurile pot fi imbricate pe mai multe niveluri. Ele pot fi etichetate, iar ieirea din ciclu se poate realiza cu ajutorul comenzii EXIT. Se utilizeaz: comanda LOOP, dac instruciunile din cadrul ciclului trebuie s se execute cel puin o dat; comanda WHILE, n cazul n care condiia trebuie evaluat la nceputul fiecrei iteraii; comanda FOR, dac numrul de iteraii este cunoscut.

Instruciunea LOOP are urmtoarea form sintactic: LOOP secvena_de_comenzi; END LOOP; Ciclarea simpl cuprinde o mulime de comenzi incluse ntre cuvintele cheie LOOP i END LOOP. Aceste comenzi se execut cel puin o dat. Dac nu este utilizat comanda EXIT, ciclarea poate continua la infinit. Exemplu: Se presupune c a fost creat structura tabelului org_tab, constnd din dou coloane: cod_tab de tip INTEGER, ce conine un contor al nregistrrilor i text_tab de tip VARCHAR2, ce conine un text asociat fiecrei nregistrri. S se introduc 70 de nregistrri n tabelul org_tab. CREATE TABLE ORG_TAB (cod_tab INTEGER, text_tab VARCHAR2(25)); SET SERVEROUTPUT ON DECLARE v_contor BINARY_INTEGER := 1; BEGIN LOOP INSERT INTO org_tab VALUES (v_contor, 'indicele '|| v_contor := v_contor + 1; EXIT WHEN v_contor > 70; END LOOP; COMMIT; END; SELECT * FROM ORG_TAB;

v_contor);

COD_TAB TEXT_TAB ---------- --------------------

67 68 69 70

indicele indicele indicele indicele

67 68 69 70

70 rows selected. Instruciunea repetitiv WHILE permite repetarea unei secvene de instruciuni, atta timp ct o anumit condiie specificat este adevrat. Comanda WHILE are urmtoarea sintax: WHILE condiie LOOP secvena_de_comenzi; END LOOP; Dac variabilele care apar n condiie nu se schimb n interiorul ciclului, atunci condiia rmne adevrat i ciclul nu se termin. Cnd condiia este evaluat ca fiind FALSE sau NULL, atunci secvena de comenzi nu este executat i controlul trece la prima instruciune dup END LOOP. Exemplu: 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; Instruciunea repetitiv FOR (ciclare cu pas) permite executarea unei secvene de instruciuni pentru valori ale variabilei contor cuprinse ntre dou limite, lim_inf i lim_sup. Dac este prezent opiunea REVERSE, iteraia se face

(n sens invers) de la lim_sup la lim_inf. Comanda FOR are sintaxa: FOR contor_ciclu IN [REVERSE] lim_inf..lim_sup LOOP secvena_de_comenzi; END LOOP; Variabila contor_ciclu nu trebuie declarat. Ea este neidentificat n afara ciclului i implicit de tip BINARY_INTEGER. Pasul are implicit valoarea 1 i nu poate fi modificat. Limitele domeniului pot fi variabile sau expresii, care s poat fi convertite la ntreg. Exemplu: n structura tabelului opera se va introduce un nou cmp (stea). S se creeze un bloc PL/SQL care va reactualiza acest cmp, introducnd o stelu pentru fiecare 10000$ din valoarea unei opere de art al crei cod este specificat. ALTER TABLE ADD stea opera 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; END IF; UPDATE opera SET stea = v_stea WHERE cod_opera = v_cod_opera; COMMIT; END;

Exemplu: SQL> ALTER TABLE 2 ADD stea Table altered.

EMPLOYEES VARCHAR2(20);

DECLARE v_cod_ANGAJ EMPLOYEES.EMPLOYEE_ID%TYPE :=&p_cod_ANGAJ; v_valoare EMPLOYEES.SALARY%TYPE; v_stea EMPLOYEES.stea%TYPE := NULL; BEGIN SELECT NVL(ROUND(SALARY/10000),0) INTO v_valoare FROM EMPLOYEES WHERE EMPLOYEE_ID = v_cod_ANGAJ; IF v_valoare > 0 THEN FOR i IN 1..v_valoare LOOP v_stea := v_stea || '*'; END LOOP; END IF; UPDATE EMPLOYEES SET stea = v_stea WHERE EMPLOYEE_ID = v_cod_ANGAJ; COMMIT; END; / Enter value for p_cod_angaj: 100 old 2: v_cod_ANGAJ EMPLOYEES.EMPLOYEE_ID%TYPE &p_cod_ANGAJ; new 2: v_cod_ANGAJ EMPLOYEES.EMPLOYEE_ID%TYPE := 100; PL/SQL procedure successfully completed. :=

SQL> SELECT * FROM EMPLOYEES 2 WHERE EMPLOYEE_ID=100; EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMBER HIRE_DATE JOB_ID SALARY ----------- -------------------- ------------------------- ------------------------- --------------COMMISSION_PCT MANAGER_ID DEPARTMENT_ID STEA -------------- ---------- ------------- -------------------100 Steven King SKING 515.123.4567 17-JUN-87 AD_PRES 24000 90 **

Instruciuni de salt
Instruciunea EXIT permite ieirea dintr-un ciclu. Ea are o form necondiional (ieire fr condiii) i una condiional. Controlul trece fie la prima instruciune situat dup clauza END LOOP corespunztoare, fie dup instruciunea LOOP avnd eticheta nume_eticheta. EXIT [nume_eticheta] [WHEN condiie]; Numele etichetelor urmeaz aceleai reguli ca cele definite pentru identificatori. Eticheta se plaseaz naintea comenzii, fie pe aceeai linie, fie pe o linie separat. n PL/SQL etichetele se definesc prin intercalarea numelui etichetei ntre caracterele << i >> (<<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; Instruciunea GOTO determin un salt necondiionat la o instruciune executabil sau la nceputul unui bloc care are eticheta specificat n comand. Instruciunea are urmtoarea form sintactic: GOTO nume_eticheta; Nu este permis saltul: n interiorul unui bloc (subbloc); n interiorul unei comenzi IF, CASE sau LOOP; de la o clauz a comenzii CASE, la alt clauz a aceleai comenzi; de la tratarea unei excepii, n blocul curent; n exteriorul unui subprogram.

Instruciunea vid Instruciunea vid (NULL) este folosit pentru o mai bun lizibilitate a programului. NULL este instruciunea care nu are nici un efect, marcnd faptul c nu trebuie ntreprins nici o aciune. Nu trebuie confundat instruciunea NULL cu valoarea null! Uneori instruciunea NULL este folosit ntr-o comand IF, indicnd faptul c pentru o anumit clauz ELSIF nu se execut nici o aciune.

2. Tipuri de date n PL/SQL


Fiecare variabil sau constant utilizat ntr-un bloc PL/SQL este de un anumit tip prin care se specific: formatul su de stocare, constrngerile care trebuie s le verifice domeniul valorilor sale. Variabilele folosite n Oracle9i pot fi: specifice PL/SQL; nespecifice PL/SQL. Variabile specifice PL/SQL se clasific n variabile: de tip scalar, compuse, referin, LOB (large objects), tipuri obiect. Variabile nespecifice PL/SQL pot fi: variabile de legtur (bind variables), variabile gazd (host variables), variabile indicator.

Variabile specifice PL/SQL Tipurile de date scalare


Nu au componente interne (conin valori atomice). Se mpart n 5 clase. Tipurile de date ce stocheaz valori numerice cuprind tipul NUMBER cu subtipurile DEC, DECIMAL, DOUBLE PRECISION, FLOAT, INTEGER, INT, NUMERIC, REAL, SMALLINT; tipul BINARY_INTEGER cu subtipurile NATURAL, NATURALN, POSITIVE, POSITIVEN, SIGNTYPE; tipul PLS_INTEGER. Tipurile de date ce stocheaz 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 stocheaz data calendaristic i 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 globalizare ce stocheaz date unicode includ tipurile NCHAR i NVARCHAR2. Tipul de date BOOLEAN stocheaz valori logice (true, false sau null).

Tipurile de date compuse


Au componente interne care pot fi manipulate individual. Oracle ofer programatorului dou tipuri de date compuse: nregistrare (RECORD); colecie (INDEX-BY TABLE, NESTED TABLE, VARRAY).

Tipurile de date referin


REF CURSOR si REF obiect sunt tipuri de date ale cror valori, numite pointeri, fac referin ctre obiecte din program. Pointerii conin locaia de memorie (adresa) unui element i nu elementul n sine. Tipul REF CURSOR este folosit pentru a face referin la un cursor explicit. Tipul REF obiect face referin la adresa unui obiect.

Tipurile de date LOB


Large object sunt acele tipuri de date ale cror valori, numite locatori (locators) specific localizarea unor obiecte de dimensiuni mari, adic blocuri de date nestructurate, cum ar fi texte, imagini grafice, clipuri video i sunete. Tipurile LOB sunt manipulate cu ajutorul pachetului DBMS_LOB. Aceste tipuri sunt: CLOB (character large object), BLOB (binary large object), BFILE (binary file), NCLOB (national language character large object).

Tipurile obiect
Sunt tipuri compuse, definite de utilizator, care ncapsuleaz structuri de date (atribute) mpreun cu subprograme pentru manipularea datelor (metode). Dintre tipurile scalare PL/SQL, urmtoarele sunt i tipuri SQL (adic 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 difer de corespondentele lor SQL prin dimensiunea maxim permis.

Tipul NUMBER memoreaz numerele n virgul fix i virgul mobil. El are forma general NUMBER (m, n), unde m reprezint numrul total de cifre, iar n numrul de zecimale. Valoarea unei variabile de tip NUMBER este cuprins ntre 1.0E-129 i 9.99E125. Numrul de zecimale determin poziia n care apare rotunjirea. Valoarea sa este cuprins ntre -84 i 127, iar implicit este 0. Tipul NUMBER are urmtoarele subtipuri, care au aceleai intervale de valori: NUMERIC, REAL, DEC, DECIMAL i DOUBLE PRECISION (pentru memorarea datelor numerice n virgul fix), FLOAT (pentru memorarea datelor numerice n virgul mobil), SMALLINT, INTEGER i INT (pentru memorarea numerelor ntregi). Aceste subtipuri se pot utiliza pentru compatibilitate ANSI/ISO, IBM SQL/DS sau IBM DB2. Tipul BINARY_INTEGER memoreaz numere ntregi cu semn avnd valori cuprinse ntre -231 - 1 i 231 - 1. Acest tip de date este utilizat frecvent pentru indecii tabelelor, nu necesit conversii i admite mai multe subtipuri. De exemplu, pentru a restriciona domeniul variabilelor la valori ntregi nenegative se utilizeaz tipurile NATURAL (0 .. 231 1) i POSITIVE (1 .. 231 1). Tipul PLS_INTEGER este utilizat pentru stocarea numerelor ntregi cu semn i are acelai interval de definire ca i tipul BINARY_INTEGER. Operaiile cu acest tip sunt efectuate mai rapid (folosesc aritmetica mainii), dect cele cu tipurile NUMBER sau BINARY_INTEGER (folosesc librrii aritmetice). Prin urmare, pentru o mai bun performan, este preferabil s se utilizeze tipul PLS_INTEGER. Variabilele alfanumerice pot fi de tip CHAR, VARCHAR2, LONG, RAW i LONGRAW. Reprezentarea intern depinde de setul de caractere ales (ASCII sau EBCDIC). Tipurile CHAR, VARCHAR2 i RAW pot avea un parametru pentru a preciza lungimea maxim. Dac aceasta nu este precizat atunci, implicit, se consider 1. Lungimea este exprimat n octei (nu n caractere). Subtipurile acestor tipuri se pot utiliza pentru compatibilitate ANSI/ISO, IBM SQL/DS sau IBM DB2. n Oracle9i a fost extins sintaxa pentru CHAR i VARCHAR2, permind ca variabila ce precizeaz lungimea maxim s fie de tip CHAR sau BYTE. Variabilele de tip LONG pot memora texte, tabele de caractere sau documente, prin urmare iruri de caractere de lungime variabil de pn la 32760 octei. Este similar tipului VARCHAR2. Tipul RAW permite memorarea datelor binare (bii) sau a irurilor de octei. De exemplu, o variabil RAW poate memora o secven de caractere grafice sau o imagine digitizat. Tipul RAW este similar tipului alfanumeric, cu excepia faptului

c PL/SQL nu interpreteaz datele de tip RAW. Oracle nu face conversia datelor de acest tip, atunci cnd se transmit de la un sistem la altul. Chiar dac lungimea maxim a unei variabile RAW poate fi 32767 octei, ntr-o coloan RAW a bazei de date nu se pot introduce dect 2000 octei. Pentru a insera valori mai mari se folosete o coloan de tip LONG RAW, care are lungimea maxim 231 octei. 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 i permit rafinri ale tipului DATE. De exemplu, TIMESTAMP poate lua n considerare i fraciuni de secund. PL/SQL suport dou seturi de caractere: una specific bazei de date care este utilizat pentru definirea identificatorilor i a codului surs (database character set - DCS) i o mulime de caractere naionale care este folosit pentru reprezentarea informaiei cu caracter naional (national character set - NCS). Tipurile de date NCHAR i NVARCHAR2 sunt utilizate pentru stocarea n baza de date a irurilor de caractere ce folosesc NCS. Ele ofer suport pentru globalizarea datelor, astfel nct utilizatorii din toat lumea pot interaciona cu Oracle n limba lor naional. Aceste tipuri de date suport numai date Unicode. Unicode este o mulime de caractere globale care permite stocarea de informaie n orice limb, folosind o mulime unic de caractere. Prin urmare, unicode furnizeaz o valoare cod unic pentru fiecare caracter, indiferent de platform, program sau limb.

Variabile nespecifice PL/SQL


Variabila de legtur (bind) se declar ntr-un mediu gazd i este folosit pentru transferul (la execuie) valorilor numerice sau de tip caracter n/din unul sau mai multe programe PL/SQL. Variabilele declarate n mediul gazd sau n cel apelant pot fi referite n instruciuni PL/SQL, dac acestea nu sunt n cadrul unei proceduri, funcii sau pachet. n SQL*Plus, variabilele de legtur se declar folosind comanda VARIABLE, iar pentru afiarea valorilor acestora se utilizeaz comanda PRINT. Ele sunt referite prin prefixarea cu simbolul :, pentru a putea fi deosebite de variabilele declarate n PL/SQL. Deoarece instruciunile SQL pot fi integrate n programe C, este necesar un mecanism pentru a transfera valori ntre mediul de programare C i instruciunile SQL care comunic cu server-ul bazei de date Oracle. n acest scop, n programul

ncapsulat sunt definite variabilele gazd (host). Acestea sunt declarate ntre directivele BEGIN DECLARE SECTION i END DECLARE SECTION ale preprocesorului. O valoare null n baza de date nu are o valoare corespunztoare n mediul limbajului gazd (de exemplu, limbajul C). Pentru a rezolva problema comunicrii valorilor null ntre programul scris n limbaj gazd i sistemul Oracle, au fost definite variabilele indicator. Acestea sunt variabile speciale de tip ntreg, folosite pentru a indica dac o valoare null este recuperat (extras) din baza de date sau stocat n aceasta. Ele au urmtoarea form: :nume_extern [: indicator] De exemplu, dac atribuirea este fcut de limbajul gazd, valoarea 1 a indicatorului specific faptul c PL/SQL trebuie s nlocuiasc valoarea variabilei prin null, iar o valoare a indicatorului mai mare ca zero precizeaz c PL/SQL trebuie s considere chiar valoarea variabilei.

Declararea variabilelor
Identificatorii PL/SQL trebuie declarai nainte de a fi referii n blocul PL/SQL. Dac n declaraia unei variabile apar referiri la alte variabile, acestea trebuie s fi fost declarate anterior. Orice variabil declarat ntr-un bloc este accesibil blocurilor coninute sintactic n acesta. Tipurile scalare sunt predefinite n pachetul STANDARD. Pentru a folosi un astfel de tip ntr-un program este suficient s fie declarat o variabil de tipul respectiv. Tipurile compuse sunt definite de utilizator. Prin urmare, n acest caz trebuie definit efectiv tipul i apoi declarat variabila de tipul respectiv. n declararea variabilelor pot fi utilizate atributele %TYPE i %ROWTYPE, care reprezint tipuri de date implicite. Aceste tipuri permit declararea unei variabile n concordan cu declaraii de variabile fcute 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 nregistrri dintr-un tabel. Avantajul utilizrii acestui atribut const n faptul c nu este necesar s se cunoasc numrul i tipurile coloanelor tabelului. Elementele individuale ale acestei structuri de tip nregistrare sunt referite n maniera clasic, prefixnd numele coloanei cu numele variabilei declarate. Calitatea atributelor %TYPE i %ROWTYPE const n faptul c simplific ntreinerea codului PL/SQL. De exemplu, poate fi modificat dimensiunea unei coloane, fr s fie necesar modificarea declaraiei variabilelor al cror tip s-a

definit fcnd referin la tipul coloanei respective. Sintaxa declarrii unei variabile este urmtoarea: identificator [CONSTANT] {tip_de_date | identificator%TYPE | identificator%ROWTYPE} [NOT NULL] [ {:= | DEFAULT} expresie_PL/SQL]; Se pot defini constante (valoarea stocat nu poate fi modificat) prin specificarea la declarare a cuvntului cheie CONSTANT. Exemplu:
v_valoare v_data_achizitie v_material c_valoare v_stare v_clasificare v_cod_opera v_opera int_an_luna NUMBER(15) NOT NULL := 0; DATE DEFAULT SYSDATE; VARCHAR2(15) := 'Matase'; CONSTANT NUMBER := 100000; VARCHAR2(20) DEFAULT 'Buna'; BOOLEAN DEFAULT FALSE; opera.cod_opera TYPE; opera ROWTYPE; INTERVAL YEAR TO MONTH := INTERVAL '3-2' YEAR TO MONTH;

Observaii: 1. Pentru a denumi o variabil este utilizat frecvent (pentru uurina referirii) prefixarea cu litera v (v_identificator), iar pentru o constant este folosit prefixarea cu litera c (c_identificator). 2. Variabilele pot fi iniializate, iar dac o variabil nu este iniializat, valoarea implicit a acesteia este null. Dac o variabil este declarat NOT NULL, atunci ea va fi obligatoriu iniializat. 3. Pentru a iniializa o variabil sau o constant poate fi utilizat o expresie PL/SQL compatibil ca tip cu variabila sau constanta respectiv. 4. Constantele trebuie iniializate cnd sunt declarate, altfel apare o eroare la compilare. 5. n seciunea declarativ, pe fiecare linie, exist o singur declaraie de variabil. 6. Dou obiecte (variabile) pot avea acelai nume cu condiia s fie definite n blocuri diferite. Dac ele coexist, poate fi folosit doar obiectul declarat n blocul curent. 7. Atributul %ROWTYPE nu poate include clauze de iniializare.

Definirea subtipurilor
Subtipurile deriv dintr-un tip de baz, la care se adaug anumite restricii. De exemplu, NATURAL este un subtip predefinit PL/SQL, derivat din tipul de baz BINARY_INTEGER, cu restricia c permite prelucrarea valorilor ntregi nenegative. Prin urmare, un subtip nu reprezint un nou tip de date, ci un tip existent asupra cruia se aplic anumite constrngeri. Subtipurile presupun acelai set de operaii ca i tipul de baz, dar aplicate unui subset de valori al acestui tip. Sistemul Oracle permite ca utilizatorul s-i defineasc propriile sale tipuri i subtipuri de date n partea declarativ a unui bloc PL/SQL, subprogram sau pachet utiliznd sintaxa: SUBTYPE nume_subtip IS tip_de_baza [NOT NULL]; n dicionarul datelor exist vizualizri care furnizeaz informaii despre tipurile de date create de utilizator (USER_TYPES, USER_TYPE_ATTRS).

Conversii ntre tipuri de date


Exist dou tipuri de conversii: implicite; explicite. PL/SQL face automat conversii implicite ntre caractere i numere sau ntre caractere i date calendaristice. Chiar dac sistemul realizeaz automat aceste conversii, n practic se utilizeaz frecvent funcii de conversie explicit. Funciile de conversie explicit din SQL sunt utilizabile i n PL/SQL. Acestea sunt: TO_NUMBER, TO_CHAR, TO_DATE, TO_MULTI_BYTE, TO_SINGLE_BYTE, CHARTOROWID, ROWIDTOCHAR, RAWTOHEX, HEXTORAW, TO_CLOB, TO_LOB. n Oracle9i se pot folosi urmtoarele funcii 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 funcii reflect posibilitile pe care le ofer. De

exemplu, TO_YMINTERVAL convertete argumentele sale la tipul INTERVAL YEAR TO MONTH conform unui format specificat. Funcia COMPOSE convertete un ir de caractere la un ir unicode (asociaz o valoare cod unic pentru fiecare simbol din ir).

nregistrri
Tipul RECORD ofer un mecanism pentru prelucrarea nregistrrilor. nregistrrile au mai multe cmpuri ce pot fi de tipuri diferite, dar care sunt legate din punct de vedere logic. Inregistrrile trebuie definite n doi pai: se definete tipul RECORD; se declar nregistrrile de acest tip. Declararea tipului RECORD se face conform urmtoarei sintaxe: TYPE nume_tip IS RECORD (nume_cmp1 {tip_cmp | variabil%TYPE | nume_tabel.coloan%TYPE | nume_tabel%ROWTYPE} [ [NOT NULL] {:= | DEFAULT} expresie1], (nume_cmp2 {tip_cmp | variabil%TYPE | nume_tabel.coloan%TYPE | nume_tabel%ROWTYPE} [ [NOT NULL] {:= | DEFAULT} expresie2],); Identificatorul nume_tip reprezint numele tipului RECORD care se va specifica n declararea nregistrrilor, nume_cmp este numele unui cmp al nregistrrii, iar tip_cmp este tipul de date al cmpului. Observaii: Dac un cmp nu este iniializat atunci implicit se consider c are valoarea NULL. Dac s-a specificat constrngerea NOT NULL, atunci obligatoriu cmpul trebuie iniializat cu o valoare diferit de NULL. Pentru referirea cmpurilor individuale din nregistrare se prefixeaz numele cmpului cu numele nregistrrii. Pot fi asignate valori unei nregistrri utiliznd comenzile SELECT, FETCH sau instruciunea clasic de atribuire. De asemenea, o nregistrare poate fi asignat altei nregistrari de acelai tip.

Componentele unei nregistrri pot fi de tip scalar, RECORD, TABLE, obiect, colecie (dar, nu tipul REF CURSOR). PL/SQL permite declararea i referirea nregistrrilor imbricate. Numrul de cmpuri ale unei nregistrri nu este limitat. nregistrrile nu pot fi comparate (egalitate, inegalitate sau null). nregistrrile pot fi parametri n subprograme i pot s apar n clauza RETURN a unei funcii. Diferena dintre atributul %ROWTYPE i tipul de date compus RECORD: tipul RECORD permite specificarea tipului de date pentru cmpuri i permite declararea cmpurilor sale; atributul %ROWTYPE nu cere cunoaterea numrului i tipurilor coloanelor tabloului. Oracle9i introduce cteva faciliti legate de acest tip de date. Se poate insera (INSERT) o linie ntr-un tabel utiliznd o nregistrare. Nu mai este necesar listarea cmpurilor individuale, ci este suficient utilizarea numelui nregistrrii. Se poate reactualiza (UPDATE) o linie a unui tabel utiliznd o nregistrare. Sintaxa SET ROW permite s se reactualizeze ntreaga linie folosind coninutul unei nregistrri. ntr-o nregistrare se poate regsi i returna sau terge informaia din clauza RETURNING a comenzilor UPDATE sau DELETE. Dac n comenzile UPDATE sau DELETE se modific mai multe linii, atunci pot fi utilizate n sintaxa BULK COLLECT INTO, colecii de nregistrri. Exemplu: Exemplul urmtor arat modul n care poate s fie utilizat o nregistrare n clauza RETURNING asociat 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;

Colecii
Uneori este preferabil s fie prelucrate simultan mai multe variabile de acelai tip. Tipurile de date care permit acest lucru sunt coleciile. Fiecare element are un indice unic, care determin poziia sa n colecie. Oracle7 a furnizat tipul index-by table, iniial numit PL/SQL table datorit asemnrii sale cu structura tabelelor relaionale. Oracle8 a introdus dou tipuri colecie, nested table i varray. Oracle9i permite crearea de colecii pe mai multe niveluri, adic colecii de colecii. n PL/SQL exist trei tipuri de colecii: tablouri indexate (index-by tables); tablouri imbricate (nested tables); vectori (varrays sau varying arrays). Tipul index-by table poate fi utilizat numai n declaraii PL/SQL. Tipurile varray i nested table pot fi utilizate att n declaraii PL/SQL, ct i n declaraii la nivelul schemei (de exemplu, pentru definirea tipului unei coloane a unui tabel relaional). Exemplu: n exemplul care urmeaz sunt ilustrate cele trei tipuri de colecii.
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;

Observaii: Deoarece coleciile nu pot fi comparate (egalitate sau inegalitate), ele nu pot s apar n clauzele DISTINCT, GROUP BY, ORDER BY. Tipul colecie poate fi definit ntr-un pachet. Tipul colecie poate s apar n clauza RETURN a unei funcii. Coleciile pot fi parametri formali ntr-un subprogram. Accesul la elementele individuale ale unei colecii se face prin utilizarea unui indice.

Tablouri indexate
Tipul de date index-by table ofer un mecanism pentru prelucrarea tablourilor. Tabloul indexat PL/SQL are dou componente: o coloan ce cuprinde cheia primar pentru acces la liniile tabloului i o coloan care include valoarea efectiv a elementelor tabloului. Oracle7 asigur definirea tablourilor de nregistrri care pot fi declarate i utilizate numai n programe PL/SQL, Oracle8 realizeaz definirea tablourilor de tipuri obiect, iar Oracle9i permite definirea tablourilor de colecii. n Oracle9i tipul index-by table este redenumit associative array pentru compatibilitate (de limbaj) cu termenul folosit n alte limbaje de programare (C++, JavaScript, PHP, Perl) pentru a defini aceast structur de date. Tablourile indexate PL/SQL trebuie definite n doi pai: se definete tipul TABLE; se declar tabloul indexat PL/SQL de acest tip. Declararea tipului TABLE se face respectnd urmtoarea sintax: TYPE nume_tip IS TABLE OF {tip_coloan | variabil%TYPE | nume_tabel.coloan%TYPE [NOT NULL] | nume_tabel%ROWTYPE} INDEX BY tip_indexare; Identificatorul nume_tip este numele noului tip definit care va fi specificat n declararea tabloului PL/SQL, iar tip_coloan este un tip scalar simplu (de exemplu, VARCHAR2, CHAR, DATE sau NUMBER).

Pn la versiunea Oracle9i unicul tip de indexare acceptat era INDEX BY BINARY_INTEGER. Oracle9i permite urmtoarele opiuni pentru tip_indexare: PLS_INTEGER, NATURAL, POSITIVE, VARCHAR2(n) sau chiar indexarea dup un tip declarat cu %TYPE. Nu sunt permise indexrile INDEX BY NUMBER, INDEX BY INTEGER, INDEX BY DATE, INDEX BY VARCHAR2, INDEX BY CHAR(n) sau indexarea dup un tip declarat cu %TYPE n care intervine unul dintre tipurile enumerate anterior. Observaii: Elementele unui tablou indexat nu sunt ntr-o ordine particular i pot fi inserate cu chei arbitrare. Deoarece nu exist constrngeri de dimensiune, dimensiunea tabloului se modific dinamic. Tabloul indexat PL/SQL nu poate fi iniializat n declararea sa. Un tablou indexat neiniializat este vid (nu conine nici valori, nici chei). Un element al tabloului este nedefinit atta timp ct nu are atribuit o valoare efectiv. Iniial, un tablou indexat este nedens. Dup declararea unui tablou se poate face referire la liniile lui prin precizarea valorii cheii primare. Dac se face referire la o linie care nu exist, atunci se produce excepia NO_DATA_FOUND. Dac se dorete contorizarea numrului de linii, trebuie declarat o variabil n acest scop sau poate fi utilizat o metod asociat tabloului. Deoarece numrul de linii nu este limitat, operaia de adugare de linii este restricionat doar de dimensiunea memoriei alocate. Tablourile pot s apar ca argumente ntr-o procedur. Pentru inserarea unor valori din tablourile PL/SQL ntr-o coloan a unui tabel de date se utilizeaz instruciunea INSERT n cadrul unei secvene repetitive LOOP. Asemntor, pentru regsirea unor valori dintr-o coloan a unei baze de date ntr-un tablou PL/SQL se utilizeaz instruciunea FETCH (cursoare) sau instruciunea de atribuire n cadrul unei secvene repetitive LOOP. Pentru a terge liniile unui tablou fie se asigneaz elementelor tabloului valoarea null, fie se declar un alt tablou PL/SQL (de acelai tip) care nu este iniializat i acest tablou vid se asigneaz tabloului PL/SQL care trebuie ters. n PL/SQL 2.3 tergerea liniilor unui tabel se poate face utiliznd metoda DELETE.

Exemplu: S se defineasc un tablou indexat PL/SQL avnd elemente de tipul NUMBER. S se introduc 20 de elemente n acest tablou. S se tearg 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;

n PL/SQL este folosit frecvent tipul tablou de nregistrri. Referirea la un element al tabloului se face prin forma clasic: tabel(index).cmp. Exemplu: S se defineasc un tablou de nregistrri avnd tipul celor din tabelul organizator. S se iniializeze un element al tabloului i s se introduc n tabelul organizator. S se tearg 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; -- sterge toate elementele DBMS_OUTPUT.PUT_LINE('Dupa aplicarea metodei DELETE sunt '||TO_CHAR(org_table.COUNT)||' elemente'); END;

Vectori
Vectorii (varray) sunt structuri asemntoare vectorilor din limbajele C sau Java. Spre deosebire de tablourile indexate, vectorii au o dimensiune maxim (constant) stabilit la declarare. n special, se utilizeaz pentru modelarea relaiilor one-to-many, atunci cnd numrul maxim de elemente din partea many este cunoscut i ordinea elementelor este important. Vectorii reprezint structuri dense. Fiecare element are un index care d poziia sa n vector i care este folosit pentru accesarea elementelor particulare. Limita inferioar a indicelui este 1. Vectorul poate conine un numr variabil de elemente, de la 0 (vid) la numrul maxim specificat obligatoriu n definiia sa. Tipul de date vector este declarat utiliznd sintaxa: TYPE nume_tip IS {VARRAY | VARYING ARRAY} (lungime_maxim) OF tip_elemente [NOT NULL]; Identificatorul nume_tip este numele tipului de date vector, iar lungime_maxim reprezint numrul 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. Exist restricii referitoare la tipul elementelor, n sensul c acesta nu poate s 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; -- adauga un element null 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 cror dimensiune nu este stabilit. Numrul maxim de linii ale unui tablou imbricat este dat de capacitatea maxim 2 GB. Un tablou imbricat este o mulime neordonat de elemente de acelai tip. Valorile de acest tip: -- pot fi stocate n baza de date, -- pot fi prelucrate direct n instruciuni SQL -- au excepii predefinite proprii. Sistemul Oracle nu stocheaz liniile unui tablou imbricat ntr-o ordine particular. Dar, cnd se regsete tabloul n variabile PL/SQL, liniile vor avea indici consecutivi ncepnd cu valoarea 1. Iniial, aceste tablouri sunt structuri dense, dar se poate ca n urma prelucrrii s nu mai aib indici consecutivi. Comanda de declarare a tipului de date tablou imbricat are sintaxa: TYPE nume_tip IS TABLE OF tip_ elemente [NOT NULL]; Identificatorul nume_tip reprezint numele noului tip de date tablou imbricat, iar tip_elemente este tipul fiecrui 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. Exist restricii referitoare la tipul elementelor, n sensul c acesta nu poate s 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. Tabloul imbricat are o singur coloan, iar dac aceasta este de tip obiect, tabloul poate fi vizualizat ca un tabel multicoloan, avnd cte o coloan 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 observ c singura diferen sintactic ntre tablourile indexate i cele imbricate este absena clauzei INDEX BY BINARY_INTEGER. Mai exact, dac aceast clauz lipsete, tipul este tablou imbricat. Observaii: Spre deosebire de tablourile indexate, vectorii i tablourile imbricate pot s apar 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 iniializate i/sau extinse pentru a li se aduga elemente. Cnd este creat un tablou indexat care nu are nc elemente, el este vid. Dac un tablou imbricat (sau un vector) este declarat, dar nu are nc nici un element (nu este iniializat), el este automat iniializat (atomic) null. Adic, colecia este null, nu elementele sale. Prin urmare, pentru tablouri imbricate poate fi utilizat operatorul IS NULL. Dac se ncearc s se adauge un element la un tablou imbricat null, se va genera eroarea ORA - 06531: reference to uninitialized collection care corespunde excepiei predefinite COLLECTION_IS_NULL. Prin urmare, cum poate fi iniializat un tablou imbricat? Ca i obiectele, vectorii i tablourile imbricate sunt iniializate cu ajutorul constructorului. Acesta are acelai nume ca i tipul coleciei referite. PL/SQL apeleaz un constructor numai n mod explicit. Tabelele indexate nu au constructori. Constructorul primete ca argumente o list de valori de tip tip_elemente. Elementele sunt numerotate n ordine, de la 1 la numrul de valori date ca

parametrii constructorului. Dimensiunea iniial a coleciei este egal cu numrul de argumente date n constructor, cnd aceasta este iniializat. Pentru vectori nu poate fi depit dimensiunea maxim precizat la declarare. Atunci cnd constructorul este fr argumente, va crea o colectie fr nici un element (vida), dar care are valoarea not null. Exemplul urmtor este concludent n acest sens. Exemplu:
DECLARE TYPE alfa IS TABLE OF VARCHAR2(50); -- creeaza un tablou (atomic) null tab1 alfa ; /* creeaza un tablou cu un element care este null, dar tabloul nu este null, el este initializat, poate primi elemente */ tab2 alfa := alfa() ; BEGIN IF tab1 IS NULL THEN DBMS_OUTPUT.PUT_LINE('tab1 ELSE DBMS_OUTPUT.PUT_LINE('tab1 END IF; IF tab2 IS NULL THEN DBMS_OUTPUT.PUT_LINE('tab2 ELSE DBMS_OUTPUT.PUT_LINE('tab2 END IF; END;

este NULL'); este NOT NULL');

este NULL'); este NOT NULL');

n urma execuiei acestui bloc se obine urmtorul rezultat: tab1 este NULL tab2 este NOT NULL Excepiile semnificative care apar n cazul utilizrii incorecte a coleciilor:
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 i vectorii pot fi utilizai drept cmpuri n tabelele bazei. Aceasta presupune c fiecare nregistrare din tabelul respectiv conine un obiect de tip colecie. nainte de utilizare, tipul trebuie stocat n dicionarul datelor, deci trebuie declarat prin comanda: CREATE TYPE nume_tip AS {TABLE | VARRAY} OF tip_elemente; Dup crearea tabelului (prin comanda CREATE TABLE), pentru fiecare cmp de tip tablou imbricat din tabel este necesar clauza de stocare: NESTED TABLE nume_cmp STORE AS nume_tabel;

Colecii pe mai multe niveluri


n Oracle9i se pot construi colecii pe mai multe niveluri (multilevel collections), prin urmare colecii ale cror elemente sunt, n mod direct sau indirect, colecii. n felul acesta pot fi definite structuri complexe: vectori 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 relaional, atributelor unui obiect ntr-un tabel obiect, variabilelor PL/SQL. Observaii:

Numrul nivelurilor de imbricare este limitat doar de capacitatea de stocare a sistemului. Pentru a accesa un element al coleciei incluse sunt utilizate dou seturi de paranteze. Obiectele de tipul colecie pe mai multe niveluri nu pot fi comparate. Exemplu: n exemplele care urmeaz sunt definite trei structuri complexe i sunt prezentate cteva modaliti de utilizare ale acestora. Exemplele se refer la vectori pe mai multe niveluri, tablouri imbricate pe mai multe niveluri i 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

tablou al tabloului 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'); var2 beta; var3 delt; var4 alfa; var5 alfa; -- tablou vid 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 element 40 in var2 var2(40) := var5; -- asignez un tablou null var2(40)(3) := 55; -- corect END; /

Prelucrarea coleciilor O colecie poate fi exploatat fie n ntregime (atomic) utiliznd comenzi LMD, fie pot fi prelucrate elemente individuale dintr-o colecie (piecewise updates) utiliznd operatori SQL sau anumite faciliti oferite de PL/SQL. Comanda INSERT permite inserarea unei colecii ntr-o linie a unui tabel. Colecia trebuie s fie creat i iniializat anterior. Comanda UPDATE este folosit pentru modificarea unei colecii stocate. Comanda DELETE poate terge o linie ce conine o colecie. Coleciile din baza de date pot fi regsite 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 nume_galerie info

NUMBER(10), VARCHAR2(20), 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 := 2345; 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 variabil PL/SQL a crei valoare poate fi modificat i apoi reinserat n tabel. Tablourile imbricate depuse n baza de date sunt mai flexibile, deoarece pot fi prelucrate fie n ntregime, fie ca elemente individuale. n fiecare caz pot fi utilizate numai comenzi SQL. Se pot face reactualizri sau inserri asupra tablourilor imbricate care dau o valoare nou pentru ntreaga colecie sau se pot face inserri, tergeri, reactualizri de elemente particulare din colecie. O colecie poate fi asignat altei colecii prin comenzile INSERT, UPDATE, FETCH, SELECT, instruciunea de atribuire sau prin apelul unui subprogram, dar coleciile trebuie s fie de acelai tip. Dac unei colecii i se asigneaz o colecie atomic null, aceasta devine atomic null i trebuie reiniializat. n Oracle8i a fost introdus operatorul TABLE, ce permite prelucrarea elementelor unui tablou imbricat care este stocat ntr-un tabel. Operatorul permite interogarea unei colecii n clauza FROM (la fel ca un tabel).

Operandul lui TABLE este: fie numele unei colecii i atunci rezultatul operatorului este tot o colecie, fie este o subinterogare referitoare la o colecie, iar n acest caz, operatorul TABLE returneaz o singur valoare (coloan) care este un tablou imbricat sau un vector. Prin urmare, lista din clauza SELECT a subcererii trebuie s aib un singur articol. Exemplu: Se presupune c tabelul opera are o coloan info de tip tablou imbricat. Acest tablou are dou componente n care pentru fiecare oper de art sunt depuse numele articolului referitor la opera respectiv i revista n care a aprut. S se insereze o linie n tabelul imbricat. INSERT INTO TABLE (SELECT info FROM opera WHERE titlu = 'Primavara') VALUES ('Pictura moderna', 'Orizonturi'); Listarea codului fiecrei opere de art i a coleciei articolelor referitoare la aceste opere de art se face prin comanda: SELECT FROM a.cod_opera, b.* opera a, TABLE (a.info) b;

Pentru tablouri imbricate pe mai multe niveluri, operaiile LMD pot fi fcute atomic sau pe elemente individuale, iar pentru vectori pe mai multe niveluri, operaiile pot fi fcute numai atomic. Pentru prelucrarea unei colecii locale se poate folosi i operatorul CAST. CAST are forma sintactic: CAST (nume_colecie AS tip_colecie) Operanzii lui CAST sunt o colecie declarat local (de exemplu, ntr-un bloc PL/SQL anonim) i un tip colecie SQL. CAST convertete colecia local la tipul specificat. n felul acesta, o colecie poate fi prelucrat ca i cum ar fi un tabel SQL al bazei de date.

Metodele unei colecii


PL/SQL ofer subprograme numite metode (methods), care opereaz asupra unei colecii. Acestea pot fi apelate numai din comenzi procedurale, i nu din SQL. Metodele sunt apelate prin expresia:

nume_colecie.nume_metod [ (parametri) ] Metodele care se pot aplica coleciilor PL/SQL sunt urmtoarele: COUNT returneaz numrul curent de elemente ale unei colecii PL/SQL; DELETE(n) terge elementul n dintr-o colecie PL/SQL; DELETE(m, n) terge toate elementele avnd indecii ntre m i n; DELETE terge toate elementele unei colecii PL/SQL (nu este valid pentru tipul varrays); EXISTS(n) returneaz TRUE dac exist al n-lea element al unei colecii PL/SQL (altfel, returneaz FALSE, chiar dac elementul este null); FIRST, LAST returneaz indicele primului, respectiv ultimului element din colecie; NEXT(n), PRIOR(n) returneaz indicele elementului urmtor, respectiv precedent celui de rang n din colecie, iar dac nu exist un astfel de element returneaz valoarea null; EXTEND adaug elemente la sfritul unei colecii: EXTEND adaug un element null la sfritul coleciei, EXTEND(n) adaug n elemente null, EXTEND(n, i) adaug n copii ale elementului de rang i (nu este valid pentru tipul index-by tables); nu poate fi utilizat pentru a iniializa o colecie atomic null; LIMIT returneaz numrul maxim de elemente ale unei colecii (cel de la declarare) pentru tipul vector i null pentru tablouri imbricate (nu este valid pentru tipul index-by tables); TRIM terge elementele de la sfritul unei colecii: TRIM terge ultimul element, TRIM(n) terge ultimele n elemente (nu este valid pentru tipul index-by tables). Similar metodei EXTEND, metoda TRIM opereaz asupra dimensiunii interne a tabloului imbricat. EXISTS este singura metod care poate fi aplicat unei colecii atomice null. Orice alt metod declaneaz excepia COLLECTION_IS_NULL.

Bulk bind
n exemplul care urmeaz, comanda DELETE este trimis motorului SQL pentru fiecare iteraie 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

DELETE FROM opera WHERE cod_galerie = alfa (j); END LOOP; END; Pentru a realiza mai rapid aceast operaie, ar trebui s existe posibilitatea de a terge (prelucra) ntreaga colecie i nu elemente individuale. Tehnica care permite acest lucru este cunoscut sub numele bulk bind. n timpul compilrii, compilatorul PL/SQL asociaz identificatorii cu o adres, un tip de date i 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 napoi date motorului PL/SQL (de exemplu, ca rezultat al unei interogri). De multe ori, datele care trebuie manipulate aparin unei colecii, iar colecia este iterat printr-un ciclu FOR. Prin urmare, transferul (n ambele sensuri) ntre SQL i PL/SQL are loc pentru fiecare linie a coleciei. ncepnd cu Oracle8i exist posibilitatea ca toate liniile unei colecii s fie transferate simultan printr-o singur operaie. Procedeul este numit bulk bind i este realizat cu ajutorul comenzii FORALL, ce poate fi folosit cu orice tip de colecie. Comanda FORALL are sintaxa: FORALL index IN lim_inf..lim_sup comanda_sql; Motorul SQL execut comanda_sql o singur dat pentru toate valorile indexului. Comanda_sql este una din comenzile INSERT, UPDATE, DELETE care refer elementele uneia sau mai multor colecii. Variabila index poate fi referit numai n comanda FORALL i numai ca indice de colecie. n exemplul care urmeaz este optimizat problema anterioar, n sensul c instruciunea DELETE este trimis motorului SQL o singur dat, pentru toate liniile coleciei. Exemplu: DECLARE 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 este necesar respectarea urmtoarelor restricii: comanda poate fi folosit numai n programe server-side, altfel apare eroarea this feature is not supported in client-side programs; comenziile INSERT, UPDATE, DELETE trebuie s refere cel puin o colecie; toate elementele coleciei din domeniul precizat trebuie s existe (dac, de exemplu, un element a fost ters, atunci este semnalat o eroare); indicii coleciilor nu pot s fie expresii i trebuie s aib valori continue. Exemplu: CREATE TABLE exemplu (x NUMBER, y NUMBER); DECLARE TYPE nume IS TABLE OF NUMBER; ttt nume:= nume(8,10,12); BEGIN FORALL i IN ttt.FIRST..ttt.LAST INSERT INTO exemplu VALUES(ttt(i), 100); -- corect FORALL i IN 1..3 INSERT INTO exemplu VALUES(7, 9); -- exceptie nu e colectie END; / PL/SQL procedure successfully completed. SQL> select * from exemplu; X Y --------- ---------8 100 10 100 12 100

FORALL i IN gama.FIRST..gama.LAST DELETE FROM carte WHERE codel = gama(i+1); -- eroare dubla (expresie si >LAST) DECLARE TYPE alfa IS TABLE OF NUMBER; xx alfa := alfa(8,10,12); BEGIN FORALL i IN xx.FIRST..xx.LAST DELETE FROM exemplu WHERE x = xx(i); -- eroare END; PL/SQL procedure successfully completed. SQL> select * from exemplu; no rows selected Dac exist o eroare n procesarea unei linii printr-o operaie LMD de tip bulk, numai acea linie va fi rollback. Regsirea rezultatului unei interogri n colecii (nainte de a fi trimis motorului PL/SQL) se poate obine cu ajutorul clauzei BULK COLLECT. Clauza poate s apar n: comenzile SELECT INTO (cursoare implicite), comenzile FETCH INTO (cursoare explicite), clauza RETURNING INTO a comenzilor INSERT, UPDATE, DELETE. Clauza are urmtoarea sintax: BULK COLLECT INTO nume_colecie [,nume_colecie]

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 */ DELETE FROM opera WHERE stare = 'buna' RETURNING cod_opera BULK COLLECT INTO alfa; END; Acelasi exemplu pentru tabelul exemplu creat mai sus. Inseram sase inregistrari (toate inreg. au fost sterse prin exemplul de mai sus) DECLARE TYPE nume IS TABLE OF NUMBER; ttt nume:= nume(8,10,12,14,12,8,12); BEGIN FORALL i IN ttt.FIRST..ttt.LAST INSERT INTO exemplu VALUES(ttt(i), 100); end; / PL/SQL procedure successfully completed. SQL> select * from exemplu; X Y ---------- ---------8 100 10 100 12 100 14 100 12 100

8 12 7 rows selected.

100 100

SQL> DECLARE 2 TYPE tip1 IS TABLE OF exemplu.x%TYPE; 3 alfa tip1; 4 BEGIN 5 SELECT x BULK COLLECT INTO alfa 6 FROM exemplu; 7 DELETE FROM exemplu WHERE x=12 8 RETURNING x BULK COLLECT INTO alfa; 9 For I in alfa.first .. alfa.last loop 10 Dbms_output.put_line('alfa('||I || ') alfa(i)); 11 End loop; 12 END; 13 / alfa(1) = 12 alfa(2) = 12 alfa(3) = 12 PL/SQL procedure successfully completed.

'

||

Comanda FORALL se poate combina cu clauza BULK COLLECT. Totui, trebuie subliniat c ele nu pot fi folosite simultan n comanda SELECT. Motorul SQL incarca toate liniile unei coloane. Cum se poate limita numarul de linii procesate? SQL> 2 3 4 5 6 7 8 9 10 11 DECLARE TYPE alfa IS TABLE OF employees.salary%TYPE; xx alfa; BEGIN SELECT salary BULK COLLECT INTO xx FROM employees WHERE ROWNUM <= 25; For I in xx.first .. xx.last LOOP DBMS_OUTPUT.PUT_LINE('XX('||I||')=' || xx(i)); End loop; END; /

XX(1)=24000 XX(2)=17000 XX(3)=17000 XX(4)=9000 XX(5)=6000 XX(6)=4800 XX(7)=4800 XX(8)=4200 XX(9)=12000 XX(10)=9000 XX(11)=8200 XX(12)=7700 XX(13)=7800 XX(14)=6900 XX(15)=11000 XX(16)=3100 XX(17)=2900 XX(18)=2800 XX(19)=2600 XX(20)=2500 XX(21)=8000 XX(22)=8200 XX(23)=7900 XX(24)=6500 XX(25)=5800 PL/SQL procedure successfully completed.

4.

Gestiunea cursoarelor n PL/SQL

Sistemul Oracle folosete, pentru a procesa o comand SQL, o zon de memorie cunoscut sub numele de zon context (context area). Cnd este procesat o instruciune SQL, server-ul Oracle deschide aceast zon de memorie n care comanda este analizat sintactic i este executat. Zona conine informaii necesare procesrii comenzii, cum ar fi: numrul de rnduri procesate de instruciune; un pointer ctre reprezentarea intern a comenzii; n cazul unei cereri, mulimea rndurilor rezultate n urma execuiei acestei comenzi (active set). Un cursor este un pointer la aceast zon context. Prin intermediul cursoarelor, un program PL/SQL poate controla zona context i transformrile petrecute n urma procesrii comenzii. Exist dou tipuri de cursoare: implicite, generate de server-ul Oracle cnd n partea executabil a unui bloc PL/SQL apare o instruciune SQL; explicite, declarate i definite de ctre utilizator atunci cnd o cerere (SELECT), care apare ntr-un bloc PL/SQL, ntoarce mai multe linii ca rezultat. Att cursoarele implicite ct i cele explicite au o serie de atribute ale cror valori pot fi folosite n expresii. Lista atributelor este urmtoarea: %ROWCOUNT, care este de tip ntreg i reprezint numrul liniilor ncrcate de cursor; %FOUND, care este de tip boolean i ia valoarea TRUE dac ultima operaie de ncrcare (FETCH) dintr-un cursor a avut succes (n cazul cursoarelor explicite) sau dac instruciunea SQL a ntors cel puin o linie (n cazul cursoarelor implicite); %NOTFOUND, care este de tip boolean i are semnificaie opus fa de cea a atributului %FOUND; %ISOPEN, care este de tip boolean i indic dac un cursor este deschis (n cazul cursoarelor implicite, acest atribut are ntotdeauna valoarea FALSE, deoarece un cursor implicit este nchis de sistem imediat dup executarea instruciunii 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 s apar n comenzi PL/SQL, n funcii, n seciunea de tratare a erorilor, dar nu pot fi utilizate n comenzi SQL.

Cursoare implicite
Cnd se proceseaz o comand LMD, motorul SQL deschide un cursor implicit. Atributele scalare ale cursorului implicit (SQL%ROWCOUNT, SQL%FOUND, SQL%NOTFOUND, SQL%ISOPEN) furnizeaz informaii referitoare la ultima comand INSERT, UPDATE, DELETE sau SELECT INTO executat. nainte ca Oracle s deschid 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) conine numrul de linii procesate de a j-a execuie a unei comenzi INSERT, DELETE sau UPDATE. Dac a j-a execuie nu afecteaz nici o linie, atunci atributul returneaz valoarea 0. Comanda FORALL i atributul %BULK_ROWCOUNT au aceiai indici, deci folosesc acelai domeniu. Dac %BULK_ROWCOUNT(j) este zero, atributul %FOUND este FALSE. Exemplu: n exemplul care urmeaz, comanda FORALL insereaz un numr arbitrar de linii la fiecare iteraie, iar dup fiecare iteraie atributul %BULK_ROWCOUNT returneaz numrul 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 urmtoarele etape: declararea cursorului (atribuirea unui nume i asocierea cu o comand SELECT); deschiderea cursorului pentru cerere (executarea interogrii asociate i determinarea mulimii 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 seciunea declarativ a programului, trebuie deschis n partea executabil, urmnd s fie utilizat apoi pentru extragerea datelor. Dac nu mai este necesar n restul programului, cursorul trebuie s fie nchis. DECLARE declarare cursor BEGIN deschidere cursor (OPEN) WHILE rmn 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 i CLOSE.

Declararea unui cursor explicit Prin declaraia CURSOR n cadrul comenzii DECLARE este definit un cursor explicit i este precizat structura cererii care va fi asociat acestuia. Declaraia CURSOR are urmtoarea form sintactic: CURSOR nume_cursor IS comanda_select

Identificatorul nume_cursor este numele cursorului, iar comanda_select este cererea SELECT care va fi procesat. Observaii: Comanda SELECT care apare n declararea cursorului, nu trebuie s includ clauza INTO. Dac se cere procesarea liniilor ntr-o anumit ordine, atunci n cerere este utilizat clauza ORDER BY. Variabilele care sunt referite n comanda de selectare trebuie declarate naintea comenzii CURSOR. Ele sunt considerate variabile de legtur. Dac n lista comenzii SELECT apare o expresie, atunci pentru expresia respectiv 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 s apar ntr-o expresie i cruia nu i se poate atribui o valoare. Deschiderea unui cursor explicit Comanda OPEN execut cererea asociat cursorului, identific mulimea liniilor rezultat i poziioneaz cursorul naintea primei linii. Deschiderea unui cursor se face prin comanda: OPEN nume_cursor; Identificatorul nume_cursor reprezint numele cursorului ce va fi deschis. La deschiderea unui cursor se realizeaz urmtoarele operaii: se evalueaz cererea asociat (sunt examinate valorile variabilelor de legtur ce apar n declaraia cursorului); este determinat mulimea rezultat (active set) prin executarea cererii SELECT, avnd n vedere valorile de la pasul anterior; pointer-ul este poziionat la prima linie din mulimea activ. ncrcarea datelor dintr-un cursor explicit Comanda FETCH regsete liniile rezultatului din mulimea activ. FETCH realizeaz urmtoarele operaii: avanseaz pointer-ul la urmtoarea linie n mulimea activ (pointer-ul poate avea doar un sens de deplasare de la prima spre ultima nregistrare); citete datele liniei curente n variabile PL/SQL; dac pointer-ul este poziionat la sfritul mulimii active atunci se iese din bucla cursorului. Comanda FETCH are urmtoarea sintax:

FETCH nume_cursor INTO {nume_variabil [, nume_variabil] | nume_nregistrare}; Identificatorul nume_cursor reprezint numele unui cursor declarat i deschis anterior. Variabila sau lista de variabile din clauza INTO trebuie s fie compatibil (ca ordine i tip) cu lista selectat din cererea asociat cursorului. La un moment dat, comanda FETCH regsete o singur linie. Totui, n ultimele versiuni Oracle pot fi ncrcate mai multe linii (la un moment dat) ntr-o colecie, utiliznd clauza BULK COLLECT. Exemplu: n exemplul care urmeaz se ncarc date dintr-un cursor n dou colecii.
DECLARE TYPE TYPE cod1 titlu1 CURSOR ccopera IS TABLE OF opera.cod_opera%TYPE; ctopera IS TABLE OF opera.titlu%TYPE; ccopera; ctopera; 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 Dup ce a fost procesat mulimea activ, cursorul trebuie nchis. Prin aceast operaie, PL/SQL este informat c programul a terminat folosirea cursorului i resursele asociate acestuia pot fi eliberate. Aceste resurse includ spaiul utilizat pentru memorarea mulimii active i spaiul temporar folosit pentru determinarea mulimii active. Cursorul va fi nchis prin comanda CLOSE, care are urmtoarea sintax: CLOSE nume_cursor; Identificatorul nume_cursor este numele unui cursor deschis anterior. Pentru a reutiliza cursorul este suficient ca acesta s fie redeschis. Dac se ncearc ncrcarea datelor dintr-un cursor nchis, atunci apare excepia

INVALID_CURSOR. Un bloc PL/SQL poate s se termine fr a nchide cursoarele, dar acest lucru nu este indicat, deoarece este bine ca resursele s fie eliberate. Exemplu: Pentru toi artitii care au opere de art expuse n muzeu s se insereze n tabelul temp informaii referitoare la numele acestora i anul naterii.
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 urmtorul tabel:


%FOUND %ISOPEN %NOTFOUND %ROWCOUNT

OPEN

nainte Dup nainte Dup nainte Dup nainte Dup nainte Dup

Excepie Null Null True True True True False False Excepie

False True True True True True True True True False

Excepie Null Null False False False False True True Excepie

Excepie 0 0 1 1 Depinde de date Depinde de date Depinde de date Depinde de date Excepie

Prima ncrcare Urmtoarea ncrcare Ultima ncrcare CLOSE

Dup prima ncrcare, dac mulimea rezultat este vid, %FOUND va fi FALSE, %NOTFOUND va fi TRUE, iar %ROWCOUNT este 0. ntr-un pachet poate fi separat specificarea unui cursor de corpul acestuia. Cursorul va fi declarat n specificaia pachetului prin comanda: CURSOR nume_cursor [ (parametru [, parametru]) ] RETURN tip_returnat; n felul acesta va crete flexibilitatea programului, putnd fi modificat doar corpul cursorului, fr a schimba specificaia. 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 folosete operaia de ciclare (LOOP, WHILE, FOR), prin care la fiecare iteraie se va ncrca o nou linie. Comanda EXIT poate fi utilizat pentru ieirea din ciclu, iar valoarea atributului %ROWCOUNT pentru terminarea ciclului. Procesarea liniilor unui cursor explicit se poate realiza i cu ajutorul unui ciclu FOR special, numit ciclu cursor. Pentru acest ciclu este necesar doar declararea cursorului, operaiile de deschidere, ncrcare i nchidere ale acestuia fiind implicite. Comanda are urmtoarea sintax: FOR nume_nregistrare IN nume_cursor LOOP secven_de_instruciuni; END LOOP; Variabila nume_nregistrare (care controleaz ciclul) nu trebuie declarat. Domeniul ei este doar ciclul respectiv. Pot fi utilizate cicluri cursor speciale care folosesc subcereri, iar n acest caz

nu mai este necesar nici declararea cursorului. Exemplul care urmeaz este concludent n acest sens. Exemplu: S se calculeze, utiliznd un ciclu cursor cu subcereri, valoarea operelor de art expuse ntr-o galerie al crei cod este introdus de la tastatur. De asemenea, s se obin media valorilor operelor de art expuse n galeria respectiv.
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 implicit 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 comand SELECT, care nu poate fi schimbat pe parcursul programului. Pentru a putea lucra cu nite cursoare ale cror comenzi SELECT ataate depind de parametri ce pot fi modificai la momentul execuiei, n PL/SQL s-a introdus noiunea de cursor parametrizat. Prin urmare, un cursor parametrizat este un cursor n care comanda SELECT ataat depinde de unul sau mai muli parametri. Transmiterea de parametri unui cursor parametrizat se face n mod similar procedurilor stocate. Un astfel de cursor este mult mai uor de interpretat i de ntreinut, oferind i posibilitatea reutilizrii sale n blocul PL/SQL. Declararea unui astfel de cursor se face respectnd urmtoarea sintax: CURSOR nume_cursor [ (nume_parametru[, nume_parametru ] ) ] [RETURN tip_returnat] IS comanda_select; Identificatorul comanda_select este o instruciune SELECT fr clauza INTO, tip_returnat reprezint un tip nregistrare sau linie de tabel, iar nume_parametru are sintaxa: nume_parametru [IN] tip_parametru [ {:= | DEFAULT} expresie] n aceast declaraie, atributul tip_parametru reprezint tipul parametrului, care este un tip scalar. Parametrii formali sunt de tip IN i, prin urmare, nu pot returna valori parametrilor actuali. Ei nu suport constrngerea NOT NULL. Deschiderea unui astfel de cursor se face asemntor apelului unei funcii, specificnd lista parametrilor actuali ai cursorului. n determinarea mulimii 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 specificai similar celor de la subprograme. Asocierea dintre parametrii formali i cei actuali se face prin: poziie parametrii formali i actuali sunt separai prin virgul; nume parametrii actuali sunt aranjai ntr-o ordine arbitrar, dar cu o coresponden de forma parametru formal => parametru actual. Dac n definiia cursorului, toi parametrii au valori implicite (DEFAULT), cursorul poate fi deschis fr a specifica vreun parametru. Exemplu: Utiliznd un cursor parametrizat s se obin codurile operelor de art din

fiecare sal, identificatorul slii i al galeriei. Rezultatele s 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; END LOOP; CLOSE sala_cursor; COMMIT; END;

Cursoare SELECT FOR UPDATE


Uneori este necesar blocarea liniilor nainte ca acestea s fie terse sau reactualizate. Blocarea se poate realiza (atunci cnd cursorul este deschis) cu ajutorul comenzii SELECT care conine 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 list ce include cmpurile tabelului care vor fi modificate. Atributul NOWAIT returneaz o eroare dac liniile sunt deja blocate de alt sesiune. Liniile unui tabel sunt blocate doar dac clauza FOR UPDATE se refer la coloane ale tabelului respectiv. n momentul deschiderii unui astfel de cursor, liniile corespunztoare mulimii active, determinate de clauza SELECT, sunt blocate pentru operaii de scriere (reactualizare sau tergere). n felul acesta este realizat consistena la citire a sistemului. De exemplu, aceast situaie este util cnd se reactualizeaz o valoare a unei linii i trebuie avut sigurana c linia nu este schimbat de alt utilizator naintea reactualizrii. Prin urmare, alte sesiuni nu pot schimba liniile din mulimea activ pn cnd tranzacia nu este permanentizat sau anulat. Dac alt sesiune a blocat deja liniile din mulimea activ, atunci comanda SELECT FOR UPDATE va atepta (sau nu) ca aceste blocri s fie eliberate. Pentru a trata aceast situaie se utilizeaz clauza WAIT, respectiv NOWAIT. n Oracle9i este utilizat sintaxa: SELECT FROM FOR UPDATE [OF lista_campuri] [ {WAIT n | NOWAIT} ]; Valoarea lui n reprezint numrul de secunde de ateptare. Dac liniile nu sunt deblocate n n secunde, atunci se declaneaz eroarea ORA-30006, respectiv eroarea ORA-00054, dup cum este specificat clauza WAIT, respectiv NOWAIT. Dac nu este specificat nici una din clauzele WAIT sau NOWAIT, sistemul ateapt pn ce linia este deblocat i atunci returneaz rezultatul comenzii SELECT. Dac un cursor este declarat cu clauza FOR UPDATE, atunci comenzile DELETE i UPDATE corespunztoare trebuie s conin clauza WHERE CURRENT OF nume_cursor. Aceast clauz refer linia curent care a fost gsit de cursor, permind ca reactualizrile i tergerile s se efectueze asupra acestei linii, fr referirea explicit a cheii primare sau pseudocoloanei ROWID. De subliniat c instruciunile UPDATE i DELETE vor reactualiza numai coloanele listate n clauza FOR UPDATE. Pseudocoloana ROWID poate fi utilizat dac tabelul referit n interogare nu are o cheie primar specificat. ROWID-ul fiecrei linii poate fi ncrcat ntr-o variabil PL/SQL (declarat de tipul ROWID sau UROWID), iar aceast variabil poate fi utilizat n clauza WHERE (WHERE ROWID = v_rowid). Dup nchiderea cursorului este necesar comanda COMMIT pentru a realiza scrierea efectiv a modificrilor, deoarece cursorul lucreaz doar cu nite copii ale liniilor reale existente n tabele.

Deoarece blocrile implicate de clauza FOR UPDATE vor fi eliberate de comanda COMMIT, nu este recomandat utilizarea comenzii COMMIT n interiorul ciclului n care se fac ncrcri de date. Orice FETCH executat dup COMMIT va eua. n cazul n care cursorul nu este definit prin SELECTFOR UPDATE, nu sunt probleme n acest sens i, prin urmare, n interiorul ciclului unde se fac schimbri ale datelor poate fi utilizat un COMMIT. Exemplu: S se dubleze valoarea operelor de art pictate pe pnz care au fost achiziionate nainte de 1 ianuarie 1956.
DECLARE CURSOR calc IS SELECT * FROM opera WHERE material = 'panza' AND data_achizitie <= TO_DATE('01-JAN-56','DD-MONYY') 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 refer la cursoare statice. Unui cursor static i se asociaz o comand SQL care este cunoscut n momentul n care blocul este compilat. n PL/SQL a fost introdus variabila cursor, care este de tip referin. Variabilele cursor sunt similare tipului pointer din limbajele C sau Pascal. Prin urmare, un cursor este un obiect static, iar un cursor dinamic este un pointer la un cursor. n momentul declarrii, variabilele cursor nu solicit o comand SQL asociat. n acest fel, diferite comenzi SQL pot fi asociate variabilelor cursor, la diferite momente de timp. Acest tip de variabil trebuie declarat, deschis, ncrcat i nchis n mod similar unui cursor static. Variabilele cursor sunt dinamice deoarece li se pot asocia diferite interogri atta timp ct coloanele returnate de fiecare interogare corespund declaraiei

variabilei cursor. Aceste variabile sunt utile n transmiterea seturilor de rezultate ntre subprograme PL/SQL stocate i diferii clieni. De exemplu, un client OCI, o aplicaie Oracle Forms i server-ul Oracle pot referi aceeai zon de lucru (care conine mulimea rezultat). Pentru a reduce traficul n reea, o variabil cursor poate fi declarat pe staia client, deschis i se pot ncrca date din ea pe server, apoi poate continua ncrcarea, dar de pe staia client etc. Pentru a crea o variabil cursor este necesar definirea unui tip REF CURSOR, urmnd apoi declararea unei variabile de tipul respectiv. Dup ce variabila cursor a fost declarat, ea poate fi deschis pentru orice cerere SQL care returneaz date de tipul declarat. Sintaxa pentru declararea variabilei cursor este urmtoarea: TYPE tip_ref_cursor IS REF CURSOR [RETURN tip_returnat]; var_cursor tip_ref_cursor; Identificatorul var_cursor este numele variabilei cursor, tip_ref_cursor este un nou tip de dat ce poate fi utilizat n declaraiile urmtoare 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 ctre orice cursor asociat variabilelor cursor de tipul definit. Dac lipsete clauza RETURN, cursorul poate fi deschis pentru orice cerere SELECT. Dac variabila cursor apare ca parametru ntr-un subprogram, atunci trebuie specificat tipul parametrului (tipul REF CURSOR) i forma acestuia (IN sau IN OUT). Exist anumite restricii referitoare la utilizarea variabilelor cursor: nu pot fi declarate ntr-un pachet; cererea asociat variabilei cursor nu poate include clauza FOR UPDATE (restricia dispare n Oracle9i); nu poate fi asignat 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 utilizai 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 colecii (varray, nested table); nu pot fi folosite cu SQL dinamic n Pro*C/C++. n cazul variabilelor cursor, instruciunile de deschidere (OPEN), ncrcare (FETCH), nchidere (CLOSE) vor avea o sintax similar celor comentate anterior.

Comanda OPENFOR asociaz o variabil cursor cu o cerere multilinie, execut cererea, identific mulimea rezultat i poziioneaz cursorul la prima linie din mulimea rezultat. Sintaxa comenzii este: OPEN {variabila_cursor | :variabila_cursor_host} FOR {cerere_select | ir_dinamic [USING argument_bind [, argument_bind ] ] }; Identificatorul variabila_cursor specific o variabil cursor declarat anterior, dar fr opiunea RETURN tip, cerere_select este interogarea pentru care este deschis variabila cursor, iar ir_dinamic este o secven de caractere care reprezint cererea multilinie. Opiunea ir_dinamic este specific prelucrrii dinamice a comenzilor, iar posibilitile oferite de SQL dinamic vor fi analizate ntr-un capitol separat. Identificatorul :variabila_cursor_host reprezint o variabil cursor declarat ntrun mediu gazd PL/SQL (de exemplu, un program OCI). Comanda OPEN - FOR poate deschide acelai cursor pentru diferite cereri. Nu este necesar nchiderea variabilei cursor nainte de a o redeschide. Dac se redeschide variabila cursor pentru o nou cerere, cererea anterioar este pierdut. 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>2000; ELSIF alege = 3 THEN OPEN ope_var FOR SELECT * FROM opera WHERE valoare=7777; END IF; END deschis_ope; END alfa; Exemplu:

n urmtorul exemplu se declar o variabil cursor care se asociaz unei comenzi SELECT (SQL dinamic) ce returneaz anumite linii din tabelul opera. DECLARE TYPE operaref IS REF CURSOR; opera_var operaref; mm_val INTEGER := 100000; BEGIN OPEN opera_var FOR 'SELECT cod_opera,valoare FROM opera WHERE valoare> :vv' USING mm_val; END; Comanda FETCH returneaz o linie din mulimea rezultat a cererii multilinie, atribuie valori componentelor din lista cererii prin clauza INTO, avanseaz cursorul la urmtoarea linie. Sintaxa comenzii este: FETCH {variabila_cursor | :variabila_cursor_host} INTO {variabila [, variabila] | nregistrare} [BULK COLLECT INTO {nume_colecie [, nume_colecie]} | {nume_array_host [, nume_array_host]} [LIMIT expresie_numerica]]; Clauza BULK COLLECT permite ncrcarea tuturor liniilor simultan n una sau mai multe colecii. Atributul nume_colecie indic o colecie declarat anterior, n care sunt depuse valorile respective, iar nume_array_host identific un vector declarat ntr-un mediu gazd PL/SQL i trimis lui PL/SQL ca variabil de legtur. Prin clauza LIMIT se limiteaz numrul liniilor ncrcate 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 alfa FOR SELECT titlu, valoare FROM opera; FETCH var1 BULK COLLECT INTO var2, var3;

CLOSE var1; END; Comanda CLOSE dezactiveaz variabila cursor precizat. Ea are sintaxa: CLOSE {variabila_cursor | :variabila_cursor_host} Cursoarele i variabilele cursor nu sunt interoperabile. Nu poate fi folosit una din ele, cnd este ateptat cealalt. Urmtoarea secven este incorect.
DECLARE TYPE beta IS REF CURSOR RETURN opera%ROWTYPE; gama beta; BEGIN FOR k IN gama LOOP --nu este corect! END;

Expresie cursor
n Oracle9i a fost introdus conceptul de expresie cursor (cursor expression), care returneaz un cursor imbricat (nested cursor). Expresia cursor are urmtoarea sintax: CURSOR (subcerere) Fiecare linie din mulimea rezultat poate conine valori uzuale i cursoare generate de subcereri. PL/SQL accept cereri care au expresii cursor n cadrul unei declaraii cursor, declaraii REF CURSOR i a variabilelor cursor. Prin urmare, expresia cursor poate s apar ntr-o comand SELECT ce este utilizat 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 ncrcat automat atunci cnd liniile care l conin sunt ncrcate din cursorul printe. El este nchis dac: este nchis explicit de ctre utilizator; cursorul printe este reexecutat, nchis sau anulat; apare o eroare n timpul unei ncrcri din cursorul printe. Exist cteva restricii asupra folosirii unei expresii cursor: nu poate fi utilizat cu un cursor implicit; poate s apar numai ntr-o comand SELECT care nu este imbricat n alt cerere (exceptnd cazul n care este o subcerere chiar a expresiei cursor) sau ca argument pentru funcii tabel, n clauza FROM a lui

SELECT; nu poate s apar n interogarea ce definete o vizualizare; nu se pot efectua operaii BIND sau EXECUTE cu aceste expresii. Exemplu: S se defineasc un cursor care furnizeaz codurile operelor expuse n cadrul unei expoziii avnd un cod specificat (val_cod) i care se desfoar ntr-o localitate precizat (val_oras). S se afieze data cnd a avut loc vernisajul acestei expoziii. n acest caz cursorul returneaz dou coloane, cea de-a doua coloan 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:

S se listeze numele galeriilor din muzeu i pentru fiecare galerie s se afieze numele slilor din galeria respectiv. Sunt prezentate dou variante de rezolvare. Prima variant reprezint o implementare simpl utiliznd programarea secvenial clasic, iar a doua utilizeaz 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; 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;

4. Gestiunea cursoarelor n PL/SQL


Sistemul Oracle folosete, pentru a procesa o comand SQL, o zon de memorie cunoscut sub numele de zon context (context area). Cnd este procesat o instruciune SQL, server-ul Oracle deschide aceast zon de memorie n care comanda este analizat sintactic i este executat. Zona conine informaii necesare procesrii comenzii, cum ar fi: numrul de rnduri procesate de instruciune; un pointer ctre reprezentarea intern a comenzii; n cazul unei cereri, mulimea rndurilor rezultate n urma execuiei acestei comenzi (active set). Un cursor este un pointer la aceast zon context. Prin intermediul cursoarelor, un program PL/SQL poate controla zona context i transformrile petrecute n urma procesrii comenzii. Exist dou tipuri de cursoare: implicite, generate de server-ul Oracle cnd n partea executabil a unui bloc PL/SQL apare o instruciune SQL; explicite, declarate i definite de ctre utilizator atunci cnd o cerere (SELECT), care apare ntr-un bloc PL/SQL, ntoarce mai multe linii ca rezultat. Att cursoarele implicite ct i cele explicite au o serie de atribute ale cror valori pot fi folosite n expresii. Lista atributelor este urmtoarea: %ROWCOUNT, care este de tip ntreg i reprezint numrul liniilor ncrcate de cursor; %FOUND, care este de tip boolean i ia valoarea TRUE dac ultima operaie de ncrcare (FETCH) dintr-un cursor a avut succes (n cazul cursoarelor explicite) sau dac instruciunea SQL a ntors cel puin o linie (n cazul cursoarelor implicite); %NOTFOUND, care este de tip boolean i are semnificaie opus fa de cea a atributului %FOUND; %ISOPEN, care este de tip boolean i indic dac un cursor este deschis (n cazul cursoarelor implicite, acest atribut are ntotdeauna valoarea FALSE, deoarece un cursor implicit este nchis de sistem imediat dup executarea instruciunii 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 s apar n comenzi PL/SQL, n funcii, n seciunea de tratare a erorilor, dar nu pot fi utilizate n comenzi SQL.

Cursoare implicite
Cnd se proceseaz o comand LMD, motorul SQL deschide un cursor implicit. Atributele scalare ale cursorului implicit (SQL%ROWCOUNT, SQL%FOUND, SQL%NOTFOUND, SQL%ISOPEN) furnizeaz informaii referitoare la ultima comand INSERT, UPDATE, DELETE sau SELECT INTO executat. nainte ca Oracle s deschid 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) conine numrul de linii procesate de a j-a execuie a unei comenzi INSERT, DELETE sau UPDATE. Dac a j-a execuie nu afecteaz nici o linie, atunci atributul returneaz valoarea 0. Comanda FORALL i atributul %BULK_ROWCOUNT au aceiai indici, deci folosesc acelai domeniu. Dac %BULK_ROWCOUNT(j) este zero, atributul %FOUND este FALSE. Exemplu: n exemplul care urmeaz, comanda FORALL insereaz un numr arbitrar de linii la fiecare iteraie, iar dup fiecare iteraie atributul %BULK_ROWCOUNT returneaz numrul 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 Exemplu: Sa se creeze tabelul JOB_ANG cu cimputile employee_id si job_id. Sa se numere citi angajati sunt pe fiecare job si citi angajati in total. CREATE TABLE JOB_ANG (EMPLOYEE_ID NUMBER(6) NOT NULL , JOB_ID VARCHAR2(10) NOT NULL ); SQL> CREATE TABLE JOB_ANG 2 (EMPLOYEE_ID NUMBER(6) NOT NULL , 3 JOB_ID VARCHAR2(10) NOT NULL );
Table created.

SET SERVEROUTPUT ON DECLARE TYPE alfa IS TABLE OF JOBS.JOB_ID%TYPE; beta alfa; BEGIN SELECT JOB_ID BULK COLLECT INTO beta FROM JOBS; FORALL j IN 1..beta.COUNT INSERT INTO JOB_ANG SELECT EMPLOYEE_ID, JOB_ID FROM EMPLOYEES WHERE JOB_ID = beta(j); FOR j IN 1..beta.COUNT LOOP DBMS_OUTPUT.PUT_LINE ('Pentru jobul ' || beta(j) || ' au fost inserate ' || SQL%BULK_ROWCOUNT(j) || ' inregistrari (angajati)'); END LOOP; DBMS_OUTPUT.PUT_LINE ('Numarul total de inregistrari inserate este ' || SQL%ROWCOUNT); END; / Pentru jobul AD_PRES au fost inserate 1 inregistrari (angajati) Pentru jobul AD_VP au fost inserate 2 inregistrari (angajati) Pentru jobul AD_ASST au fost inserate 1 inregistrari (angajati) Pentru jobul FI_MGR au fost inserate 1 inregistrari (angajati)

Pentru jobul FI_ACCOUNT au fost inserate 5 inregistrari (angajati) Pentru jobul AC_MGR au fost inserate 1 inregistrari (angajati) Pentru jobul AC_ACCOUNT au fost inserate 1 inregistrari (angajati) Pentru jobul SA_MAN au fost inserate 5 inregistrari (angajati) Pentru jobul SA_REP au fost inserate 30 inregistrari (angajati) Pentru jobul PU_MAN au fost inserate 1 inregistrari (angajati) Pentru jobul PU_CLERK au fost inserate 5 inregistrari (angajati) Pentru jobul ST_MAN au fost inserate 5 inregistrari (angajati) Pentru jobul ST_CLERK au fost inserate 20 inregistrari (angajati) Pentru jobul SH_CLERK au fost inserate 20 inregistrari (angajati) Pentru jobul IT_PROG au fost inserate 5 inregistrari (angajati) Pentru jobul MK_MAN au fost inserate 1 inregistrari (angajati) Pentru jobul MK_REP au fost inserate 1 inregistrari (angajati) Pentru jobul HR_REP au fost inserate 1 inregistrari (angajati) Pentru jobul PR_REP au fost inserate 1 inregistrari (angajati) Numarul total de inregistrari inserate este 107 PL/SQL procedure successfully completed. Cursoare explicite Pentru gestiunea cursoarelor explicite sunt necesare urmtoarele etape: declararea cursorului (atribuirea unui nume i asocierea cu o comand SELECT); deschiderea cursorului pentru cerere (executarea interogrii asociate i determinarea mulimii 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 seciunea declarativ a programului, trebuie deschis n partea executabil, urmnd s fie utilizat apoi pentru extragerea datelor. Dac nu mai este necesar n restul programului, cursorul trebuie s fie nchis. DECLARE declarare cursor BEGIN deschidere cursor (OPEN) WHILE rmn 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 i CLOSE. Declararea unui cursor explicit Prin declaraia CURSOR n cadrul comenzii DECLARE este definit un cursor explicit i este precizat structura cererii care va fi asociat acestuia. Declaraia CURSOR are urmtoarea form sintactic: CURSOR nume_cursor IS comanda_select Identificatorul nume_cursor este numele cursorului, iar comanda_select este cererea SELECT care va fi procesat. Observaii: Comanda SELECT care apare n declararea cursorului, nu trebuie s includ clauza INTO. Dac se cere procesarea liniilor ntr-o anumit ordine, atunci n cerere este utilizat clauza ORDER BY. Variabilele care sunt referite n comanda de selectare trebuie declarate naintea comenzii CURSOR. Ele sunt considerate variabile de legtur. Dac n lista comenzii SELECT apare o expresie, atunci pentru expresia respectiv 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 s apar ntr-o expresie i cruia nu i se poate atribui o valoare. Deschiderea unui cursor explicit Comanda OPEN execut cererea asociat cursorului, identific mulimea liniilor rezultat i poziioneaz cursorul naintea primei linii. Deschiderea unui cursor se face prin comanda: OPEN nume_cursor; Identificatorul nume_cursor reprezint numele cursorului ce va fi deschis. La deschiderea unui cursor se realizeaz urmtoarele operaii: se evalueaz cererea asociat (sunt examinate valorile variabilelor de legtur ce apar n declaraia cursorului); este determinat mulimea rezultat (active set) prin executarea cererii

SELECT, avnd n vedere valorile de la pasul anterior; pointer-ul este poziionat la prima linie din mulimea activ.

ncrcarea datelor dintr-un cursor explicit Comanda FETCH regsete liniile rezultatului din mulimea activ. FETCH realizeaz urmtoarele operaii: avanseaz pointer-ul la urmtoarea linie n mulimea activ (pointer-ul poate avea doar un sens de deplasare de la prima spre ultima nregistrare); citete datele liniei curente n variabile PL/SQL; dac pointer-ul este poziionat la sfritul mulimii active atunci se iese din bucla cursorului. Comanda FETCH are urmtoarea sintax: FETCH nume_cursor INTO {nume_variabil [, nume_variabil] | nume_nregistrare}; Identificatorul nume_cursor reprezint numele unui cursor declarat i deschis anterior. Variabila sau lista de variabile din clauza INTO trebuie s fie compatibil (ca ordine i tip) cu lista selectat din cererea asociat cursorului. La un moment dat, comanda FETCH regsete o singur linie. Totui, n ultimele versiuni Oracle pot fi ncrcate mai multe linii (la un moment dat) ntr-o colecie, utiliznd clauza BULK COLLECT. Exemplu: n exemplul care urmeaz se ncarc date dintr-un cursor n dou colecii.
DECLARE TYPE TYPE cod1 titlu1 CURSOR ccopera IS TABLE OF opera.cod_opera%TYPE; ctopera IS TABLE OF opera.titlu%TYPE; ccopera; ctopera; 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 Dup ce a fost procesat mulimea activ, cursorul trebuie nchis. Prin aceast operaie, PL/SQL este informat c programul a terminat folosirea cursorului i resursele asociate acestuia pot fi eliberate. Aceste resurse includ spaiul utilizat pentru memorarea mulimii active i spaiul temporar folosit pentru determinarea mulimii active. Cursorul va fi nchis prin comanda CLOSE, care are urmtoarea sintax: CLOSE nume_cursor; Identificatorul nume_cursor este numele unui cursor deschis anterior. Pentru a reutiliza cursorul este suficient ca acesta s fie redeschis. Dac se ncearc ncrcarea datelor dintr-un cursor nchis, atunci apare excepia INVALID_CURSOR. Un bloc PL/SQL poate s se termine fr a nchide cursoarele, dar acest lucru nu este indicat, deoarece este bine ca resursele s fie eliberate. Exemplu: Pentru toi artitii care au opere de art expuse n muzeu s se insereze n tabelul temp informaii referitoare la numele acestora i anul naterii.
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 urmtorul tabel: Dup prima ncrcare, dac mulimea rezultat este vid, %FOUND va fi FALSE, %NOTFOUND va fi TRUE, iar %ROWCOUNT este 0.

Procesarea liniilor unui cursor explicit Pentru procesarea diferitelor linii ale unui cursor explicit se folosete operaia de ciclare (LOOP, WHILE, FOR), prin care la fiecare iteraie se va ncrca o nou linie. Comanda EXIT poate fi utilizat pentru ieirea din ciclu, iar valoarea atributului %ROWCOUNT pentru terminarea ciclului. Procesarea liniilor unui cursor explicit se poate realiza i cu ajutorul unui ciclu FOR special, numit ciclu cursor. Pentru acest ciclu este necesar doar declararea cursorului, operaiile de deschidere, ncrcare i nchidere ale acestuia fiind implicite. Comanda are urmtoarea sintax: FOR nume_nregistrare IN nume_cursor LOOP secven_de_instruciuni; END LOOP; Variabila nume_nregistrare (care controleaz ciclul) nu trebuie declarat. Domeniul ei este doar ciclul respectiv. Pot fi utilizate cicluri cursor speciale care folosesc subcereri, iar n acest caz nu mai este necesar nici declararea cursorului. Exemplul care urmeaz este concludent n acest sens. Exemplu: S se calculeze, utiliznd un ciclu cursor cu subcereri, valoarea operelor de art expuse ntr-o galerie al crei cod este introdus de la tastatur. De asemenea, s se obin media valorilor operelor de art expuse n galeria respectiv.
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 implicit 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 comand SELECT, care nu poate fi schimbat pe parcursul programului. Pentru a putea lucra cu nite cursoare ale cror comenzi SELECT ataate depind de parametri ce pot fi modificai la momentul execuiei, n PL/SQL s-a introdus noiunea de cursor parametrizat. Prin urmare, un cursor parametrizat este un cursor n care comanda SELECT ataat depinde de unul sau mai muli parametri. Transmiterea de parametri unui cursor parametrizat se face n mod similar procedurilor stocate. Un astfel de cursor este mult mai uor de interpretat i de ntreinut, oferind i posibilitatea reutilizrii sale n blocul PL/SQL. Declararea unui astfel de cursor se face respectnd urmtoarea sintax: CURSOR nume_cursor [ (nume_parametru[, nume_parametru ] ) ] [RETURN tip_returnat]

IS comanda_select; Identificatorul comanda_select este o instruciune SELECT fr clauza INTO, tip_returnat reprezint un tip nregistrare sau linie de tabel, iar nume_parametru are sintaxa: nume_parametru [IN] tip_parametru [ {:= | DEFAULT} expresie] n aceast declaraie, atributul tip_parametru reprezint tipul parametrului, care este un tip scalar. Parametrii formali sunt de tip IN i, prin urmare, nu pot returna valori parametrilor actuali. Ei nu suport constrngerea NOT NULL. Deschiderea unui astfel de cursor se face asemntor apelului unei funcii, specificnd lista parametrilor actuali ai cursorului. n determinarea mulimii 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 specificai similar celor de la subprograme. Asocierea dintre parametrii formali i cei actuali se face prin: poziie parametrii formali i actuali sunt separai prin virgul; nume parametrii actuali sunt aranjai ntr-o ordine arbitrar, dar cu o coresponden de forma parametru formal => parametru actual. Dac n definiia cursorului, toi parametrii au valori implicite (DEFAULT), cursorul poate fi deschis fr a specifica vreun parametru. Exemplu: S se declare un cursor parametrizat (parametrii fiind var_salary i var_dept) prin care s se afieze in ordine alfabetica numele, salariul si codul salariailor pentru care salary<var_salary i department_id=var_dept. Rezolvarea se va face n trei moduri (cursor explicit, ciclu cursor, ciclu cursor cu subcereri). DECLARE v_nume employees.last_name%TYPE; v_sal employees.salary%TYPE; v_cod employees.employee_id %TYPE; CURSOR ang_cursor (var_salary NUMBER, var_dept NUMBER) IS SELECT employee_id, last_name, salary FROM employees WHERE salary<var_salary AND department_id=var_dept ORDER BY last_name; BEGIN DBMS_OUTPUT.PUT_LINE('---Cursor explicit---');

OPEN ang_cursor(10000,80); LOOP FETCH ang_cursor INTO v_cod, v_nume,v_sal; EXIT WHEN ang_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Salariatul '|| v_nume|| ' are salariul ' ||v_sal||' cod '||v_cod); END LOOP; CLOSE ang_cursor; DBMS_OUTPUT.PUT_LINE('---Ciclu cursor---'); FOR v_ang_cursor IN ang_cursor(10000,80) LOOP DBMS_OUTPUT.PUT_LINE('Salariatul '|| v_ang_cursor.last_name || ' are salariul ' || v_ang_cursor.salary ||' cod '|| v_ang_cursor.employee_id); END LOOP; DBMS_OUTPUT.PUT_LINE('---Ciclu cursor cu subcereri---'); FOR vv_ang_cursor IN (SELECT employee_id, last_name, salary FROM employees WHERE salary<10000 AND department_id=80 ORDER BY last_name) LOOP DBMS_OUTPUT.PUT_LINE('Salariatul '|| vv_ang_cursor.last_name|| ' are salariul ' ||vv_ang_cursor.salary||' cod '|| vv_ang_cursor.employee_id); END LOOP; END; / Exemplu: Utiliznd un cursor parametrizat s se obin codurile operelor de art din fiecare sal, identificatorul slii i al galeriei. Rezultatele s 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; END LOOP; CLOSE sala_cursor; COMMIT; END;

Cursoare SELECT FOR UPDATE


Uneori este necesar blocarea liniilor nainte ca acestea s fie terse sau reactualizate. Blocarea se poate realiza (atunci cnd cursorul este deschis) cu ajutorul comenzii SELECT care conine 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 list ce include cmpurile tabelului care vor fi modificate. Atributul NOWAIT returneaz o eroare dac liniile sunt deja blocate de alt sesiune. Liniile unui tabel sunt blocate doar dac clauza FOR UPDATE se refer la coloane ale tabelului respectiv. n momentul deschiderii unui astfel de cursor, liniile corespunztoare mulimii active, determinate de clauza SELECT, sunt blocate pentru operaii de scriere (reactualizare sau tergere). n felul acesta este realizat consistena la citire a sistemului. De exemplu, aceast situaie este util cnd se reactualizeaz o valoare a unei linii i trebuie avut sigurana c linia nu este schimbat de alt utilizator naintea reactualizrii. Prin urmare, alte sesiuni nu pot schimba liniile din mulimea activ pn cnd tranzacia nu este permanentizat sau anulat. Dac alt sesiune a blocat deja liniile din mulimea activ, atunci comanda SELECT FOR UPDATE va atepta (sau nu) ca aceste blocri s fie eliberate. Pentru a trata aceast situaie se utilizeaz clauza WAIT, respectiv NOWAIT. n Oracle9i este utilizat sintaxa: SELECT FROM FOR UPDATE [OF lista_campuri] [ {WAIT n | NOWAIT} ]; Valoarea lui n reprezint numrul de secunde de ateptare. Dac liniile nu sunt deblocate n n secunde, atunci se declaneaz eroarea ORA-30006, respectiv eroarea ORA-00054, dup cum este specificat clauza WAIT, respectiv NOWAIT. Dac nu este specificat nici una din clauzele WAIT sau NOWAIT, sistemul ateapt pn ce linia este deblocat i atunci returneaz rezultatul comenzii SELECT. Dac un cursor este declarat cu clauza FOR UPDATE, atunci comenzile DELETE i UPDATE corespunztoare trebuie s conin clauza WHERE CURRENT OF nume_cursor. Aceast clauz refer linia curent care a fost gsit de cursor, permind ca reactualizrile i tergerile s se efectueze asupra acestei linii, fr referirea explicit a cheii primare sau pseudocoloanei ROWID. De subliniat c instruciunile UPDATE i DELETE vor reactualiza numai coloanele listate n clauza FOR UPDATE. Pseudocoloana ROWID poate fi utilizat dac tabelul referit n interogare nu are o cheie primar specificat. ROWID-ul fiecrei linii poate fi ncrcat ntr-o variabil PL/SQL (declarat de tipul ROWID sau UROWID), iar aceast variabil poate fi utilizat n clauza WHERE (WHERE ROWID = v_rowid). Dup nchiderea cursorului este necesar comanda COMMIT pentru a realiza scrierea efectiv a modificrilor, deoarece cursorul lucreaz doar cu nite copii ale liniilor reale existente n tabele.

Deoarece blocrile implicate de clauza FOR UPDATE vor fi eliberate de comanda COMMIT, nu este recomandat utilizarea comenzii COMMIT n interiorul ciclului n care se fac ncrcri de date. Orice FETCH executat dup COMMIT va eua. n cazul n care cursorul nu este definit prin SELECTFOR UPDATE, nu sunt probleme n acest sens i, prin urmare, n interiorul ciclului unde se fac schimbri ale datelor poate fi utilizat un COMMIT. Exemplu: S se mreasc cu 1000 salariile angajailor care au fost angajai n 2000 din tabelul emp_***. Se va folosi un cursor SELECT FOR UPDATE. SELECT last_name, hire_date, salary FROM emp_*** WHERE TO_CHAR(hire_date, 'yyyy') = 2000; DECLARE CURSOR emp_cursor IS SELECT * FROM emp_*** WHERE TO_CHAR(hire_date, 'YYYY') = 2000 FOR UPDATE OF salary NOWAIT; BEGIN FOR v_emp_cursor IN emp_cursor LOOP UPDATE emp_*** SET salary= salary+1000 WHERE CURRENT OF emp_cursor; END LOOP; END; / SELECT last_name, hire_date, salary FROM emp_*** WHERE TO_CHAR(hire_date, 'yyyy') = 2000; ROLLBACK;

Exemplu: S se dubleze valoarea operelor de art pictate pe pnz care au fost achiziionate nainte de 1 ianuarie 1956.

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;

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