You are on page 1of 83

SINTEZE SGBD

AN III – SEM. II MATEMATICA-INFORMATICA

TEMATICA – CURSULUI
1. PL/SQL – CONCEPTE GENERALE
2. BLOCURI PL/SQL; INSTRUCTIUNI
3. TIPURI DE DATE IN PL/SQL
4. GESTIUNEA CURSOARELOR IN PL/SQL
5. SUBPROGRAME IN PL/SQL
6. PACHETE IN PL/SQL
7. DECLANSATORI IN PL/SQL
8. 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 relaţionale orientate obiect şi permite gruparea unei
mulţimi de comenzi într-un bloc unic de tratare a datelor. Programul este format
din unul sau mai multe blocuri care pot conţine blocuri încuibărite.
PL/SQL include atât instrucţiuni SQL pentru manipularea datelor şi pentru
gestiunea tranzacţiilor, cât şi instrucţiuni proprii. Limbajul combină construcţiile
procedurale ale unui limbaj LG3 cu puterea şi flexibilitatea lui SQL (LG4).
Combinaţia a generat un limbaj puternic pentru modelarea aplicaţiilor complexe.
PL/SQL extinde SQL prin construcţii specifice limbajelor procedurale
(definirea variabilelor, declararea tipurilor, utilizarea structurilor de control,
implementarea procedurilor şi funcţiilor, introducerea tipurilor obiect şi metodelor
etc.). PL/SQL oferă posibilităţi moderne de tratare a informaţiei: încapsularea

datelor, analiza specială a erorilor, mascarea informaţiei, orientarea obiect.
Posibilităţile lui SQL sunt folosite pentru un acces rafinat la date, iar facilităţile
oferite de PL/SQL sunt folosite pentru fluxul controlului procesării datelor.

Dintre funcţionalităţile limbajului PL/SQL care determină ca acesta să fie
frecvent utilizat se remarcă următoarele facilităţi:
integrarea comenzilor SQL de bază;
integrarea cu server-ul Oracle şi cu utilitare Oracle;
oferirea unui suport pentru programarea orientată obiect;
asigurarea securităţii informaţiei;
definirea şi gestiunea blocurilor de instrucţiuni;
gestiunea variabilelor, constantelor şi a cursoarelor;
modularizarea programelor (subprograme, pachete);
implementarea şi utilizarea declanşatorilor;
utilizarea structurilor de control fundamentale;
detectarea şi gestiunea erorilor de execuţie şi a situaţiilor excepţionale;
dezvoltarea de aplicaţii 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 staţia client fără interacţiune cu server-
ul sau în întregime pe server. Când blocurile PL/SQL sunt referite dintr-un
program PRO*, din iSQL*Plus, sau de către Server Manager, motorul PL/SQL de
pe server-ul Oracle va procesa aceste blocuri. Acesta descompune blocul în
instrucţiuni SQL şi le trimite executorului de instrucţiuni SQL (SQL Statement
Executor) de pe server-ul Oracle. Fără PL/SQL, instrucţiunile SQL ar fi procesate
separat, fiecare la un moment dat, fiecare implicând un apel la server-ul Oracle.
Restul comenzilor (procedurale) sunt procesate de către executorul
instrucţiunilor procedurale (PSE – Procedural Statement Executor) care este în
motorul PL/SQL. PSE poate procesa datele care sunt locale aplicaţiei, reducându-
se astfel activitatea de transfer spre server-ul Oracle şi numărul de cursoare
solicitate. În felul acesta, este necesar un singur transfer pentru a trimite blocul din

aplicaţie către server.
O aplicaţie bază de date poate fi structurată în trei părţi:
interfaţa utilizator (utilizatorul introduce anumite informaţii şi obţine
nişte rezultate în urma executării aplicaţiei);
aplicaţia logică efectivă;
baza de date.
Există două modele pentru proiectarea unei aplicaţii bază de date:
modelul client-server (two-tier);
modelul three-tier.

Multe dintre aplicaţiile 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 mânuieşte interfaţa, iar server-ul
conţine baza de date. Aplicaţia logică este scindată între client şi server. De
remarcat această caracteristică fundamentală a modelului că aplicaţia 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 aplicaţia (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 utilizând 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
staţia client (local) şi un motor PL/SQL pe server. De exemplu, un declanşator ce
se execută pe staţia client şi care apelează un subprogram stocat în baza de date. În
acest caz, blocurile anonime sunt trimise motorului PL/SQL de pe staţia client, care
procesează local comenzile procedurale. Comenzile neprocedurale din interiorul
blocului sunt trimise executorului de instrucţiuni 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 execuţiei 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
obţinută combinând subprograme, pachete, blocuri imbricate. Blocurile pot fi
folosite în utilitarele Oracle.
Pentru modularizarea unui program este necesară:
gruparea logică a instrucţiunilor în blocuri;
imbricarea de subblocuri în blocuri mai mari;
descompunerea unei probleme complexe într-o mulţime de module logice
şi implementarea acestora cu ajutorul blocurilor;
plasarea în biblioteci a codului PL/SQL reutilizabil, de unde poate fi folosit
de aplicaţii;
depunerea codului într-un server Oracle, de unde este accesibil oricărei
aplicaţii care interacţionează 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 fără 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 aplicaţiei, unde vor fi
executate (trimise motorului PL/SQL). În blocurile anonime pot fi declarate
proceduri şi funcţii 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 fişier, iar apoi
fişierul este executat din SQL*Plus. De asemenea, declanşatorii 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 declanşatori.
Subprogramele sunt proceduri sau funcţii 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 funcţiile stocate sunt depuse pe server-ul
Oracle, acceptă parametri şi pot fi apelate prin nume. Procedurile şi funcţiile
aplicaţie sunt depuse într-o aplicaţie Developer Suite sau într-o bibliotecă.
Pachetele (stocate sau aplicaţie) sunt blocuri neanonime care grupează

iar în acest caz sunt executaţi implicit ori de câte ori apare un anumit eveniment declanşator (de exemplu. . funcţii. constantele. variabile într-o unitate logică. Structura unui bloc PL/SQL Un bloc PL/SQL este compus din trei secţiuni distincte. cursoare. Declanşatorii sunt blocuri PL/SQL neanonime depuse în baza de date. tipuri. De asemenea.proceduri. Secţiunea pentru tratarea erorilor (opţională) specifică acţiunile ce vor fi efectuate atunci când în execuţia blocului apar erori sau condiţii anormale. UPDATE sau DELETE ce se execută asupra unui tabel al bazei de date) sau pot fi asociaţi unei aplicaţii (de exemplu. Secţiunea executabilă conţine instrucţiuni neprocedurale SQL pentru prelucrarea datelor din baza de date şi instrucţiuni PL/SQL pentru prelucrarea datelor în cadrul blocului. în baza de date. Blocul PL/SQL are următoarea structură generală: [<<nume_bloc>>] [DECLARE instrucţiuni de declarare] BEGIN instrucţiuni executabile (SQL sau PL/SQL) [EXCEPTION tratarea erorilor] END [nume_bloc]. invariant va apărea 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 cărei valoare va fi titlul jobului salariatului având codul 200. ceea ce presupune că se execută automat. constante. instrucţiuni INSERT. care pot fi asociaţi bazei. Dacă blocul PL/SQL este executat fără erori. pot fi declarate subprograme locale care sunt vizibile doar în blocul respectiv. Secţiunea declarativă (opţională) conţine declaraţii pentru toate variabilele. cursoarele şi erorile definite de utilizator la care se face referinţă în secţiunea executabilă sau chiar în cea declarativă. în funcţie de anumite condiţii sistem. declanşator SQL*Forms).

cât şi din exteriorul acestuia. SQL> VARIABLE rezultat VARCHAR2(35) SQL> BEGIN 2 SELECT job_title 3 INTO :rezultat 4 FROM employees e. 7 END. Varianta 2 Să se rezolve problema anterioară utilizând variabile de legătură.job_id AND employee_id=200. jobs j 5 WHERE e. 9 DBMS_OUTPUT. 3 BEGIN 4 SELECT job_title 5 INTO v_job 6 FROM employees e.job_id=j.PUT_LINE('rezultatul este '|| :rezultat).SQL> SET SERVEROUTPUT ON SQL> DECLARE 2 v_job jobs. 8 / rezultatul este Administration Assistant PL/SQL procedure successfully completed. 6 DBMS_OUTPUT. SQL> PRINT REZULTAT REZULTAT ------------------------------ . jobs j 7 WHERE e.job_id=j.job_title%TYPE. Să se afişeze rezultatul atât din bloc. 11 / jobul este Administration Assistant PL/SQL procedure successfully completed.job_id 8 AND employee_id=200.PUT_LINE('jobul este '|| v_job). 10 END.

există comentarii pe mai multe linii. Oracle permite folosirea dinamică a comenzilor SQL. SYS_DBURIGEN. există funcţii SQL care nu sunt disponibile în instrucţiuni procedurale (DECODE. WHILE. În felul acesta. DECOMPOSE. Oracle9i introduce clauza OVER. care încep în orice punct al liniei şi se termină la sfârşitul acesteia. De asemenea. dar care sunt disponibile în instrucţiunile SQL dintr-un bloc PL/SQL. pseudocoloanele şi operatorii SQL. funcţiile. Următoarele funcţii SQL nu sunt permise în PL/SQL: WIDTH_BUCKET. funcţiile grup). utilizând tehnica oferită de SQL dinamic. Există comentarii pe o singură linie. deoarece clauza GROUP BY nu are sens să apară în instrucţiunea SELECT … INTO. FOR).Administration Assistant Compatibilitate SQL Din punct de vedere al compatibilităţii dintre PL/SQL şi SQL. Totuşi. PL/SQL nu furnizează comenzile LDD. CASE). TREAT. Observaţii: Comentariile sunt ignorate de compilatorul PL/SQL. cum sunt funcţiile SQLCODE şi SQLERRM. de salt (GOTO. DUMP. Majoritatea funcţiilor SQL sunt disponibile în PL/SQL. De asemenea. Funcţiile grup trebuie folosite cu atenţie. EXIT) şi instrucţiunea vidă (NULL). EXISTSNODE. EXTRACT. BIN_TO_NUM. DECODE. prefixate de simbolurile „--“. SYS_CONNECT_BY_PATH. Instrucţiuni PL/SQL Orice program poate fi scris utilizând structuri de control de bază care sunt combinate în diferite moduri pentru rezolvarea problemei propuse. care sunt delimitate de simbolurile „/*“ şi „*/“. NULLIF. de atribuire (:=). Există însă funcţii specifice PL/SQL. orice comandă SQL (inclusiv comandă LDD) poate să fie utilizată în PL/SQL. SQL nu poate folosi funcţii sau atribute specifice PL/SQL. TO_LOB. care permite ca funcţia grup căreia îi este asociată să fie considerată o funcţie analitică (poate returna mai multe linii pentru fiecare grup). comenzile LCD. Instrucţiunile limbajului pot fi: iterative (LOOP. se remarcă următoarele reguli de bază: PL/SQL furnizează toate comenzile LMD ale lui SQL. COMPOSE. condiţionale (IF. comanda SELECT cu clauza INTO. în ultimele sale versiuni. PL/SQL dispune de comenzi ce permit controlul execuţiei unui bloc. Nu se .

care este obligatorie. Comanda respectă proprietăţile instrucţiunii de atribuire din clasa LG3.“ este separator pentru instrucţiuni. atunci îl caută în secţiunea declarativă a blocurilor care includ blocul respectiv şi niciodată nu caută în blocurile încuibărite în acesta. Acţiunile COMMIT. pot fi folosite variabile PL/SQL sau variabile de legătură. EXIT. admit comentarii imbricate. Comenzile SQL*Plus nu pot să apară într-un bloc PL/SQL. PL/SQL nu suportă comenzile GRANT şi REVOKE. . apare eroarea TOO_MANY_ROWS. Într-un bloc pot fi mai multe tranzacţii sau blocul poate face parte dintr-o tranzacţie. Dacă blocul nu găseşte identificatorul declarat local. Un bloc PL/SQL nu este o unitate tranzacţională. Fluxul secvenţial de execuţie a comenzilor unui program PL/SQL poate fi modificat cu ajutorul structurilor de control: IF. Caracterul „. iar în cazul în care comanda nu găseşte date se generează eroarea NO_DATA_FOUND. GOTO. Atunci când comanda SELECT întoarce mai multe linii. În PL/SQL este introdus un nou operator („**“) pentru ridicare la putere. Cererea dintr-o comandă SELECT trebuie să returneze o singură linie drept rezultat. utilizarea lor fiind posibilă doar prin SQL dinamic. Referirea la o variabilă de legătură se face prin prefixarea acesteia cu simbolul „:“. WHILE. CASE. Un identificator este vizibil în blocul în care este declarat şi în toate subblocurile. sunt identici cu cei din SQL. Instrucţiunea de atribuire Instrucţiunea de atribuire se realizează cu ajutorul operatorului de asignare (:=) şi are forma generală clasică (variabila := expresie). procedurile şi funcţiile imbricate în acesta. cât şi ordinea de execuţie a acestora. În comanda SELECT trebuie specificate variabilele care recuperează rezultatul acţiunii acestei comenzi. FOR. Atât operatorii din PL/SQL. LOOP. În clauza INTO. dar instrucţiunile asociate acestor acţiuni pot fi folosite într-un bloc. De remarcat că nu poate fi asignată valoarea null unei variabile care a fost declarată NOT NULL. SAVEPOINT şi ROLLBACK sunt independente de blocuri.

Exemplu: Următorul exemplu prezintă modul în care acţionează instrucţiunea de atribuire în cazul unor tipuri de date particulare. 6 end.PUT_LINE(alfa).pot fi specificate numai lunile alfa := '200-7'. -. BEGIN alfa := INTERVAL '200-7' YEAR TO MONTH. . 5 DBMS_OUTPUT.PUT_LINE('alfa = '|| alfa). 3 begin 4 alfa :=interval '7' month. 3 begin 4 alfa :=interval '1 .pot fi specificati numai anii alfa := INTERVAL '7' MONTH.7' year to month. 6 end. DECLARE alfa INTERVAL YEAR TO MONTH. SQL> declare 2 alfa interval year to month. -.alfa ia valoarea 200 de ani si 7 luni alfa := INTERVAL '200' YEAR. -. SQL> declare 2 alfa interval year to month. DBMS_OUTPUT. SQL> declare 2 alfa interval year to month.PUT_LINE('alfa = '|| alfa). -. 5 DBMS_OUTPUT. 7 / alfa = +01-07 PL/SQL procedure successfully completed. 7 / alfa = +00-07 PL/SQL procedure successfully completed.conversie implicita din caracter END.

Instrucţiunea IF Un program PL/SQL poate executa diferite porţiuni de cod. delta epsilon%ROWTYPE. BEGIN beta := gama. Instrucţiunile care realizează acest lucru sunt cele condiţionale (IF. gama opera%ROWTYPE. 3 begin 4 alfa := '10 . în funcţie de anumite condiţii. permiţând efectuarea unor acţiuni în mod selectiv. 7 / alfa = +10-08 PL/SQL procedure successfully completed. 6 end.8'. 5 DBMS_OUTPUT.PUT_LINE('alfa = '|| alfa). CASE). . DECLARE beta opera%ROWTYPE. -.corect gama := delta.incorect???-testati! END. cursor epsilon IS SELECT * FROM opera. Structura instrucţiunii IF în PL/SQL este similară instrucţiunii IF din alte limbaje procedurale. -. Instrucţiunea IF-THEN-ELSIF are următoarea formă sintactică: IF condiţie1 THEN secvenţa_de_comenzi_1 [ELSIF condiţie2 THEN secvenţa_de_comenzi_2] … [ELSE secvenţa_de_comenzi_n] END IF. în funcţie de rezultatul unui test (predicat).

Este permis un număr arbitrar de opţiuni ELSIF. BEGIN SELECT COUNT(*) INTO v_numar FROM opera WHERE cod_galerie = v_cod_galerie.cod_galerie%TYPE := &p_cod_gal. END. ELSE v_comentariu := 'mare'. se foloseşte ramura ELSIF (atenţie. dar poate apărea cel mult o clauză ELSE. DBMS_OUTPUT. IF v_numar < 100 THEN v_comentariu := 'mica'. nu ELSEIF) cu o nouă condiţie. v_numar NUMBER(3) := 0. . cuprins între 10000 şi 20000 sau mai mic decât 10000. medie sau mica după cum numărul operelor de artă expuse în galeria respectivă este mai mare decât 200. Aceasta se referă la ultimul ELSIF. cuprins între 100 şi 200 sau mai mic decât 100. Dacă pe ramura THEN se doreşte verificarea unei alternative. v_comentariu VARCHAR2(10). DEFINE p_cod_gal = 753 DECLARE v_cod_galerie opera. ELSIF v_numar BETWEEN 100 AND 200 THEN v_comentariu := 'medie'. mediu sau mic după cum este mai mare decât 20000. / Exemplu: Să se specifice dacă un angajat dat are salariu mare. END IF. Exemplu: Să se specifice dacă o galerie este mare.PUT_LINE('Galeria avand codul '|| v_cod_galerie ||' este de tip '|| v_comentariu). Atunci când condiţia este FALSE sau NULL. secvenţa nu este executată. O secvenţă de comenzi din IF este executată numai în cazul în care condiţia asociată este TRUE.

EMPLOYEE_ID%TYPE := 201. 18 END. salariatul avand codul 201 are salariu 13000 considerat mediu PL/SQL procedure successfully completed. [ELSE altă_secvenţă. EMPLOYEE_ID%TYPE := &p_cod_em./ Instrucţiunea CASE Oracle9i furnizează o nouă comandă (CASE) care permite implementarea unor condiţii multiple. 12 ELSIF v_sal BETWEEN 10000 AND 20000 THEN 13 v_comentariu := 'mediu'. .PUT_LINE('salariatul avand codul '|| v_cod_ang ||' are salariu '|| v_sal || ' considerat '|| v_comentariu).] END CASE [eticheta]. 3 v_sal EMPLOYEES. new 2: v_cod_ang EMPLOYEES. 5 BEGIN 6 SELECT salary 7 INTO v_sal 8 FROM EMPLOYEES 9 WHERE EMPLOYEE_ID = v_cod_ang. 4 v_comentariu VARCHAR2(10).SQL> DEFINE p_cod_em = 201 SQL> DECLARE 2 v_cod_ang EMPLOYEES. 16 END IF. 19 / old 2: v_cod_ang EMPLOYEES. … WHEN valoare_k THEN secvenţa_de_comenzi_k. 17 DBMS_OUTPUT. Instrucţiunea are următoarea formă sintactică: [<<eticheta>>] CASE test_var WHEN valoare_1 THEN secvenţa_de_comenzi_1.salary%type. EMPLOYEE_ID%TYPE := &p_cod_em. 10 IF v_sal < 10000 THEN 11 v_comentariu := 'mic'. WHEN valoare_2 THEN secvenţa_de_comenzi_2. 14 ELSE 15 v_comentariu:= 'mare'.

] END CASE [eticheta]. care reprezintă abrevierea zilelor unei săptămâni. … WHEN condiţie_k THEN secvenţa_de_comenzi_k. eticheta poate să apară la sfârşitul clauzei END CASE. WHEN 'V' THEN DBMS_OUTPUT. Dacă valoarea lui condiţie_p este TRUE. în acest caz. Varianta 1: SET SERVEROUTPUT ON DEFINE p_zi = x DECLARE v_zi CHAR(2) := UPPER('&p_zi'). Se va executa secvenţa_de_comenzi_p. BEGIN CASE v_zi WHEN 'L' THEN DBMS_OUTPUT. Dacă această clauză este necesară în implementarea unei probleme.PUT_LINE(' Astazi este Vineri'). WHEN 'MI' THEN DBMS_OUTPUT. Fiecare clauză WHEN conţine o expresie booleană. De remarcat că eticheta după END CASE este permisă numai în cazul în care comanda CASE este etichetată. controlul va trece la următoarea instrucţiune după CASE. Selectorul test_var poate fi o variabilă sau o expresie complexă care poate conţine chiar şi apeluri de funcţii. Selectorul test_var poate să lipsească din structura comenzii CASE. atunci este executată secvenţa_de_comenzi_p.PUT_LINE(' Astazi este Miercuri'). WHEN 'J' THEN DBMS_OUTPUT. dar totuşi lipseşte. Comanda CASE poate fi etichetată şi. WHEN condiţie_2 THEN secvenţa_de_comenzi_2.PUT_LINE(' Astazi este Marti'). [ELSE altă_secvenţă. dacă valoarea selectorului test_var este valoare_p. atunci se declanşează eroarea predefinită CASE_NOT_FOUND (ORA . Clauza ELSE este opţională. WHEN 'M' THEN DBMS_OUTPUT.06592). care în acest caz va avea următoarea formă sintactică: [<<eticheta>>] CASE WHEN condiţie_1 THEN secvenţa_de_comenzi_1. iar test_var nu ia nici una dintre valorile ce apar în clauzele WHEN. să se afişeze (în cele două variante) un mesaj prin care este specificată ziua săptămânii corespunzătoare abrevierii respective. După ce este executată secvenţa de comenzi. .PUT_LINE(' Astazi este Luni'). Exemplu: În funcţie de o valoare introdusă de utilizator.PUT_LINE(' Astazi este Joi').

“. / SET SERVEROUTPUT OFF Varianta 2: SET SERVEROUTPUT ON DEFINE p_zi = x DECLARE v_zi CHAR(2) := UPPER('&p_zi'). / SET SERVEROUTPUT OFF În Oracle9i poate fi utilizată o construcţie CASE într-o comandă SQL a unui bloc PL/SQL. WHEN v_zi = 'MI' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Marti').PUT_LINE(' Astazi este Joi').PUT_LINE('Este o eroare!').PUT_LINE(' Astazi este Miercuri'). WHEN v_zi = 'S' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Duminica').PUT_LINE('Astazi este Luni'). END. ELSE DBMS_OUTPUT.PUT_LINE(' Astazi este Sambata'). END CASE.PUT_LINE(' Astazi este Sambata').PUT_LINE('este o eroare!'). ELSE DBMS_OUTPUT. clauza END nu include cuvântul cheie CASE şi nu se fac atribuiri în clauza WHEN. Expresia CASE are sintaxa similară comenzii CASE.PUT_LINE(' Astazi este Vineri'). WHEN v_zi = 'J' THEN DBMS_OUTPUT. WHEN v_zi = 'V' THEN DBMS_OUTPUT. WHEN 'S' THEN DBMS_OUTPUT. BEGIN CASE WHEN v_zi = 'L' THEN DBMS_OUTPUT. WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE(' Astazi este Duminica'). Expresia CASE returneaza null daca nu exista clauza ELSE si daca nici o . END CASE. dar clauzele WHEN nu se termină prin caracterul „. WHEN v_zi = 'D' THEN DBMS_OUTPUT. END. WHEN v_zi = 'M' THEN DBMS_OUTPUT.

conditie nu este indeplinita. SALARY. LAST_NAME SALARY CALIFICARE ------------------------. ciclarea WHILE şi ciclarea FOR. comanda WHILE. dacă instrucţiunile din cadrul ciclului trebuie să se execute cel puţin o dată. . Ciclurile pot fi imbricate pe mai multe niveluri. iar ieşirea din ciclu se poate realiza cu ajutorul comenzii EXIT. dacă numărul de iteraţii este cunoscut. SELECT last_name.-------------------------- 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 Instrucţiuni iterative Există trei tipuri de comenzi iterative: ciclarea simplă LOOP. Se utilizează: comanda LOOP. Ele pot fi etichetate.---------. comanda FOR. Acestea permit repetarea (condiţionată sau necondiţionată) execuţiei uneia sau mai multor instrucţiuni. (CASE WHEN salary <5000 THEN ' LOW' WHEN salary <10000 THEN ' MEDIUM' WHEN salary <15000 THEN ' GOOD' ELSE ' EXCELLENT' END) AS calificare FROM EMPLOYEES. în cazul în care condiţia trebuie evaluată la începutul fiecărei iteraţii.

END LOOP. SET SERVEROUTPUT ON DECLARE v_contor BINARY_INTEGER := 1. END LOOP. v_contor := v_contor + 1. COMMIT. Să se introducă 70 de înregistrări în tabelul org_tab.-------------------- . COD_TAB TEXT_TAB ---------. SELECT * FROM ORG_TAB. constând din două coloane: cod_tab de tip INTEGER. Ciclarea simplă cuprinde o mulţime de comenzi incluse între cuvintele cheie LOOP şi END LOOP. ciclarea poate continua la infinit. 'indicele '|| v_contor). Aceste comenzi se execută cel puţin o dată. END. BEGIN LOOP INSERT INTO org_tab VALUES (v_contor. EXIT WHEN v_contor > 70. CREATE TABLE ORG_TAB (cod_tab INTEGER. ce conţine un contor al înregistrărilor şi text_tab de tip VARCHAR2. Exemplu: Se presupune că a fost creată structura tabelului org_tab. Dacă nu este utilizată comanda EXIT. ce conţine un text asociat fiecărei înregistrări. text_tab VARCHAR2(25)). Instrucţiunea LOOP are următoarea formă sintactică: LOOP secvenţa_de_comenzi.

atunci condiţia rămâne adevărată şi ciclul nu se termină. atâta timp cât o anumită condiţie specificată este adevărată. lim_inf şi lim_sup. Dacă variabilele care apar în condiţie nu se schimbă în interiorul ciclului. END LOOP. atunci secvenţa de comenzi nu este executată şi controlul trece la prima instrucţiune după END LOOP. Instrucţiunea repetitivă FOR (ciclare cu pas) permite executarea unei secvenţe de instrucţiuni pentru valori ale variabilei contor cuprinse între două limite. Când condiţia este evaluată ca fiind FALSE sau NULL. BEGIN WHILE v_contor <= 70 LOOP INSERT INTO org_tab VALUES (v_contor. Exemplu: DECLARE v_contor BINARY_INTEGER := 1. iteraţia se face . Comanda WHILE are următoarea sintaxă: WHILE condiţie LOOP secvenţa_de_comenzi. 'indicele ciclului'). END. Dacă este prezentă opţiunea REVERSE. 67 indicele 67 68 indicele 68 69 indicele 69 70 indicele 70 70 rows selected. END LOOP. Instrucţiunea repetitivă WHILE permite repetarea unei secvenţe de instrucţiuni. v_contor := v_contor + 1.

END LOOP. Limitele domeniului pot fi variabile sau expresii.v_valoare LOOP v_stea := v_stea || '*'. END IF. ALTER TABLE opera ADD stea VARCHAR2(20).valoare%TYPE. IF v_valoare > 0 THEN FOR i IN 1... introducând o steluţă pentru fiecare 10000$ din valoarea unei opere de artă al cărei cod este specificat. Ea este neidentificată în afara ciclului şi implicit de tip BINARY_INTEGER.cod_opera%TYPE := &p_cod_opera.0) INTO v_valoare FROM opera WHERE cod_opera = v_cod_opera. END. Exemplu: În structura tabelului opera se va introduce un nou câmp (stea). DEFINE p_cod_opera = 7777 DECLARE v_cod_opera opera. Comanda FOR are sintaxa: FOR contor_ciclu IN [REVERSE] lim_inf. END LOOP. Să se creeze un bloc PL/SQL care va reactualiza acest câmp.stea%TYPE := NULL. Pasul are implicit valoarea 1 şi nu poate fi modificat. COMMIT.lim_sup LOOP secvenţa_de_comenzi. Variabila contor_ciclu nu trebuie declarată. v_stea opera. . UPDATE opera SET stea = v_stea WHERE cod_opera = v_cod_opera. BEGIN SELECT NVL(ROUND(valoare/10000). care să poată fi convertite la întreg. v_valoare opera.(în sens invers) de la lim_sup la lim_inf.

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

fie pe o linie separată. În PL/SQL etichetele se definesc prin intercalarea numelui etichetei între caracterele „<<“ şi „>>“ (<<eticheta>>). Controlul trece fie la prima instrucţiune situată după clauza END LOOP corespunzătoare.------------. EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMBER HIRE_DATE JOB_ID SALARY ----------.------------------------.---------. EXIT WHEN v_contor > 70. fie pe aceeaşi linie.------------------------. Numele etichetelor urmează aceleaşi reguli ca cele definite pentru identificatori.-------------------- 100 Steven King SKING 515.-------------------. Ea are o formă necondiţională (ieşire fără condiţii) şi una condiţională.4567 17-JUN-87 AD_PRES 24000 90 ** Instrucţiuni de salt Instrucţiunea EXIT permite ieşirea dintr-un ciclu.123. alt_raspuns VARCHAR2(10). BEGIN … <<exterior>> LOOP v_contor := v_contor + 1. fie după instrucţiunea LOOP având eticheta nume_eticheta. Exemplu: DECLARE v_contor BINARY_INTEGER := 1.SQL> SELECT * FROM EMPLOYEES 2 WHERE EMPLOYEE_ID=100. EXIT [nume_eticheta] [WHEN condiţie].--------------- COMMISSION_PCT MANAGER_ID DEPARTMENT_ID STEA -------------. Eticheta se plasează înaintea comenzii. <<interior>> . raspuns VARCHAR2(10).

de la o clauză a comenzii CASE. -. … END LOOP exterior. -. în exteriorul unui subprogram. marcând faptul că nu trebuie întreprinsă nici o acţiune. CASE sau LOOP. Instrucţiunea are următoarea formă sintactică: GOTO nume_eticheta. la altă clauză a aceleaşi comenzi.se parasesc ambele cicluri EXIT WHEN alt_raspuns = 'DA'. . de la tratarea unei excepţii. END. Nu trebuie confundată instrucţiunea NULL cu valoarea null! Uneori instrucţiunea NULL este folosită într-o comandă IF. Nu este permis saltul: în interiorul unui bloc (subbloc). LOOP … EXIT exterior WHEN raspuns = 'DA'. indicând faptul că pentru o anumită clauză ELSIF nu se execută nici o acţiune. Instrucţiunea vidă Instrucţiunea vidă (NULL) este folosită pentru o mai bună lizibilitate a programului. în blocul curent. în interiorul unei comenzi IF.se paraseste ciclul interior … END LOOP interior. NULL este instrucţiunea care nu are nici un efect. Instrucţiunea GOTO determină un salt necondiţionat la o instrucţiune executabilă sau la începutul unui bloc care are eticheta specificată în comandă.

LOB (large objects). INT. Variabilele folosite în Oracle9i pot fi: specifice PL/SQL. tipul BINARY_INTEGER cu subtipurile NATURAL. nespecifice PL/SQL. Tipurile de date ce stochează data calendaristică şi ora cuprind tipurile DATE. NUMERIC. 2. Tipurile de date ce stochează caractere cuprind tipul VARCHAR2 cu subtipurile STRING. INTERVAL DAY TO SECOND. ROWID. tipuri obiect. Variabile specifice PL/SQL Tipurile de date scalare Nu au componente interne (conţin valori atomice). REAL. TIMESTAMP. tipul PLS_INTEGER. variabile gazdă (host variables). SIGNTYPE. compuse. DECIMAL. LONG RAW. POSITIVEN. RAW. TIMESTAMP WITH TIME ZONE. . Variabile specifice PL/SQL se clasifică în variabile: de tip scalar. POSITIVE. Tipurile de date ce stochează valori numerice cuprind tipul NUMBER cu subtipurile DEC. NATURALN. SMALLINT. Variabile nespecifice PL/SQL pot fi: variabile de legătură (bind variables). variabile indicator. VARCHAR. TIMESTAMP WITH LOCAL TIME ZONE. FLOAT. tipul de date CHAR cu subtipul CHARACTER. constrângerile care trebuie să le verifice domeniul valorilor sale. tipurile LONG. INTEGER. referinţă. 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 său de stocare. DOUBLE PRECISION. Se împart în 5 clase. INTERVAL YEAR TO MONTH.

false sau null). Tipul REF CURSOR este folosit pentru a face referinţă la un cursor explicit. ROWID. numite pointeri. tipurile de date PL/SQL diferă de corespondentele lor SQL prin dimensiunea maximă permisă. Pointerii conţin locaţia de memorie (adresa) unui element şi nu elementul în sine. numite locatori (locators) specifică localizarea unor obiecte de dimensiuni mari. LONG. Tipul de date BOOLEAN stochează valori logice (true. VARRAY). Tipurile de date LOB Large object sunt acele tipuri de date ale căror valori. colecţie (INDEX-BY TABLE. Tipurile de date referinţă REF CURSOR si REF obiect sunt tipuri de date ale căror valori. DATE. RAW. care încapsulează structuri de date (atribute) împreună cu subprograme pentru manipularea datelor (metode). definite de utilizator. clipuri video şi sunete. CHAR. NESTED TABLE. Dintre tipurile scalare PL/SQL. VARCHAR2. următoarele sunt şi tipuri SQL (adică pot fi folosite pentru coloanele tabelelor Oracle): NUMBER. Tipul REF obiect face referinţă la adresa unui obiect. BFILE (binary file). NCLOB (national language character large object). LONG RAW. adică blocuri de date nestructurate. Tipurile LOB sunt manipulate cu ajutorul pachetului DBMS_LOB. Tipurile de date compuse Au componente interne care pot fi manipulate individual. NVARCHAR2. cum ar fi texte. Aceste tipuri sunt: CLOB (character large object). fac referinţă către obiecte din program. . Tipurile de date globalizare ce stochează date unicode includ tipurile NCHAR şi NVARCHAR2. Tipurile obiect Sunt tipuri compuse. NCHAR. Oracle oferă programatorului două tipuri de date compuse: înregistrare (RECORD). imagini grafice. BLOB (binary large object). În unele cazuri.

DEC.. Prin urmare.99E125. VARCHAR2 şi RAW pot avea un parametru pentru a preciza lungimea maximă. SMALLINT. Reprezentarea internă depinde de setul de caractere ales (ASCII sau EBCDIC). IBM SQL/DS sau IBM DB2. De exemplu. care au aceleaşi intervale de valori: NUMERIC. Variabilele alfanumerice pot fi de tip CHAR.1. Aceste subtipuri se pot utiliza pentru compatibilitate ANSI/ISO.. Valoarea unei variabile de tip NUMBER este cuprinsă între 1. Variabilele de tip LONG pot memora texte. LONG. decât cele cu tipurile NUMBER sau BINARY_INTEGER (folosesc librării aritmetice). RAW şi LONGRAW. Tipul NUMBER are următoarele subtipuri. iar n numărul de zecimale. FLOAT (pentru memorarea datelor numerice în virgulă mobilă). Subtipurile acestor tipuri se pot utiliza pentru compatibilitate ANSI/ISO. El are forma generală NUMBER (m. Tipul PLS_INTEGER este utilizat pentru stocarea numerelor întregi cu semn şi are acelaşi interval de definire ca şi tipul BINARY_INTEGER. unde m reprezintă numărul total de cifre. 231 – 1). se consideră 1. De exemplu. Numărul de zecimale determină poziţia în care apare rotunjirea. Este similar tipului VARCHAR2. o variabilă RAW poate memora o secvenţă de caractere grafice sau o imagine digitizată. DECIMAL şi DOUBLE PRECISION (pentru memorarea datelor numerice în virgulă fixă). pentru o mai bună performanţă. este preferabil să se utilizeze tipul PLS_INTEGER. Dacă aceasta nu este precizată atunci. Tipul BINARY_INTEGER memorează numere întregi cu semn având valori cuprinse între -231 . tabele de caractere sau documente. permiţând ca variabila ce precizează lungimea maximă să fie de tip CHAR sau BYTE. REAL. Valoarea sa este cuprinsă între -84 şi 127. nu necesită conversii şi admite mai multe subtipuri.1 şi 231 . implicit. Tipul RAW este similar tipului alfanumeric. Acest tip de date este utilizat frecvent pentru indecşii tabelelor. 231 – 1) şi POSITIVE (1 . Operaţiile cu acest tip sunt efectuate mai rapid (folosesc aritmetica maşinii). INTEGER şi INT (pentru memorarea numerelor întregi). Tipul NUMBER memorează numerele în virgulă fixă şi virgulă mobilă. IBM SQL/DS sau IBM DB2. În Oracle9i a fost extinsă sintaxa pentru CHAR şi VARCHAR2. prin urmare şiruri de caractere de lungime variabilă de până la 32760 octeţi.0E-129 şi 9. n). VARCHAR2. Tipul RAW permite memorarea datelor binare (biţi) sau a şirurilor de octeţi. Lungimea este exprimată în octeţi (nu în caractere). pentru a restricţiona domeniul variabilelor la valori întregi nenegative se utilizează tipurile NATURAL (0 . iar implicit este 0. cu excepţia faptului . Tipurile CHAR.

TIMESTAMP WITH LOCAL TIME ZONE. LONG RAW este similar tipului LONG. Oracle nu face conversia datelor de acest tip. TIMESTAMP WITH TIME ZONE. program sau limbă. iar pentru afişarea valorilor acestora se utilizează comanda PRINT.că PL/SQL nu interpretează datele de tip RAW. într-o coloană RAW a bazei de date nu se pot introduce decât 2000 octeţi. atunci când se transmit de la un sistem la altul. indiferent de platformă. Ele oferă suport pentru globalizarea datelor. TIMESTAMP poate lua în considerare şi fracţiuni de secundă. Aceste tipuri de date suportă numai date Unicode. în programul . dar datele nu mai sunt interpretate de PL/SQL. Deoarece instrucţiunile SQL pot fi integrate în programe C. este necesar un mecanism pentru a transfera valori între mediul de programare C şi instrucţiunile SQL care comunică cu server-ul bazei de date Oracle. unicode furnizează o valoare cod unică pentru fiecare caracter. Tipurile TIMESTAMP. Variabilele declarate în mediul gazdă sau în cel apelant pot fi referite în instrucţiuni PL/SQL. De exemplu. 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 mulţime de caractere naţionale care este folosită pentru reprezentarea informaţiei cu caracter naţional (national character set . funcţii sau pachet. INTERVAL YEAR TO MONTH. Chiar dacă lungimea maximă a unei variabile RAW poate fi 32767 octeţi. pentru a putea fi deosebite de variabilele declarate în PL/SQL. variabilele de legătură se declară folosind comanda VARIABLE. În acest scop. Prin urmare. Variabile nespecifice PL/SQL Variabila de legătură (bind) se declară într-un mediu gazdă şi este folosită pentru transferul (la execuţie) valorilor numerice sau de tip caracter în/din unul sau mai multe programe PL/SQL. folosind o mulţime unică de caractere. În SQL*Plus. Tipurile de date NCHAR şi NVARCHAR2 sunt utilizate pentru stocarea în baza de date a şirurilor de caractere ce folosesc NCS. INTERVAL DAY TO SECOND au fost introduse în Oracle9i şi permit rafinări ale tipului DATE. care are lungimea maximă 231 octeţi. Ele sunt referite prin prefixarea cu simbolul „:“. dacă acestea nu sunt în cadrul unei proceduri. astfel încât utilizatorii din toată lumea pot interacţiona cu Oracle în limba lor naţională.NCS). Pentru a insera valori mai mari se foloseşte o coloană de tip LONG RAW. Unicode este o mulţime de caractere globale care permite stocarea de informaţie în orice limbă.

Pentru a folosi un astfel de tip într-un program este suficient să fie declarată o variabilă de tipul respectiv. Calitatea atributelor %TYPE şi %ROWTYPE constă în faptul că simplifică întreţinerea codului PL/SQL. De exemplu. în acest caz trebuie definit efectiv tipul şi apoi declarată variabila de tipul respectiv. iar o valoare a indicatorului mai mare ca zero precizează că PL/SQL trebuie să considere chiar valoarea variabilei. Orice variabilă declarată într-un bloc este accesibilă blocurilor conţinute sintactic în acesta. Atributul %ROWTYPE permite definirea unei variabile având tipul unei înregistrări dintr-un tabel. Acestea sunt variabile speciale de tip întreg. Ele au următoarea formă: :nume_extern [: indicator] De exemplu. dacă atribuirea este făcută de limbajul gazdă. folosite pentru a indica dacă o valoare null este recuperată (extrasă) din baza de date sau stocată în aceasta. Avantajul utilizării acestui atribut constă în faptul că nu este necesar să se cunoască numărul şi tipurile coloanelor tabelului. fără să fie necesară modificarea declaraţiei variabilelor al căror tip s-a . Elementele individuale ale acestei structuri de tip înregistrare sunt referite în maniera clasică. Tipurile scalare sunt predefinite în pachetul STANDARD. Acestea sunt declarate între directivele BEGIN DECLARE SECTION şi END DECLARE SECTION ale preprocesorului. Dacă în declaraţia unei variabile apar referiri la alte variabile. În declararea variabilelor pot fi utilizate atributele %TYPE şi %ROWTYPE. prefixând numele coloanei cu numele variabilei declarate. Declararea variabilelor Identificatorii PL/SQL trebuie declaraţi înainte de a fi referiţi în blocul PL/SQL. care reprezintă tipuri de date implicite.încapsulat sunt definite variabilele gazdă (host). poate fi modificată dimensiunea unei coloane. Pentru a rezolva problema comunicării valorilor null între programul scris în limbaj gazdă şi sistemul Oracle. O valoare null în baza de date nu are o valoare corespunzătoare în mediul limbajului gazdă (de exemplu. Prin urmare. acestea trebuie să fi fost declarate anterior. au fost definite variabilele indicator. valoarea –1 a indicatorului specifică faptul că PL/SQL trebuie să înlocuiască valoarea variabilei prin null. Aceste tipuri permit declararea unei variabile în concordanţă cu declaraţii de variabile făcute anterior. Atributul %TYPE permite definirea unei variabile având tipul unei variabile declarate anterior sau tipul unei coloane dintr-un tabel. limbajul C). Tipurile compuse sunt definite de utilizator.

v_opera opera ROWTYPE. 2. Constantele trebuie iniţializate când sunt declarate. valoarea implicită a acesteia este null. v_cod_opera opera. Două obiecte (variabile) pot avea acelaşi nume cu condiţia să fie definite în blocuri diferite. poate fi folosit doar obiectul declarat în blocul curent. v_clasificare BOOLEAN DEFAULT FALSE. 6.cod_opera TYPE. Sintaxa declarării unei variabile este următoarea: identificator [CONSTANT] {tip_de_date | identificator%TYPE | identificator%ROWTYPE} [NOT NULL] [ {:= | DEFAULT} expresie_PL/SQL]. Atributul %ROWTYPE nu poate include clauze de iniţializare. iar dacă o variabilă nu este iniţializată. Dacă ele coexistă. Observaţii: 1. 7. Dacă o variabilă este declarată NOT NULL. Exemplu: v_valoare NUMBER(15) NOT NULL := 0. există o singură declaraţie de variabilă. v_data_achizitie DATE DEFAULT SYSDATE. int_an_luna INTERVAL YEAR TO MONTH := INTERVAL '3-2' YEAR TO MONTH. 5. altfel apare o eroare la compilare. v_material VARCHAR2(15) := 'Matase'. iar pentru o constantă este folosită prefixarea cu litera c (c_identificator). Pentru a iniţializa o variabilă sau o constantă poate fi utilizată o expresie PL/SQL compatibilă ca tip cu variabila sau constanta respectivă. Se pot defini constante (valoarea stocată nu poate fi modificată) prin specificarea la declarare a cuvântului cheie CONSTANT. 4. pe fiecare linie. 3. În secţiunea declarativă. atunci ea va fi obligatoriu iniţializată.definit făcând referinţă la tipul coloanei respective. Pentru a denumi o variabilă este utilizată frecvent (pentru uşurinţa referirii) prefixarea cu litera v (v_identificator). Variabilele pot fi iniţializate. c_valoare CONSTANT NUMBER := 100000. v_stare VARCHAR2(20) DEFAULT 'Buna'. .

Definirea subtipurilor
Subtipurile derivă dintr-un tip de bază, la care se adaugă anumite restricţii.
De exemplu, NATURAL este un subtip predefinit PL/SQL, derivat din tipul de bază
BINARY_INTEGER, cu restricţia că permite prelucrarea valorilor întregi
nenegative.
Prin urmare, un subtip nu reprezintă un nou tip de date, ci un tip existent
asupra căruia se aplică anumite constrângeri. Subtipurile presupun acelaşi set de
operaţii 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 utilizând sintaxa:
SUBTYPE nume_subtip IS tip_de_baza [NOT NULL];
În dicţionarul datelor există vizualizări care furnizează informaţii 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 funcţii de conversie explicită.
Funcţiile 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 următoarele funcţii 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 funcţii reflectă posibilităţile pe care le oferă. De

exemplu, TO_YMINTERVAL converteşte argumentele sale la tipul INTERVAL
YEAR TO MONTH conform unui format specificat. Funcţia COMPOSE
converteşte un şir de caractere la un şir unicode (asociază o valoare cod unică
pentru fiecare simbol din şir).

Înregistrări
Tipul RECORD oferă un mecanism pentru prelucrarea înregistrărilor.
Înregistrările au mai multe câmpuri ce pot fi de tipuri diferite, dar care sunt legate
din punct de vedere logic.
Inregistrările trebuie definite în doi paşi:
se defineşte tipul RECORD;
se declară înregistrările de acest tip.
Declararea tipului RECORD se face conform următoarei sintaxe:
TYPE nume_tip IS RECORD
(nume_câmp1 {tip_câmp | variabilă%TYPE |
nume_tabel.coloană%TYPE | nume_tabel%ROWTYPE}
[ [NOT NULL] {:= | DEFAULT} expresie1],
(nume_câmp2 {tip_câmp | 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 înregistrărilor, nume_câmp este numele unui câmp al
înregistrării, iar tip_câmp este tipul de date al câmpului.

Observaţii:
Dacă un câmp nu este iniţializat atunci implicit se consideră că are valoarea
NULL. Dacă s-a specificat constrângerea NOT NULL, atunci obligatoriu
câmpul trebuie iniţializat cu o valoare diferită de NULL.
Pentru referirea câmpurilor individuale din înregistrare se prefixează numele
câmpului cu numele înregistrării.
Pot fi asignate valori unei înregistrări utilizând comenzile SELECT, FETCH
sau instrucţiunea clasică de atribuire. De asemenea, o înregistrare poate fi
asignată altei înregistrari de acelaşi tip.

Componentele unei înregistrări pot fi de tip scalar, RECORD, TABLE,
obiect, colecţie (dar, nu tipul REF CURSOR).
PL/SQL permite declararea şi referirea înregistrărilor imbricate.
Numărul de câmpuri ale unei înregistrări nu este limitat.
Înregistrările nu pot fi comparate (egalitate, inegalitate sau null).
Înregistrările pot fi parametri în subprograme şi pot să apară în clauza
RETURN a unei funcţii.

Diferenţa dintre atributul %ROWTYPE şi tipul de date compus RECORD:
tipul RECORD permite specificarea tipului de date pentru câmpuri şi
permite declararea câmpurilor sale;
atributul %ROWTYPE nu cere cunoaşterea numărului şi tipurilor
coloanelor tabloului.
Oracle9i introduce câteva facilităţi legate de acest tip de date.
Se poate insera (INSERT) o linie într-un tabel utilizând o înregistrare. Nu
mai este necesară listarea câmpurilor individuale, ci este suficientă
utilizarea numelui înregistrării.
Se poate reactualiza (UPDATE) o linie a unui tabel utilizând o
înregistrare. Sintaxa SET ROW permite să se reactualizeze întreaga linie
folosind conţinutul unei înregistrări.
Într-o înregistrare se poate regăsi şi returna sau şterge informaţia 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, colecţii de
înregistrări.

Exemplu:

Exemplul următor 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;

Colecţii
Uneori este preferabil să fie prelucrate simultan mai multe variabile de
acelaşi tip. Tipurile de date care permit acest lucru sunt colecţiile. Fiecare element
are un indice unic, care determină poziţia sa în colecţie.
Oracle7 a furnizat tipul index-by table, iniţial numit PL/SQL table datorită
asemănării sale cu structura tabelelor relaţionale.
Oracle8 a introdus două tipuri colecţie, nested table şi varray. Oracle9i
permite crearea de colecţii pe mai multe niveluri, adică colecţii de colecţii.
În PL/SQL există trei tipuri de colecţii:
tablouri indexate (index-by tables);
tablouri imbricate (nested tables);
vectori (varrays sau varying arrays).
Tipul index-by table poate fi utilizat numai în declaraţii PL/SQL. Tipurile
varray şi nested table pot fi utilizate atât în declaraţii PL/SQL, cât şi în declaraţii la
nivelul schemei (de exemplu, pentru definirea tipului unei coloane a unui tabel
relaţional).
Exemplu:
În exemplul care urmează sunt ilustrate cele trei tipuri de colecţii.
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;

7). 3. Tipul colecţie poate să apară în clauza RETURN a unei funcţii. 2). 2. . Colecţiile pot fi parametri formali într-un subprogram. Declararea tipului TABLE se face respectând următoarea sintaxă: TYPE nume_tip IS TABLE OF {tip_coloană | variabilă%TYPE | nume_tabel. Tablouri indexate Tipul de date index-by table oferă un mecanism pentru prelucrarea tablourilor. DATE sau NUMBER). Tipul colecţie poate fi definit într-un pachet. Perl) pentru a defini această structură de date. PHP. JavaScript. Oracle8 realizează definirea tablourilor de tipuri obiect. Oracle7 asigură definirea tablourilor de înregistrări care pot fi declarate şi utilizate numai în programe PL/SQL. CHAR. se declară tabloul indexat PL/SQL de acest tip. Accesul la elementele individuale ale unei colecţii se face prin utilizarea unui indice. Identificatorul nume_tip este numele noului tip definit care va fi specificat în declararea tabloului PL/SQL. Tablourile indexate PL/SQL trebuie definite în doi paşi: se defineşte tipul TABLE. v_tab_imbri := tab_imbri(5. ORDER BY. v_vector := vector(1. v_tab_index(2) := 23. END. 8. VARCHAR2. ele nu pot să apară în clauzele DISTINCT.coloană%TYPE [NOT NULL] | nume_tabel%ROWTYPE} INDEX BY tip_indexare. GROUP BY. iar tip_coloană este un tip scalar simplu (de exemplu. Observaţii: Deoarece colecţiile nu pot fi comparate (egalitate sau inegalitate). iar Oracle9i permite definirea tablourilor de colecţii. 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. În Oracle9i tipul index-by table este redenumit associative array pentru compatibilitate (de limbaj) cu termenul folosit în alte limbaje de programare (C++.

pentru regăsirea unor valori dintr-o coloană a unei baze de date într-un tablou PL/SQL se utilizează instrucţiunea FETCH (cursoare) sau instrucţiunea de atribuire în cadrul unei secvenţe repetitive LOOP. Oracle9i permite următoarele opţiuni pentru tip_indexare: PLS_INTEGER. Nu sunt permise indexările INDEX BY NUMBER. un tablou indexat este nedens. INDEX BY CHAR(n) sau indexarea după un tip declarat cu %TYPE în care intervine unul dintre tipurile enumerate anterior. trebuie declarată o variabilă în acest scop sau poate fi utilizată o metodă asociată tabloului. Observaţii: Elementele unui tablou indexat nu sunt într-o ordine particulară şi pot fi inserate cu chei arbitrare. NATURAL. Asemănător. atunci se produce excepţia NO_DATA_FOUND. Dacă se doreşte contorizarea numărului de linii. INDEX BY VARCHAR2. Deoarece nu există constrângeri de dimensiune. nici chei). operaţia de adăugare de linii este restricţionată doar de dimensiunea memoriei alocate. Un element al tabloului este nedefinit atâta timp cât nu are atribuită o valoare efectivă. INDEX BY DATE. Până la versiunea Oracle9i unicul tip de indexare acceptat era INDEX BY BINARY_INTEGER. VARCHAR2(n) sau chiar indexarea după un tip declarat cu %TYPE. POSITIVE. Tabloul indexat PL/SQL nu poate fi iniţializat în declararea sa. Dacă se face referire la o linie care nu există. Tablourile pot să apară ca argumente într-o procedură. fie se declară un alt tablou PL/SQL (de acelaşi tip) care nu este iniţializat şi acest tablou vid se asignează tabloului PL/SQL care trebuie şters. După declararea unui tablou se poate face referire la liniile lui prin precizarea valorii cheii primare. Pentru a şterge liniile unui tablou fie se asignează elementelor tabloului valoarea null. dimensiunea tabloului se modifică dinamic. Pentru inserarea unor valori din tablourile PL/SQL într-o coloană a unui tabel de date se utilizează instrucţiunea INSERT în cadrul unei secvenţe repetitive LOOP.3 ştergerea liniilor unui tabel se poate face utilizând metoda DELETE. Un tablou indexat neiniţializat este vid (nu conţine nici valori. Deoarece numărul de linii nu este limitat. Iniţial. INDEX BY INTEGER. . În PL/SQL 2.

org_table(i). END LOOP. END IF. END. org_table org_table_type.cod_org. DBMS_OUTPUT. i NUMBER. ELSE i:=1. DBMS_OUTPUT.COUNT || ' elemente').cod_org := 752. org_table(i).PUT_LINE('tabloul are ' || v_tablou. BEGIN IF org_table. Să se şteargă elementele tabloului.tip := 'persoana fizica'.COUNT <>0 THEN i := org_table.PUT_LINE(v_tablou(i)).câmp.Exemplu: Să se definească un tablou indexat PL/SQL având elemente de tipul NUMBER.. org_table(i). Să se iniţializeze un element al tabloului şi să se introducă în tabelul organizator.LAST+1. Să se şteargă tabloul. org_table(i).adresa := 'Calea Plevnei 18 Sibiu'. BEGIN FOR i IN 1. Să se introducă 20 de elemente în acest tablou.nume.20 LOOP v_tablou(i) := i*i. v_tablou tablou_numar. DECLARE TYPE tablou_numar IS TABLE OF NUMBER INDEX BY PLS_INTEGER. DECLARE TYPE org_table_type IS TABLE OF organizator%ROWTYPE INDEX BY BINARY INTEGER. --aceasta atribuire da eroarea PLS-00382 FOR i IN v_tablou.nume := 'Grigore Ion'.LAST LOOP v_tablou(i) := NULL. --v_tablou := NULL. Referirea la un element al tabloului se face prin forma clasică: tabel(index). Exemplu: Să se definească un tablou de înregistrări având tipul celor din tabelul organizator. INSERT INTO organizator VALUES (org_table(i). În PL/SQL este folosit frecvent tipul tablou de înregistrări. END LOOP.FIRST.v_tablou. org_table(i). ..

NVARCHAR2.COUNT)||' elemente'). Vectori Vectorii (varray) sunt structuri asemănătoare vectorilor din limbajele C sau Java. -. atunci când numărul maxim de elemente din partea „many“ este cunoscut şi ordinea elementelor este importantă. v_sec secventa := secventa ('alb'. org_table(i). BEGIN . se utilizează pentru modelarea relaţiilor one-to-many. BLOB. org_table.sterge toate elementele DBMS_OUTPUT. NATURAL. În Oracle9i sunt permise (pentru tip_elemente) tipurile TABLE sau alt tip VARRAY. Tip_elemente este un tip scalar PL/SQL.adresa. NCLOB. POSITIVE. PLS_INTEGER. în sensul că acesta nu poate să fie BOOLEAN. STRING. Exemplu: DECLARE TYPE secventa IS VARRAY(5) OF VARCHAR2(10). În special. LONG. Spre deosebire de tablourile indexate.INSERT INTO organizator -. 'verde'). NATURALN. 'rosu'. Tipul de date vector este declarat utilizând sintaxa: TYPE nume_tip IS {VARRAY | VARYING ARRAY} (lungime_maximă) OF tip_elemente [NOT NULL]. BINARY_INTEGER. 'negru'. acest tip poate fi definit utilizând atributele %TYPE sau %ROWTYPE. POSITIVEN. de la 0 (vid) la numărul maxim specificat obligatoriu în definiţia sa. Există restricţii referitoare la tipul elementelor. END. tip obiect cu atribute TABLE sau VARRAY. LONG RAW. REF CURSOR. NCHAR. De asemenea. Limita inferioară a indicelui este 1. vectorii au o dimensiune maximă (constantă) stabilită la declarare. iar lungime_maximă reprezintă numărul maxim de elemente din vector. tip înregistrare sau tip obiect.tip). Vectorii reprezintă structuri dense. CLOB. Identificatorul nume_tip este numele tipului de date vector.sau folosind noua facilitate Oracle9i -. -. Vectorul poate conţine un număr variabil de elemente.VALUES (org_table(i)).PUT_LINE('Dupa aplicarea metodei DELETE sunt '||TO_CHAR(org_table. SIGNTYPE. org_table(i).DELETE. Fiecare element are un index care dă poziţia sa în vector şi care este folosit pentru accesarea elementelor particulare. tip obiect cu atribute BLOB sau CLOB.

Tablouri imbricate Tablourile imbricate (nested table) sunt tablouri indexate a căror dimensiune nu este stabilită. LONG.EXTEND. Numărul maxim de linii ale unui tablou imbricat este dat de capacitatea maximă 2 GB. v_sec. Exemplu: DECLARE TYPE numartab IS TABLE OF NUMBER. NVARCHAR2. aceste tablouri sunt structuri dense. În Oracle9i sunt permise (pentru tip_elemente) tipurile TABLE sau alt tip VARRAY. când se regăseşte tabloul în variabile PL/SQL. tip obiect cu atributele TABLE sau VARRAY. în sensul că acesta nu poate să fie BOOLEAN.extinderea la 6 elemente va genera eroarea ORA-06532 v_sec. SIGNTYPE. Valorile de acest tip: -. Identificatorul nume_tip reprezintă numele noului tip de date tablou imbricat. Dar.se creeaza un tablou cu un singur element . Există restricţii referitoare la tipul elementelor. NATURALN.au excepţii predefinite proprii. respectiv %ROWTYPE. Un tablou imbricat este o mulţime neordonată de elemente de acelaşi tip. iar tip_elemente este tipul fiecărui element din tabloul imbricat. tabloul poate fi vizualizat ca un tabel multicoloană. REF CURSOR. LONG RAW. Iniţial. Comanda de declarare a tipului de date tablou imbricat are sintaxa: TYPE nume_tip IS TABLE OF tip_ elemente [NOT NULL]. POSITIVE. dar se poate ca în urma prelucrării să nu mai aibă indici consecutivi. care poate fi un tip definit de utilizator sau o expresie cu %TYPE. NCLOB. NATURAL. având câte o coloană pentru fiecare atribut al tipului obiect. iar dacă aceasta este de tip obiect.EXTEND. -.pot fi stocate în baza de date. Sistemul Oracle nu stochează liniile unui tablou imbricat într-o ordine particulară. liniile vor avea indici consecutivi începând cu valoarea 1. -. NCHAR. STRING. BINARY_INTEGER. POSITIVEN. v_sec (3) := 'rosu'. Tabloul imbricat are o singură coloană. END. -. -.adauga un element null v_sec(5) := 'albastru'. PLS_INTEGER.pot fi prelucrate direct în instrucţiuni SQL -.

Adică. iar pentru tabele imbricate domeniul indexului este 1. Mai exact. vectorii şi tablourile imbricate pot să apară în definirea tabelelor bazei de date.se creeaza un tablou cu 4 elemente v_tab_2 numartab := numartab(7. pot fi prelucrate prin comenzi SQL. Dacă se încearcă să se adauge un element la un tablou imbricat null.PUT_LINE (v_tab_2(j) || ' '). -..5). Dacă un tablou imbricat (sau un vector) este declarat. PL/SQL apelează un constructor numai în mod explicit. Prin urmare. Observaţii: Spre deosebire de tablourile indexate. Prin urmare. Tabelele indexate nu au constructori. Când este creat un tablou indexat care nu are încă elemente. -. vectorii şi tablourile imbricate sunt iniţializate cu ajutorul constructorului. tipul este tablou imbricat. dar nu are încă nici un element (nu este iniţializat). cum poate fi iniţializat un tablou imbricat? Ca şi obiectele. BEGIN v_tab_1(1) := 57. Constructorul primeşte ca argumente o listă de valori de tip tip_elemente. colecţia este null.2147483647. Tablourile imbricate trebuie iniţializate şi/sau extinse pentru a li se adăuga elemente.2147483647.4 LOOP DBMS_OUTPUT. se va genera eroarea „ORA .06531: reference to uninitialized collection“ care corespunde excepţiei predefinite COLLECTION_IS_NULL. END LOOP.4. Acesta are acelaşi nume ca şi tipul colecţiei referite. de la 1 la numărul de valori date ca . dacă această clauză lipseşte.se creeaza un tablou fara nici un element v_tab_3 numartab := numartab(). Se observă că singura diferenţă sintactică între tablourile indexate şi cele imbricate este absenţa clauzei INDEX BY BINARY_INTEGER. FOR j IN 1. Tablourile imbricate. pentru tablouri imbricate poate fi utilizat operatorul IS NULL. el este automat iniţializat (atomic) null... Elementele sunt numerotate în ordine. END. nu elementele sale. Tablourile indexate pot avea indice negativ. domeniul permis pentru index fiind –2147483647.9. spre deosebire de tablourile indexate. v_tab_1 numartab := numartab(-7). el este vid.

END IF. BEGIN IF tab1 IS NULL THEN DBMS_OUTPUT. BEGIN alfa(1) := 77. END IF. dar tabloul nu este null. END. poate primi elemente */ tab2 alfa := alfa() . . Dimensiunea iniţială a colecţiei este egală cu numărul de argumente date în constructor. IF tab2 IS NULL THEN DBMS_OUTPUT. În urma execuţiei acestui bloc se obţine următorul rezultat: tab1 este NULL tab2 este NOT NULL Excepţiile semnificative care apar în cazul utilizării incorecte a colecţiilor: Exemplu: DECLARE TYPE numar IS TABLE OF INTEGER. /* creeaza un tablou cu un element care este null. dar care are valoarea not null. ELSE DBMS_OUTPUT. alfa numar. 37).PUT_LINE('tab1 este NULL').parametrii constructorului. când aceasta este iniţializată. Exemplu: DECLARE TYPE alfa IS TABLE OF VARCHAR2(50).PUT_LINE('tab1 este NOT NULL').PUT_LINE('tab2 este NOT NULL'). Atunci când constructorul este fără argumente. va crea o colectie fără nici un element (vida). el este initializat.creeaza un tablou (atomic) null tab1 alfa .declanseaza exceptia COLLECTION_IS_NULL alfa := numar(15. Exemplul următor este concludent în acest sens. ELSE DBMS_OUTPUT. -.PUT_LINE('tab2 este NULL'). Pentru vectori nu poate fi depăşită dimensiunea maximă precizată la declarare. -. 26.

prin urmare colecţii ale căror elemente sunt.exceptia SUBSCRIPT_OUTSIDE_LIMIT alfa. tablou imbricat sau vector de un tip definit de utilizator care are un atribut de tip tablou imbricat sau vector. tipul trebuie stocat în dicţionarul datelor.exceptia NO_DATA_FOUND … END. Observaţii: .declanseaza exceptia VALUE_ERROR alfa(0) := 7.DELETE(1). Înainte de utilizare. IF alfa(1) = 1 THEN … -. /* declanseaza exceptia VALUE_ERROR deoarece indicele nu este convertibil la intreg */ alfa(4) := 47. variabilelor PL/SQL. -. Tablourile imbricate şi vectorii pot fi utilizaţi drept câmpuri în tabelele bazei. atributelor unui obiect într-un tabel obiect. tablou imbricat de tablouri imbricate. deci trebuie declarat prin comanda: CREATE TYPE nume_tip AS {TABLE | VARRAY} OF tip_elemente. /* declanseaza exceptia SUBSCRIPT_BEYOND_COUNT deoarece indicele se refera la un element neinitializat */ alfa(null) := 7. alfa('P') := 77. Aceste structuri complexe pot fi utilizate ca tipuri de date pentru definirea: coloanelor unui tabel relaţional. alfa(2) := 10*alfa(1). vectori de tablouri imbricate. tablou imbricat de vectori. colecţii. După crearea tabelului (prin comanda CREATE TABLE). În felul acesta pot fi definite structuri complexe: vectori de vectori. -. alfa(1) := ASCII('X'). în mod direct sau indirect. Aceasta presupune că fiecare înregistrare din tabelul respectiv conţine un obiect de tip colecţie. pentru fiecare câmp de tip tablou imbricat din tabel este necesară clauza de stocare: NESTED TABLE nume_câmp STORE AS nume_tabel. Colecţii pe mai multe niveluri În Oracle9i se pot construi colecţii pe mai multe niveluri (multilevel collections).

var3 epsi := epsi(teta(31.acest nou element adaugat va fi 777 END. / DECLARE TYPE gama IS TABLE OF VARCHAR2(20).4321).se adauga un element la al 4-lea element vbet(4)(5) := 777. -.3.EXTEND.5)). TYPE teta IS VARRAY(10) OF INTEGER.6. --initializare vbet beta := beta(valf. tablouri imbricate pe mai multe niveluri şi tablouri indexate pe mai multe niveluri.valf). DECLARE TYPE alfa IS VARRAY(10) OF INTEGER.se adauga un element de tip vector la vbet vbet(5) := alfa(56.4321 este inlocuit cu 7 vbet(4).EXTEND. Numărul nivelurilor de imbricare este limitat doar de capacitatea de stocare a sistemului. valf alfa := alfa(12. -. TYPE epsi IS TABLE OF teta. -. BEGIN i := vbet(2)(3). -. TYPE delta IS TABLE OF gama. Exemplu: În exemplele care urmează sunt definite trei structuri complexe şi sunt prezentate câteva modalităţi de utilizare ale acestora. -. TYPE beta IS VARRAY(10) OF alfa.5).15). var2. var1 alfa. var1 gama := gama('alb'. Pentru a accesa un element al colecţiei incluse sunt utilizate două seturi de paranteze.'negru'). Exemplele se referă la vectori pe mai multe niveluri.alfa(2. var2(2) := var2(1).alfa(55.77.teta(1.sterge primul element din var2 /* sterge primul sir de caractere din al doilea . Obiectele de tipul colecţie pe mai multe niveluri nu pot fi comparate.EXTEND. BEGIN var2. -. i integer.i va lua valoarea 77 vbet.DELETE(1).4).77). vbet(4) := alfa(44.33).31. vbet(4)(4) := 7. var2 delta := delta(var1).66.

fie pot fi prelucrate elemente individuale dintr-o colecţie (piecewise updates) utilizând operatori SQL sau anumite facilităţi oferite de PL/SQL. var3 delt. CREATE TABLE gal_ope ( . Comanda DELETE poate şterge o linie ce conţine o colecţie. TYPE gama IS VARRAY(10) OF VARCHAR2(30).asignez un tablou null var2(40)(3) := 55. END. eroare nu exista element 40 in var2 var2(40) := var5. -. tablou al tabloului imbricat */ var2(2). Exemplu: CREATE OR REPLACE TYPE operalist AS VARRAY(10) OF NUMBER(4).'negru'). var3(39) := gama(77. Colecţia trebuie să fie creată şi iniţializată anterior. var5 alfa. TYPE delt IS TABLE OF gama INDEX BY BINARY_INTEGER. -. var4(2) := 222.var2(40)(3) := 55. -. TYPE beta IS TABLE OF alfa INDEX BY BINARY_INTEGER. Comanda UPDATE este folosită pentru modificarea unei colecţii stocate. var1 gama := gama('alb'. Colecţiile din baza de date pot fi regăsite în variabile PL/SQL. var4 alfa. var4(42) := 333. Comanda INSERT permite inserarea unei colecţii într-o linie a unui tabel.76.corect END.tablou vid BEGIN var4(1) := 324. -. var2 beta.908). utilizând comanda SELECT. / Prelucrarea colecţiilor O colecţie poate fi exploatată fie în întregime (atomic) utilizând comenzi LMD. var2(27) := var4.DELETE(1).89. / DECLARE TYPE alfa IS TABLE OF INTEGER INDEX BY BINARY_INTEGER.

'Alfa'. În fiecare caz pot fi utilizate numai comenzi SQL. END. dar colecţiile trebuie să fie de acelaşi tip. Un vector stocat într-un tabel este prelucrat ca un întreg (nu pot fi modificate elemente individuale). deoarece pot fi prelucrate fie în întregime. Prin urmare. v_info gal_ope. 'Cubism'. INSERT INTO gal_ope VALUES (2345. nume_galerie VARCHAR2(20). BEGIN INSERT INTO gal_ope VALUES (4567. În Oracle8i a fost introdus operatorul TABLE. Dacă unei colecţii i se asignează o colecţie atomic null. Tablourile imbricate depuse în baza de date sunt mai flexibile. SELECT info INTO v_info FROM gal_ope WHERE cod_galerie = v_cod. operalist(4567. Se pot face reactualizări sau inserări asupra tablourilor imbricate care dau o valoare nouă pentru întreaga colecţie sau se pot face inserări. v_info_op operalist := operalist (7007). Pentru referirea acestora trebuie utilizate comenzi procedurale PL/SQL. info operalist). FETCH. instrucţiunea de atribuire sau prin apelul unui subprogram. 'Impresionisti'. ce permite prelucrarea elementelor unui tablou imbricat care este stocat într-un tabel. 999). UPDATE. v_opera). el trebuie selectat într-o variabilă PL/SQL a cărei valoare poate fi modificată şi apoi reinserată în tabel. v_info_op).4987)).info%TYPE. aceasta devine atomic null şi trebuie reiniţializată. INSERT INTO gal_ope VALUES (123. O colecţie poate fi asignată altei colecţii prin comenzile INSERT. DECLARE v_opera operalist := operalist (777. ştergeri. UPDATE sau DELETE. elementele individuale ale unui vector nu pot fi referite în comenzile INSERT. reactualizări de elemente particulare din colecţie. fie ca elemente individuale. v_cod gal_ope. 888. SELECT.cod_galerie%TYPE := 2345. Pentru a modifica un vector. cod_galerie NUMBER(10). . Operatorul permite interogarea unei colecţii în clauza FROM (la fel ca un tabel).

Pentru prelucrarea unei colecţii locale se poate folosi şi operatorul CAST. 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 apărut. CAST converteşte colecţia locală la tipul specificat. lista din clauza SELECT a subcererii trebuie să aibă un singur articol. operaţiile pot fi făcute numai atomic. iar în acest caz. o colecţie poate fi prelucrată ca şi cum ar fi un tabel SQL al bazei de date.cod_opera. Metodele sunt apelate prin expresia: . fie este o subinterogare referitoare la o colecţie. iar pentru vectori pe mai multe niveluri. care operează asupra unei colecţii. INSERT INTO TABLE (SELECT info FROM opera WHERE titlu = 'Primavara') VALUES ('Pictura moderna'. Prin urmare. CAST are forma sintactică: CAST (nume_colecţie AS tip_colecţie) Operanzii lui CAST sunt o colecţie declarată local (de exemplu. Pentru tablouri imbricate pe mai multe niveluri. într-un bloc PL/SQL anonim) şi un tip colecţie SQL. şi nu din SQL. TABLE (a. Metodele unei colecţii PL/SQL oferă subprograme numite metode (methods).* FROM opera a.info) b. Listarea codului fiecărei opere de artă şi a colecţiei articolelor referitoare la aceste opere de artă se face prin comanda: SELECT a. operaţiile LMD pot fi făcute atomic sau pe elemente individuale. 'Orizonturi'). b. Exemplu: Se presupune că tabelul opera are o coloană info de tip tablou imbricat. Operandul lui TABLE este: fie numele unei colecţii şi atunci rezultatul operatorului este tot o colecţie. Să se insereze o linie în tabelul imbricat. operatorul TABLE returnează o singură valoare (coloană) care este un tablou imbricat sau un vector. În felul acesta. Acestea pot fi apelate numai din comenzi procedurale.

20.alfa. respectiv ultimului element din colecţie. alfa nume := nume(10.LAST . DELETE şterge toate elementele unei colecţii PL/SQL (nu este validă pentru tipul varrays).nume_metodă [ (parametri) ] Metodele care se pot aplica colecţiilor PL/SQL sunt următoarele: COUNT returnează numărul curent de elemente ale unei colecţii PL/SQL. iar dacă nu există un astfel de element returnează valoarea null. -. DELETE(n) şterge elementul n dintr-o colecţie PL/SQL. nu poate fi utilizată pentru a iniţializa o colecţie atomic null. Bulk bind În exemplul care urmează. returnează FALSE. nume_colecţie.. respectiv precedent celui de rang n din colecţie. EXTEND adaugă elemente la sfârşitul unei colecţii: EXTEND adaugă un element null la sfârşitul colecţiei. metoda TRIM operează asupra dimensiunii interne a tabloului imbricat. n) şterge toate elementele având indecşii între m şi n. EXTEND(n. i) adaugă n copii ale elementului de rang i (nu este validă pentru tipul index-by tables). NEXT(n). Similar metodei EXTEND. EXISTS este singura metodă care poate fi aplicată unei colecţii atomice null. EXTEND(n) adaugă n elemente null. Exemplu: DECLARE TYPE nume IS VARRAY(20) OF NUMBER. TRIM(n) şterge ultimele n elemente (nu este validă pentru tipul index-by tables). DELETE(m. Orice altă metodă declanşează excepţia COLLECTION_IS_NULL. LIMIT returnează numărul maxim de elemente ale unei colecţii (cel de la declarare) pentru tipul vector şi null pentru tablouri imbricate (nu este validă pentru tipul index-by tables). EXISTS(n) returnează TRUE dacă există al n-lea element al unei colecţii PL/SQL (altfel. FIRST. comanda DELETE este trimisă motorului SQL pentru fiecare iteraţie a comenzii FOR.coduri ale galeriilor BEGIN FOR j IN alfa. chiar dacă elementul este null). TRIM şterge elementele de la sfârşitul unei colecţii: TRIM şterge ultimul element. PRIOR(n) returnează indicele elementului următor. LAST returnează indicele primului.FIRST.70).

coduri ale galeriilor BEGIN … FORALL j IN alfa.LAST DELETE FROM opera ..20.FIRST. Comanda FORALL are sintaxa: FORALL index IN lim_inf. -. Începând cu Oracle8i există posibilitatea ca toate liniile unei colecţii să fie transferate simultan printr-o singură operaţie. un tip de date şi o valoare. Procedeul este numit bulk bind şi este realizat cu ajutorul comenzii FORALL. În exemplul care urmează este optimizată problema anterioară. Comanda_sql este una din comenzile INSERT. în sensul că instrucţiunea DELETE este trimisă motorului SQL o singură dată. ca rezultat al unei interogări). Motorul SQL execută comanda_sql o singură dată pentru toate valorile indexului. datele care trebuie manipulate aparţin unei colecţii. pentru toate liniile colecţiei. Variabila index poate fi referită numai în comanda FORALL şi numai ca indice de colecţie. În timpul compilării. De multe ori. DELETE FROM opera WHERE cod_galerie = alfa (j). Pentru a realiza mai rapid această operaţie. END. Exemplu: DECLARE TYPE nume IS VARRAY(20) OF NUMBER. Acest proces este numit binding. Comenzile SQL din blocurile PL/SQL sunt trimise motorului SQL pentru a fi executate.lim_sup comanda_sql.. UPDATE. transferul (în ambele sensuri) între SQL şi PL/SQL are loc pentru fiecare linie a colecţiei. alfa nume := nume(10.70). END LOOP. compilatorul PL/SQL asociază identificatorii cu o adresă. ar trebui să existe posibilitatea de a şterge (prelucra) întreaga colecţie şi nu elemente individuale. Motorul SQL poate trimite înapoi date motorului PL/SQL (de exemplu. DELETE care referă elementele uneia sau mai multor colecţii. ce poate fi folosită cu orice tip de colecţie. Prin urmare.alfa. Tehnica care permite acest lucru este cunoscută sub numele bulk bind. iar colecţia este iterată printr-un ciclu FOR.

de exemplu. y NUMBER).. ttt nume:= nume(8. toate elementele colecţiei din domeniul precizat trebuie să existe (dacă.FIRST.3 INSERT INTO exemplu VALUES(7. 100).corect FORALL i IN 1. UPDATE.---------- 8 100 10 100 12 100 . / PL/SQL procedure successfully completed. DELETE trebuie să refere cel puţin o colecţie. indicii colecţiilor nu pot să fie expresii şi trebuie să aibă valori continue. altfel apare eroarea “this feature is not supported in client-side programs”. -.exceptie nu e colectie END.10. comenziile INSERT. Pentru utilizarea comenzii FORALL este necesară respectarea următoarelor restricţii: comanda poate fi folosită numai în programe server-side. -.LAST INSERT INTO exemplu VALUES(ttt(i). X Y --------. BEGIN FORALL i IN ttt.12).. 9). atunci este semnalată o eroare). Exemplu: CREATE TABLE exemplu (x NUMBER. END. WHERE cod_galerie = alfa (j).ttt. un element a fost şters. DECLARE TYPE nume IS TABLE OF NUMBER. SQL> select * from exemplu.

SQL> select * from exemplu.12).eroare dubla (expresie si >LAST) DECLARE TYPE alfa IS TABLE OF NUMBER.cod_opera%TYPE. -.nume_colecţie]… DECLARE TYPE tip1 IS TABLE OF opera. xx alfa := alfa(8.titlu%TYPE. Regăsirea rezultatului unei interogări în colecţii (înainte de a fi trimisă motorului PL/SQL) se poate obţine cu ajutorul clauzei BULK COLLECT. TYPE tip2 IS TABLE OF opera.FIRST.eroare END.xx. Clauza poate să apară în: comenzile SELECT INTO (cursoare implicite). PL/SQL procedure successfully completed..LAST DELETE FROM exemplu WHERE x = xx(i). DELETE. clauza RETURNING INTO a comenzilor INSERT. no rows selected Dacă există o eroare în procesarea unei linii printr-o operaţie LMD de tip bulk.LAST DELETE FROM carte WHERE codel = gama(i+1). Clauza are următoarea sintaxă: …BULK COLLECT INTO nume_colecţie [. UPDATE. BEGIN FORALL i IN xx.10.gama. numai acea linie va fi rollback. -.FIRST. . comenzile FETCH INTO (cursoare explicite).. FORALL i IN gama.

8.beta FROM opera. ttt nume:= nume(8. BEGIN … /* motorul SQL incarca in intregime coloanele cod_opera si titlu in tabelele imbricate. titlu BULK COLLECT INTO alfa. … END..10.ttt.LAST INSERT INTO exemplu VALUES(ttt(i). 100).12.FIRST. au fost sterse prin exemplul de mai sus) DECLARE TYPE nume IS TABLE OF NUMBER. Acelasi exemplu pentru tabelul exemplu creat mai sus. … /* daca exista n opere de arta in stare buna.14. atunci alfa va contine codurile celor n opere */ DELETE FROM opera WHERE stare = 'buna' RETURNING cod_opera BULK COLLECT INTO alfa. / PL/SQL procedure successfully completed.12. beta tip2. inainte de a returna tabelele motorului PL/SQL */ SELECT cod_opera.---------- 8 100 10 100 12 100 14 100 12 100 .12). end. X Y ---------. BEGIN FORALL i IN ttt. alfa tip1. SQL> select * from exemplu. Inseram sase inregistrari (toate inreg.

SQL> DECLARE 2 TYPE tip1 IS TABLE OF exemplu.first . 10 END. 7 For I in xx. trebuie subliniat că ele nu pot fi folosite simultan în comanda SELECT. 11 / .last loop 10 Dbms_output. 13 / alfa(1) = 12 alfa(2) = 12 alfa(3) = 12 PL/SQL procedure successfully completed.x%TYPE. alfa.last LOOP 8 DBMS_OUTPUT. Comanda FORALL se poate combina cu clauza BULK COLLECT.put_line('alfa('||I || ') = ' || alfa(i)). 7 DELETE FROM exemplu WHERE x=12 8 RETURNING x BULK COLLECT INTO alfa. xx. Totuşi. 9 End loop. 9 For I in alfa. Motorul SQL incarca toate liniile unei coloane.first .. 12 END.salary%TYPE. 3 xx alfa. 4 BEGIN 5 SELECT salary BULK COLLECT INTO xx 6 FROM employees WHERE ROWNUM <= 25. 11 End loop. Cum se poate limita numarul de linii procesate? SQL> DECLARE 2 TYPE alfa IS TABLE OF employees. 3 alfa tip1. 8 100 12 100 7 rows selected.PUT_LINE('XX('||I||')=' || xx(i)). 4 BEGIN 5 SELECT x BULK COLLECT INTO alfa 6 FROM exemplu..

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. .

. pentru a procesa o comandă SQL. întoarce mai multe linii ca rezultat. Când este procesată o instrucţiune SQL. %NOTFOUND. declarate şi definite de către utilizator atunci când o cerere (SELECT). sau prin nume_cursor%nume_atribut. Prin intermediul cursoarelor. deoarece un cursor implicit este închis de sistem imediat după executarea instrucţiunii SQL asociate). care este de tip boolean şi ia valoarea TRUE dacă ultima operaţie de încărcare (FETCH) dintr-un cursor a avut succes (în cazul cursoarelor explicite) sau dacă instrucţiunea SQL a întors cel puţin o linie (în cazul cursoarelor implicite). Zona conţine informaţii necesare procesării comenzii. un program PL/SQL poate controla zona context şi transformările petrecute în urma procesării comenzii. Există două tipuri de cursoare: implicite. acest atribut are întotdeauna valoarea FALSE. Atributele pot fi referite prin expresia SQL%nume_atribut. explicite. în cazul unui cursor explicit. mulţimea rândurilor rezultate în urma execuţiei acestei comenzi (active set). care este de tip boolean şi are semnificaţie opusă faţă de cea a atributului %FOUND. dar nu pot fi utilizate în comenzi SQL. Gestiunea cursoarelor în PL/SQL Sistemul Oracle foloseşte. care apare într-un bloc PL/SQL. care este de tip întreg şi reprezintă numărul liniilor încărcate de cursor. %FOUND. Ele pot să apară în comenzi PL/SQL. în cazul cursoarelor implicite. Lista atributelor este următoarea: %ROWCOUNT. un pointer către reprezentarea internă a comenzii. 4. generate de server-ul Oracle când în partea executabilă a unui bloc PL/SQL apare o instrucţiune SQL. în cazul unei cereri. în secţiunea de tratare a erorilor. server-ul Oracle deschide această zonă de memorie în care comanda este analizată sintactic şi este executată. care este de tip boolean şi indică dacă un cursor este deschis (în cazul cursoarelor implicite. %ISOPEN. o zonă de memorie cunoscută sub numele de zonă context (context area). cum ar fi: numărul de rânduri procesate de instrucţiune. în funcţii. Atât cursoarele implicite cât şi cele explicite au o serie de atribute ale căror valori pot fi folosite în expresii. Un cursor este un pointer la această zonă context.

FOR j IN 1. care este asociat comenzii FORALL. DELETE sau SELECT INTO executată.PUT_LINE ('Pentru artistul avand codul ' || beta(j) || ' au fost inserate ' || SQL%BULK_ROWCOUNT(j) || inregistrari (opere de arta)'). SQL%NOTFOUND. deci folosesc acelaşi domeniu.cod_opera FROM opera WHERE cod_artist = beta(j). UPDATE. atunci atributul returnează valoarea 0. beta alfa. END LOOP. iar după fiecare iteraţie atributul %BULK_ROWCOUNT returnează numărul acestor linii inserate. Exemplu: În exemplul care urmează. SET SERVEROUTPUT ON DECLARE TYPE alfa IS TABLE OF NUMBER. comanda FORALL inserează un număr arbitrar de linii la fiecare iteraţie.. SQL%ISOPEN) furnizează informaţii referitoare la ultima comandă INSERT. Atributele scalare ale cursorului implicit (SQL%ROWCOUNT. Comanda FORALL şi atributul %BULK_ROWCOUNT au aceiaşi indici. BEGIN SELECT cod_artist BULK COLLECT INTO beta FROM artist. / SET SERVEROUTPUT OFF .beta.. Dacă %BULK_ROWCOUNT(j) este zero. DELETE sau UPDATE. FORALL j IN 1.COUNT INSERT INTO tab_art SELECT cod_artist. Dacă a j-a execuţie nu afectează nici o linie.Cursoare implicite Când se procesează o comandă LMD. Înainte ca Oracle să deschidă cursorul SQL implicit. atributul %FOUND este FALSE. În Oracle9i. Componenta %BULK_ROWCOUNT(j) conţine numărul de linii procesate de a j-a execuţie a unei comenzi INSERT. pentru cursoare implicite a fost introdus atributul compus %BULK_ROWCOUNT. END. atributele acestuia au valoarea null.COUNT LOOP DBMS_OUTPUT. SQL%FOUND.beta. DBMS_OUTPUT. motorul SQL deschide un cursor implicit.PUT_LINE ('Numarul total de inregistrari inserate este '||SQL%ROWCOUNT). Atributul are semantica unui tablou indexat.

pentru a utiliza un cursor. el trebuie declarat în secţiunea declarativă a programului.Cursoare explicite Pentru gestiunea cursoarelor explicite sunt necesare următoarele etape: declararea cursorului (atribuirea unui nume şi asocierea cu o comandă SELECT). închiderea cursorului (eliberarea resurselor relative la cursor). cursorul trebuie să fie închis. OPEN. Dacă nu mai este necesar în restul programului. trebuie deschis în partea executabilă. FETCH şi CLOSE. Declaraţia CURSOR are următoarea formă sintactică: CURSOR nume_cursor IS comanda_select . deschiderea cursorului pentru cerere (executarea interogării asociate şi determinarea mulţimii rezultat). DECLARE declarare cursor BEGIN deschidere cursor (OPEN) WHILE rămân linii de recuperat LOOP recuperare linie rezultat (FETCH) … END LOOP închidere cursor (CLOSE) … END. urmând să fie utilizat apoi pentru extragerea datelor. recuperarea liniilor rezultatului în variabile PL/SQL. Declararea unui cursor explicit Prin declaraţia CURSOR în cadrul comenzii DECLARE este definit un cursor explicit şi este precizată structura cererii care va fi asociată acestuia. Pentru a controla activitatea unui cursor sunt utilizate comenzile DECLARE. Prin urmare.

Numele cursorului este un identificator unic în cadrul blocului. atunci pentru expresia respectivă trebuie utilizat un alias. iar câmpul expresie se va referi prin acest alias. Observaţii: Comanda SELECT care apare în declararea cursorului. La deschiderea unui cursor se realizează următoarele operaţii: se evaluează cererea asociată (sunt examinate valorile variabilelor de legătură ce apar în declaraţia cursorului). FETCH realizează următoarele operaţii: avansează pointer-ul la următoarea linie în mulţimea activă (pointer-ul poate avea doar un sens de deplasare de la prima spre ultima înregistrare). Dacă se cere procesarea liniilor într-o anumită ordine. identifică mulţimea liniilor rezultat şi poziţionează cursorul înaintea primei linii. atunci în cerere este utilizată clauza ORDER BY. citeşte datele liniei curente în variabile PL/SQL. iar comanda_select este cererea SELECT care va fi procesată. având în vedere valorile de la pasul anterior. Ele sunt considerate variabile de legătură. pointer-ul este poziţionat la prima linie din mulţimea activă. Identificatorul nume_cursor este numele cursorului. dacă pointer-ul este poziţionat la sfârşitul mulţimii active atunci se iese din bucla cursorului. Variabilele care sunt referite în comanda de selectare trebuie declarate înaintea comenzii CURSOR. Deschiderea unui cursor explicit Comanda OPEN execută cererea asociată cursorului. Comanda FETCH are următoarea sintaxă: . Încărcarea datelor dintr-un cursor explicit Comanda FETCH regăseşte liniile rezultatului din mulţimea activă. este determinată mulţimea rezultat (active set) prin executarea cererii SELECT. Identificatorul nume_cursor reprezintă numele cursorului ce va fi deschis. Deschiderea unui cursor se face prin comanda: OPEN nume_cursor. care nu poate să apară într-o expresie şi căruia nu i se poate atribui o valoare. nu trebuie să includă clauza INTO. Dacă în lista comenzii SELECT apare o expresie.

Dacă se încearcă încărcarea datelor dintr-un cursor închis. FETCH alfa BULK COLLECT INTO cod1. care are următoarea sintaxă: CLOSE nume_cursor. BEGIN OPEN alfa. La un moment dat. Variabila sau lista de variabile din clauza INTO trebuie să fie compatibilă (ca ordine şi tip) cu lista selectată din cererea asociată cursorului. Prin această operaţie. … CLOSE alfa. Cursorul va fi închis prin comanda CLOSE. Identificatorul nume_cursor este numele unui cursor deschis anterior. PL/SQL este informat că programul a terminat folosirea cursorului şi resursele asociate acestuia pot fi eliberate. Identificatorul nume_cursor reprezintă numele unui cursor declarat şi deschis anterior. Exemplu: În exemplul care urmează se încarcă date dintr-un cursor în două colecţii.titlu%TYPE. utilizând clauza BULK COLLECT. în ultimele versiuni Oracle pot fi încărcate mai multe linii (la un moment dat) într-o colecţie. Totuşi. DECLARE TYPE ccopera IS TABLE OF opera. atunci apare excepţia . titlu FROM opera WHERE stil = 'impresionism'. nume_variabilă] … | nume_înregistrare}. Aceste resurse includ spaţiul utilizat pentru memorarea mulţimii active şi spaţiul temporar folosit pentru determinarea mulţimii active. titlu1 ctopera. cursorul trebuie închis. Închiderea unui cursor explicit După ce a fost procesată mulţimea activă. FETCH nume_cursor INTO {nume_variabilă [. cod1 ccopera. TYPE ctopera IS TABLE OF opera. CURSOR alfa IS SELECT cod_opera.cod_opera%TYPE. Pentru a reutiliza cursorul este suficient ca acesta să fie redeschis. titlu1. END. comanda FETCH regăseşte o singură linie.

dar acest lucru nu este indicat. v_an_nas.INVALID_CURSOR.an_nastere%TYPE.nume%TYPE. INSERT INTO temp VALUES (v_nume || TO_CHAR(v_an_nas)). Valorile atributelor unui cursor explicit sunt prezentate în următorul tabel: %FOUND %ISOPEN %NOTFOUND %ROWCOUNT OPEN Înainte Excepţie False Excepţie Excepţie După Null True Null 0 Prima Înainte Null True Null 0 Încărcare După True True False 1 Următoarea Înainte True True False 1 încărcare După True True False Depinde de date Ultima Înainte True True False Depinde de date încărcare După False True True Depinde de date CLOSE Înainte False True True Depinde de date După Excepţie False Excepţie Excepţie . DECLARE v_nume artist. deoarece este bine ca resursele să fie eliberate. Un bloc PL/SQL poate să se termine fără a închide cursoarele. END. an_nastere FROM artist. END LOOP. Exemplu: Pentru toţi artiştii care au opere de artă expuse în muzeu să se insereze în tabelul temp informaţii referitoare la numele acestora şi anul naşterii. CLOSE info. v_an_nas artist. CURSOR info IS SELECT DISTINCT nume. LOOP FETCH info INTO v_nume. COMMIT. EXIT WHEN info%NOTFOUND. BEGIN OPEN info.

Cursorul va fi declarat în specificaţia pachetului prin comanda: CURSOR nume_cursor [ (parametru [. După prima încărcare. Pot fi utilizate cicluri cursor speciale care folosesc subcereri. dacă mulţimea rezultat este vidă. numit ciclu cursor. WHILE. iar valoarea atributului %ROWCOUNT pentru terminarea ciclului. %FOUND va fi FALSE. Variabila nume_înregistrare (care controlează ciclul) nu trebuie declarată. CREATE PACKAGE BODY exemplu AS CURSOR alfa (p_valoare_min NUMBER) RETURN opera%ROWTYPE IS SELECT * FROM opera WHERE valoare > p_valoare_min. prin care la fiecare iteraţie se va încărca o nouă linie. În felul acesta va creşte flexibilitatea programului. Pentru acest ciclu este necesară doar declararea cursorului. iar în acest caz . Procesarea liniilor unui cursor explicit Pentru procesarea diferitelor linii ale unui cursor explicit se foloseşte operaţia de ciclare (LOOP. fără a schimba specificaţia. Comanda are următoarea sintaxă: FOR nume_înregistrare IN nume_cursor LOOP secvenţă_de_instrucţiuni. putând fi modificat doar corpul cursorului. END LOOP. Comanda EXIT poate fi utilizată pentru ieşirea din ciclu. Domeniul ei este doar ciclul respectiv. iar %ROWCOUNT este 0. parametru]…) ] RETURN tip_returnat. -. operaţiile de deschidere. Într-un pachet poate fi separată specificarea unui cursor de corpul acestuia.definire corp cursor … END exemplu.declaratie specificatie cursor … END exemplu. încărcare şi închidere ale acestuia fiind implicite. Procesarea liniilor unui cursor explicit se poate realiza şi cu ajutorul unui ciclu FOR special. %NOTFOUND va fi TRUE. -. FOR). Exemplu: CREATE PACKAGE exemplu AS CURSOR alfa (p_valoare_min NUMBER) RETURN opera%ROWTYPE.

valoarea operelor de artă expuse într-o galerie al cărei cod este introdus de la tastatură.PUT_LINE('Galeria nu are opere de arta'). i:=0. media NUMBER.PUT_LINE('Media valorilor operelor de arta din galeria cu numarul ' || TO_CHAR(v_cod_galerie) || ' este ' || TO_CHAR(media)). IF i=0 THEN DBMS_OUTPUT. / SET SERVEROUTPUT OFF Cursoare parametrizate . DBMS_OUTPUT. i := i+1.cod_galerie%TYPE:=&p_galerie.nu mai este necesară nici declararea cursorului. să se obţină media valorilor operelor de artă expuse în galeria respectivă.valoare. De asemenea. END LOOP. BEGIN val:=0. END IF. END. Exemplul care urmează este concludent în acest sens. SET SERVEROUTPUT ON ACCEPT p_galerie PROMPT 'Dati codul galeriei:' DECLARE v_cod_galerie galerie. FOR numar_opera IN (SELECT cod_opera. valoare FROM opera WHERE cod_galerie = v_cod_galerie) LOOP val := val + numar_opera.PUT_LINE('Valoarea operelor de arta din galeria cu numarul ' || TO_CHAR(v_cod_galerie) || ' este ' || TO_CHAR(val)). ELSE media := val/i. i INTEGER. Exemplu: Să se calculeze. val NUMBER. utilizând un ciclu cursor cu subcereri.--închidere implicită DBMS_OUTPUT.

iar nume_parametru are sintaxa: nume_parametru [IN] tip_parametru [ {:= | DEFAULT} expresie] În această declaraţie. Ei nu suportă constrângerea NOT NULL. Unei variabile de tip cursor îi corespunde o comandă SELECT. nume – parametrii actuali sunt aranjaţi într-o ordine arbitrară. specificând lista parametrilor actuali ai cursorului. Exemplu: Utilizând un cursor parametrizat să se obţină codurile operelor de artă din . tip_returnat reprezintă un tip înregistrare sau linie de tabel. nu pot returna valori parametrilor actuali. Identificatorul comanda_select este o instrucţiune SELECT fără clauza INTO. Pentru a putea lucra cu nişte cursoare ale căror comenzi SELECT ataşate depind de parametri ce pot fi modificaţi la momentul execuţiei. Parametrii sunt specificaţi similar celor de la subprograme. Parametrii formali sunt de tip IN şi. Declararea unui astfel de cursor se face respectând următoarea sintaxă: CURSOR nume_cursor [ (nume_parametru[. Un astfel de cursor este mult mai uşor de interpretat şi de întreţinut. care este un tip scalar. oferind şi posibilitatea reutilizării sale în blocul PL/SQL. un cursor parametrizat este un cursor în care comanda SELECT ataşată depinde de unul sau mai mulţi parametri. Prin urmare. în PL/SQL s-a introdus noţiunea de cursor parametrizat. prin urmare. Dacă în definiţia cursorului. Deschiderea unui astfel de cursor se face asemănător apelului unei funcţii. Transmiterea de parametri unui cursor parametrizat se face în mod similar procedurilor stocate. care nu poate fi schimbată pe parcursul programului. valoare_parametru] …) ]. nume_parametru …] ) ] [RETURN tip_returnat] IS comanda_select. atributul tip_parametru reprezintă tipul parametrului. În determinarea mulţimii active se vor folosi valorile actuale ale acestor parametri. Sintaxa pentru deschiderea unui cursor parametrizat este: OPEN nume_cursor [ (valoare_parametru [. dar cu o corespondenţă de forma parametru formal => parametru actual. toţi parametrii au valori implicite (DEFAULT). Asocierea dintre parametrii formali şi cei actuali se face prin: poziţie – parametrii formali şi actuali sunt separaţi prin virgulă. cursorul poate fi deschis fără a specifica vreun parametru.

LOOP FETCH sala_cursor INTO v_cod_sala. . CURSOR sala_cursor IS SELECT cod_sala. BEGIN OPEN sala_cursor. identificatorul sălii şi al galeriei. INSERT INTO mesaje (rezultat) VALUES (v_car). END. EXIT WHEN ope_cursor%NOTFOUND. Blocarea se poate realiza (atunci când cursorul este deschis) cu ajutorul comenzii SELECT care conţine clauza FOR UPDATE.cod_galerie%TYPE. Rezultatele să fie inserate în tabelul mesaje. LOOP FETCH ope_cursor INTO v_car. v_cod_galerie galerie. v_car VARCHAR2(75).fiecare sală.v_cod_galerie. END LOOP.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. END IF. EXIT WHEN sala_cursor%NOTFOUND. v_cod_galerie). COMMIT. END LOOP. CLOSE ope_cursor. DECLARE v_cod_sala sala. Cursoare SELECT FOR UPDATE Uneori este necesară blocarea liniilor înainte ca acestea să fie şterse sau reactualizate. OPEN ope_cursor (v_cod_sala. Declararea unui astfel de cursor se face conform sintaxei: CURSOR nume_cursor IS comanda_select FOR UPDATE [OF lista_câmpuri] [NOWAIT]. CURSOR ope_cursor (v_id_sala NUMBER. CLOSE sala_cursor.cod_sala%TYPE.cod_galerie FROM sala. IF ope_cursor%ISOPEN THEN CLOSE ope_cursor.

această situaţie este utilă când se reactualizează o valoare a unei linii şi trebuie avută siguranţa că linia nu este schimbată de alt utilizator înaintea reactualizării. De subliniat că instrucţiunile UPDATE şi DELETE vor reactualiza numai coloanele listate în clauza FOR UPDATE. Atributul NOWAIT returnează o eroare dacă liniile sunt deja blocate de altă sesiune. După închiderea cursorului este necesară comanda COMMIT pentru a realiza scrierea efectivă a modificărilor. atunci comanda SELECT … FOR UPDATE va aştepta (sau nu) ca aceste blocări să fie eliberate. respectiv NOWAIT. atunci comenzile DELETE şi UPDATE corespunzătoare trebuie să conţină clauza WHERE CURRENT OF nume_cursor. Identificatorul lista_câmpuri este o listă ce include câmpurile tabelului care vor fi modificate. Liniile unui tabel sunt blocate doar dacă clauza FOR UPDATE se referă la coloane ale tabelului respectiv. . determinate de clauza SELECT. În felul acesta este realizată consistenţa la citire a sistemului. Pseudocoloana ROWID poate fi utilizată dacă tabelul referit în interogare nu are o cheie primară specificată. respectiv eroarea ORA-00054. Dacă liniile nu sunt deblocate în n secunde. Dacă un cursor este declarat cu clauza FOR UPDATE. alte sesiuni nu pot schimba liniile din mulţimea activă până când tranzacţia nu este permanentizată sau anulată. liniile corespunzătoare mulţimii active. deoarece cursorul lucrează doar cu nişte copii ale liniilor reale existente în tabele. după cum este specificată clauza WAIT. De exemplu. În Oracle9i este utilizată sintaxa: SELECT … FROM … FOR UPDATE [OF lista_campuri] [ {WAIT n | NOWAIT} ]. Dacă altă sesiune a blocat deja liniile din mulţimea activă. sunt blocate pentru operaţii de scriere (reactualizare sau ştergere). Această clauză referă linia curentă care a fost găsită de cursor. respectiv NOWAIT. Pentru a trata această situaţie se utilizează clauza WAIT. Dacă nu este specificată nici una din clauzele WAIT sau NOWAIT. sistemul aşteaptă până ce linia este deblocată şi atunci returnează rezultatul comenzii SELECT. În momentul deschiderii unui astfel de cursor. permiţând ca reactualizările şi ştergerile să se efectueze asupra acestei linii. iar această variabilă poate fi utilizată în clauza WHERE (WHERE ROWID = v_rowid). Prin urmare. Valoarea lui n reprezintă numărul de secunde de aşteptare. fără referirea explicită a cheii primare sau pseudocoloanei ROWID. atunci se declanşează eroarea ORA-30006. ROWID-ul fiecărei linii poate fi încărcat într-o variabilă PL/SQL (declarată de tipul ROWID sau UROWID).

BEGIN FOR x IN calc LOOP UPDATE opera SET valoare = valoare*2 WHERE CURRENT OF calc.'DD-MON- YY') FOR UPDATE OF valoare NOWAIT. Unui cursor static i se asociază o comandă SQL care este cunoscută în momentul în care blocul este compilat. variabilele cursor nu solicită o comandă SQL asociată. DECLARE CURSOR calc IS SELECT * FROM opera WHERE material = 'panza' AND data_achizitie <= TO_DATE('01-JAN-56'. diferite comenzi SQL pot fi asociate variabilelor cursor. un cursor este un obiect static. la diferite momente de timp. În momentul declarării. deschisă. Variabilele cursor sunt dinamice deoarece li se pot asocia diferite interogări atâta timp cât coloanele returnate de fiecare interogare corespund declaraţiei . Deoarece blocările implicate de clauza FOR UPDATE vor fi eliberate de comanda COMMIT. Exemplu: Să se dubleze valoarea operelor de artă pictate pe pânză care au fost achiziţionate înainte de 1 ianuarie 1956. încărcată şi închisă în mod similar unui cursor static. Prin urmare. În PL/SQL a fost introdusă variabila cursor.se permanentizeaza actiunea si se elibereaza blocarea COMMIT. care este de tip referinţă. iar un cursor dinamic este un pointer la un cursor. în interiorul ciclului unde se fac schimbări ale datelor poate fi utilizat un COMMIT. nu sunt probleme în acest sens şi. În acest fel. Cursoare dinamice Toate exemplele considerate anterior se referă la cursoare statice. END LOOP. În cazul în care cursorul nu este definit prin SELECT…FOR UPDATE. prin urmare. -. nu este recomandată utilizarea comenzii COMMIT în interiorul ciclului în care se fac încărcări de date. Acest tip de variabilă trebuie declarată. END. Orice FETCH executat după COMMIT va eşua. Variabilele cursor sunt similare tipului pointer din limbajele C sau Pascal.

nu poate fi asignată valoarea null unei variabile cursor. Dacă lipseşte clauza RETURN. În cazul variabilelor cursor. nu pot fi utilizaţi operatorii de comparare pentru a testa egalitatea. Pentru a crea o variabilă cursor este necesară definirea unui tip REF CURSOR. ea poate fi deschisă pentru orice cerere SQL care returnează date de tipul declarat. tip_ref_cursor este un nou tip de dată ce poate fi utilizat în declaraţiile următoare ale variabilelor cursor. încărcare (FETCH). Există anumite restricţii referitoare la utilizarea variabilelor cursor: nu pot fi declarate într-un pachet. o aplicaţie Oracle Forms şi server-ul Oracle pot referi aceeaşi zonă de lucru (care conţine mulţimea rezultat). Acest tip corespunde coloanelor returnate de către orice cursor asociat variabilelor cursor de tipul definit. instrucţiunile de deschidere (OPEN). Pentru a reduce traficul în reţea. . De exemplu. închidere (CLOSE) vor avea o sintaxă similară celor comentate anterior. nested table). Aceste variabile sunt utile în transmiterea seturilor de rezultate între subprograme PL/SQL stocate şi diferiţi clienţi. nu poate fi utilizat tipul REF CURSOR pentru a specifica tipul elementelor unei colecţii (varray. iar tip_returnat este un tip înregistrare sau tipul unei linii dintr-un tabel al bazei. nu poate fi utilizat tipul REF CURSOR pentru a specifica tipul unei coloane în comanda CREATE TABLE. urmând apoi declararea unei variabile de tipul respectiv. cursorul poate fi deschis pentru orice cerere SELECT. Sintaxa pentru declararea variabilei cursor este următoarea: TYPE tip_ref_cursor IS REF CURSOR [RETURN tip_returnat]. dar de pe staţia client etc. nu pot fi folosite cu SQL dinamic în Pro*C/C++. Dacă variabila cursor apare ca parametru într-un subprogram. deschisă şi se pot încărca date din ea pe server. apoi poate continua încărcarea. cererea asociată variabilei cursor nu poate include clauza FOR UPDATE (restricţia dispare în Oracle9i). După ce variabila cursor a fost declarată. o variabilă cursor poate fi declarată pe staţia client. var_cursor tip_ref_cursor. inegalitatea sau valoarea null a variabilelor cursor.variabilei cursor. Identificatorul var_cursor este numele variabilei cursor. atunci trebuie specificat tipul parametrului (tipul REF CURSOR) şi forma acestuia (IN sau IN OUT). un client OCI.

cerere_select este interogarea pentru care este deschisă variabila cursor. Exemplu: . Identificatorul :variabila_cursor_host reprezintă o variabilă cursor declarată într- un mediu gazdă PL/SQL (de exemplu. Opţiunea şir_dinamic este specifică prelucrării dinamice a comenzilor. un program OCI). Sintaxa comenzii este: OPEN {variabila_cursor | :variabila_cursor_host} FOR {cerere_select | şir_dinamic [USING argument_bind [. dar fără opţiunea RETURN tip. iar posibilităţile oferite de SQL dinamic vor fi analizate într-un capitol separat. iar şir_dinamic este o secvenţă de caractere care reprezintă cererea multilinie.FOR poate deschide acelaşi cursor pentru diferite cereri. cererea anterioară este pierdută. ELSIF alege = 3 THEN OPEN ope_var FOR SELECT * FROM opera WHERE valoare=7777. identifică mulţimea rezultat şi poziţionează cursorul la prima linie din mulţimea rezultat. alege IN NUMBER) IS BEGIN IF alege = 1 THEN OPEN ope_var FOR SELECT * FROM opera. END IF. Comanda OPEN…FOR asociază o variabilă cursor cu o cerere multilinie. Nu este necesară închiderea variabilei cursor înainte de a o redeschide. END alfa. PROCEDURE deschis_ope (ope_var IN OUT ope_tip. Identificatorul variabila_cursor specifică o variabilă cursor declarată anterior. Exemplu: CREATE OR REPLACE PACKAGE alfa AS TYPE ope_tip IS REF CURSOR RETURN opera%ROWTYPE. execută cererea. END alfa. CREATE OR REPLACE PACKAGE BODY alfa AS PROCEDURE deschis_ope (ope_var IN OUT ope_tip. alege IN NUMBER). Dacă se redeschide variabila cursor pentru o nouă cerere. ELSIF alege = 2 THEN OPEN ope_var FOR SELECT * FROM opera WHERE valoare>2000. Comanda OPEN . argument_bind …] ] }. END deschis_ope.

TYPE beta IS TABLE OF opera. . În următorul exemplu se declară o variabilă cursor care se asociază unei comenzi SELECT (SQL dinamic) ce returnează anumite linii din tabelul opera. Prin clauza LIMIT se limitează numărul liniilor încărcate din baza de date. var3. valoare FROM opera. var1 alfa. Exemplu: DECLARE TYPE alfa IS REF CURSOR RETURN opera%ROWTYPE. atribuie valori componentelor din lista cererii prin clauza INTO. Sintaxa comenzii este: FETCH {variabila_cursor | :variabila_cursor_host} INTO {variabila [. nume_array_host]…} [LIMIT expresie_numerica]]. Atributul nume_colecţie indică o colecţie declarată anterior. Comanda FETCH returnează o linie din mulţimea rezultat a cererii multi- linie. var3 gama. iar nume_array_host identifică un vector declarat într-un mediu gazdă PL/SQL şi trimis lui PL/SQL ca variabilă de legătură. TYPE gama IS TABLE OF opera. var2 beta. … END.titlu%TYPE.valoare FROM opera WHERE valoare> :vv' USING mm_val. mm_val INTEGER := 100000. avansează cursorul la următoarea linie. BEGIN OPEN opera_var FOR 'SELECT cod_opera. variabila]… | înregistrare} [BULK COLLECT INTO {nume_colecţie [.valoare%TYPE. opera_var operaref. DECLARE TYPE operaref IS REF CURSOR. nume_colecţie]…} | {nume_array_host [. în care sunt depuse valorile respective. Clauza BULK COLLECT permite încărcarea tuturor liniilor simultan în una sau mai multe colecţii. FETCH var1 BULK COLLECT INTO var2. BEGIN OPEN alfa FOR SELECT titlu.

care returnează un cursor imbricat (nested cursor). expresia cursor poate să apară într-o comandă SELECT ce este utilizată pentru deschiderea unui cursor dinamic. PL/SQL acceptă cereri care au expresii cursor în cadrul unei declaraţii cursor. El este închis dacă: este închis explicit de către utilizator. BEGIN FOR k IN gama LOOP --nu este corect! … END. Comanda CLOSE dezactivează variabila cursor precizată. Nu poate fi folosită una din ele. declaraţii REF CURSOR şi a variabilelor cursor. DECLARE TYPE beta IS REF CURSOR RETURN opera%ROWTYPE. apare o eroare în timpul unei încărcări din cursorul „părinte“. când este aşteptată cealaltă. gama beta. Prin urmare. Un cursor imbricat este încărcat automat atunci când liniile care îl conţin sunt încărcate din cursorul „părinte“. Ea are sintaxa: CLOSE {variabila_cursor | :variabila_cursor_host} Cursoarele şi variabilele cursor nu sunt interoperabile. END. cursorul „părinte“ este reexecutat. închis sau anulat. Expresie cursor În Oracle9i a fost introdus conceptul de expresie cursor (cursor expression). Există câteva restricţii asupra folosirii unei expresii cursor: nu poate fi utilizată cu un cursor implicit. expresiile cursor pot fi folosite în cereri SQL dinamice sau ca parametri actuali într-un subprogram. … CLOSE var1. poate să apară numai într-o comandă SELECT care nu este imbricată în altă cerere (exceptând cazul în care este o subcerere chiar a expresiei cursor) sau ca argument pentru funcţii tabel. Următoarea secvenţă este incorectă. în clauza FROM a lui . De asemenea. Expresia cursor are următoarea sintaxă: CURSOR (subcerere) Fiecare linie din mulţimea rezultat poate conţine valori uzuale şi cursoare generate de subcereri.

SELECT. Varianta 2: .galerie) LOOP DBMS_OUTPUT. END.nume_sala). nu poate să apară în interogarea ce defineşte o vizualizare. Sunt prezentate două variante de rezolvare. În acest caz cursorul returnează două coloane. Să se afişeze data când a avut loc vernisajul acestei expoziţii.cod.PUT_LINE (sal.cod_expo=d.cod_opera FROM figureaza_in f WHERE f. FOR sal IN (SELECT cod_sala.cod_expo. cea de-a doua coloană fiind un cursor imbricat. nume_sala FROM sala WHERE cod_galerie = gal.cod_expo) AS xx FROM expozitie d WHERE l.PUT_LINE (gal.nume_galerie). val_oras VARCHAR2(20)) IS SELECT l.datai.cod_expo = d.cod_expo) AS yy FROM locped l WHERE cod_expo = val_cod AND nume_oras= val_oras. Varianta 1: BEGIN FOR gal IN (SELECT cod_galerie. CURSOR (SELECT d. Prima variantă reprezintă o implementare simplă utilizând programarea secvenţială clasică. nu se pot efectua operaţii BIND sau EXECUTE cu aceste expresii. iar a doua utilizează expresii cursor pentru rezolvarea acestei probleme. END LOOP. nume_galerie FROM galerie) LOOP DBMS_OUTPUT. END LOOP. CURSOR alfa (val_cod NUMBER. Exemplu: Să se definească un cursor care furnizează codurile operelor expuse în cadrul unei expoziţii având un cod specificat (val_cod) şi care se desfăşoară într-o localitate precizată (val_oras). CURSOR (SELECT f. Exemplu: Să se listeze numele galeriilor din muzeu şi pentru fiecare galerie să se afişeze numele sălilor din galeria respectivă.

END.cod_galerie = g.nume_galerie%TYPE. END LOOP. EXIT WHEN c_gal%NOTFOUND. FETCH v_sala BULK COLLECT INTO v_nume_sala. . v_nume_sala sala_nume.LAST LOOP DBMS_OUTPUT.v_nume_sala. v_sala.FIRST. END LOOP.PUT_LINE (v_nume_gal). DBMS_OUTPUT. v_nume_gal galerie. CURSOR (SELECT nume_sala FROM sala s WHERE s. FOR ind IN v_nume_sala.nume_sala%TYPE INDEX BY BINARY_INTEGER. CLOSE c_gal. LOOP FETCH c_gal INTO v_nume_gal.cod_galerie) FROM galerie g. v_sala SYS_REFCURSOR.DECLARE CURSOR c_gal IS SELECT nume_galerie..PUT_LINE (v_nume_sala (ind)). TYPE sala_nume IS TABLE OF sala. BEGIN OPEN c_gal.

în secţiunea de tratare a erorilor. mulţimea rândurilor rezultate în urma execuţiei acestei comenzi (active set). Gestiunea cursoarelor în PL/SQL Sistemul Oracle foloseşte. în cazul unei cereri. Zona conţine informaţii necesare procesării comenzii. sau prin nume_cursor%nume_atribut. . care este de tip întreg şi reprezintă numărul liniilor încărcate de cursor. care este de tip boolean şi indică dacă un cursor este deschis (în cazul cursoarelor implicite. %NOTFOUND. deoarece un cursor implicit este închis de sistem imediat după executarea instrucţiunii SQL asociate). în funcţii. care este de tip boolean şi are semnificaţie opusă faţă de cea a atributului %FOUND. acest atribut are întotdeauna valoarea FALSE. server-ul Oracle deschide această zonă de memorie în care comanda este analizată sintactic şi este executată. Un cursor este un pointer la această zonă context. %FOUND. întoarce mai multe linii ca rezultat. care este de tip boolean şi ia valoarea TRUE dacă ultima operaţie de încărcare (FETCH) dintr-un cursor a avut succes (în cazul cursoarelor explicite) sau dacă instrucţiunea SQL a întors cel puţin o linie (în cazul cursoarelor implicite). pentru a procesa o comandă SQL. explicite. un program PL/SQL poate controla zona context şi transformările petrecute în urma procesării comenzii. Există două tipuri de cursoare: implicite. Prin intermediul cursoarelor. Ele pot să apară în comenzi PL/SQL. Lista atributelor este următoarea: %ROWCOUNT. în cazul cursoarelor implicite. o zonă de memorie cunoscută sub numele de zonă context (context area). Atât cursoarele implicite cât şi cele explicite au o serie de atribute ale căror valori pot fi folosite în expresii. Atributele pot fi referite prin expresia SQL%nume_atribut. declarate şi definite de către utilizator atunci când o cerere (SELECT). în cazul unui cursor explicit. un pointer către reprezentarea internă a comenzii. cum ar fi: numărul de rânduri procesate de instrucţiune. Când este procesată o instrucţiune SQL. 4. %ISOPEN. care apare într-un bloc PL/SQL. dar nu pot fi utilizate în comenzi SQL. generate de server-ul Oracle când în partea executabilă a unui bloc PL/SQL apare o instrucţiune SQL.

iar după fiecare iteraţie atributul %BULK_ROWCOUNT returnează numărul acestor linii inserate. Atributele scalare ale cursorului implicit (SQL%ROWCOUNT. SQL%NOTFOUND. SET SERVEROUTPUT ON DECLARE TYPE alfa IS TABLE OF NUMBER. DELETE sau SELECT INTO executată. FOR j IN 1.. deci folosesc acelaşi domeniu.Cursoare implicite Când se procesează o comandă LMD. SQL%ISOPEN) furnizează informaţii referitoare la ultima comandă INSERT. FORALL j IN 1.cod_opera FROM opera WHERE cod_artist = beta(j). Componenta %BULK_ROWCOUNT(j) conţine numărul de linii procesate de a j-a execuţie a unei comenzi INSERT.PUT_LINE ('Pentru artistul avand codul ' || beta(j) || ' au fost inserate ' || SQL%BULK_ROWCOUNT(j) || inregistrari (opere de arta)'). DELETE sau UPDATE. Dacă a j-a execuţie nu afectează nici o linie. atunci atributul returnează valoarea 0. END. SQL%FOUND. beta alfa. atributele acestuia au valoarea null. motorul SQL deschide un cursor implicit. . În Oracle9i.PUT_LINE ('Numarul total de inregistrari inserate este '||SQL%ROWCOUNT). END LOOP. care este asociat comenzii FORALL. DBMS_OUTPUT.COUNT INSERT INTO tab_art SELECT cod_artist.beta. Exemplu: În exemplul care urmează. Dacă %BULK_ROWCOUNT(j) este zero. BEGIN SELECT cod_artist BULK COLLECT INTO beta FROM artist. UPDATE. pentru cursoare implicite a fost introdus atributul compus %BULK_ROWCOUNT.beta.COUNT LOOP DBMS_OUTPUT.. Atributul are semantica unui tablou indexat. Comanda FORALL şi atributul %BULK_ROWCOUNT au aceiaşi indici. Înainte ca Oracle să deschidă cursorul SQL implicit. comanda FORALL inserează un număr arbitrar de linii la fiecare iteraţie. atributul %FOUND este FALSE.

PUT_LINE ('Numarul total de inregistrari inserate este ' || SQL%ROWCOUNT).COUNT LOOP DBMS_OUTPUT. SET SERVEROUTPUT ON DECLARE TYPE alfa IS TABLE OF JOBS. 3 JOB_ID VARCHAR2(10) NOT NULL ).beta. END LOOP./ SET SERVEROUTPUT OFF Exemplu: Sa se creeze tabelul JOB_ANG cu cimputile employee_id si job_id. CREATE TABLE JOB_ANG (EMPLOYEE_ID NUMBER(6) NOT NULL .JOB_ID%TYPE. BEGIN SELECT JOB_ID BULK COLLECT INTO beta FROM JOBS. DBMS_OUTPUT. Sa se numere citi angajati sunt pe fiecare job si citi angajati in total.. JOB_ID FROM EMPLOYEES WHERE JOB_ID = beta(j). FOR j IN 1. Table created. FORALL j IN 1. SQL> CREATE TABLE JOB_ANG 2 (EMPLOYEE_ID NUMBER(6) NOT NULL .beta. / 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) . END.. JOB_ID VARCHAR2(10) NOT NULL ). beta alfa.COUNT INSERT INTO JOB_ANG SELECT EMPLOYEE_ID.PUT_LINE ('Pentru jobul ' || beta(j) || ' au fost inserate ' || SQL%BULK_ROWCOUNT(j) || ' inregistrari (angajati)').

Dacă nu mai este necesar în restul programului. Prin urmare. trebuie deschis în partea executabilă. el trebuie declarat în secţiunea declarativă a programului. recuperarea liniilor rezultatului în variabile PL/SQL. cursorul trebuie să fie închis. urmând să fie utilizat apoi pentru extragerea datelor. DECLARE declarare cursor BEGIN deschidere cursor (OPEN) WHILE rămân linii de recuperat LOOP recuperare linie rezultat (FETCH) … END LOOP .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 următoarele etape: declararea cursorului (atribuirea unui nume şi asocierea cu o comandă SELECT). pentru a utiliza un cursor. deschiderea cursorului pentru cerere (executarea interogării asociate şi determinarea mulţimii rezultat). închiderea cursorului (eliberarea resurselor relative la cursor).

Declaraţia CURSOR are următoarea formă sintactică: CURSOR nume_cursor IS comanda_select Identificatorul nume_cursor este numele cursorului. La deschiderea unui cursor se realizează următoarele operaţii: se evaluează cererea asociată (sunt examinate valorile variabilelor de legătură ce apar în declaraţia cursorului). Variabilele care sunt referite în comanda de selectare trebuie declarate înaintea comenzii CURSOR. identifică mulţimea liniilor rezultat şi poziţionează cursorul înaintea primei linii. iar comanda_select este cererea SELECT care va fi procesată. iar câmpul expresie se va referi prin acest alias. atunci pentru expresia respectivă trebuie utilizat un alias. Deschiderea unui cursor explicit Comanda OPEN execută cererea asociată cursorului. Identificatorul nume_cursor reprezintă numele cursorului ce va fi deschis. nu trebuie să includă clauza INTO. Dacă se cere procesarea liniilor într-o anumită ordine. Ele sunt considerate variabile de legătură. FETCH şi CLOSE. închidere cursor (CLOSE) … END. Pentru a controla activitatea unui cursor sunt utilizate comenzile DECLARE. este determinată mulţimea rezultat (active set) prin executarea cererii . OPEN. Observaţii: Comanda SELECT care apare în declararea cursorului. Declararea unui cursor explicit Prin declaraţia CURSOR în cadrul comenzii DECLARE este definit un cursor explicit şi este precizată structura cererii care va fi asociată acestuia. Deschiderea unui cursor se face prin comanda: OPEN nume_cursor. atunci în cerere este utilizată clauza ORDER BY. Numele cursorului este un identificator unic în cadrul blocului. care nu poate să apară într-o expresie şi căruia nu i se poate atribui o valoare. Dacă în lista comenzii SELECT apare o expresie.

comanda FETCH regăseşte o singură linie. utilizând clauza BULK COLLECT. titlu1 ctopera. La un moment dat. citeşte datele liniei curente în variabile PL/SQL. DECLARE TYPE ccopera IS TABLE OF opera. cod1 ccopera. BEGIN OPEN alfa. CURSOR alfa IS SELECT cod_opera. Comanda FETCH are următoarea sintaxă: FETCH nume_cursor INTO {nume_variabilă [. pointer-ul este poziţionat la prima linie din mulţimea activă. FETCH realizează următoarele operaţii: avansează pointer-ul la următoarea linie în mulţimea activă (pointer-ul poate avea doar un sens de deplasare de la prima spre ultima înregistrare). Totuşi.titlu%TYPE. având în vedere valorile de la pasul anterior. Încărcarea datelor dintr-un cursor explicit Comanda FETCH regăseşte liniile rezultatului din mulţimea activă. în ultimele versiuni Oracle pot fi încărcate mai multe linii (la un moment dat) într-o colecţie. titlu FROM opera WHERE stil = 'impresionism'. TYPE ctopera IS TABLE OF opera. SELECT. titlu1.cod_opera%TYPE. Exemplu: În exemplul care urmează se încarcă date dintr-un cursor în două colecţii. Identificatorul nume_cursor reprezintă numele unui cursor declarat şi deschis anterior. . nume_variabilă] … | nume_înregistrare}. Variabila sau lista de variabile din clauza INTO trebuie să fie compatibilă (ca ordine şi tip) cu lista selectată din cererea asociată cursorului. dacă pointer-ul este poziţionat la sfârşitul mulţimii active atunci se iese din bucla cursorului. FETCH alfa BULK COLLECT INTO cod1.

. cursorul trebuie închis. Exemplu: Pentru toţi artiştii care au opere de artă expuse în muzeu să se insereze în tabelul temp informaţii referitoare la numele acestora şi anul naşterii. an_nastere FROM artist. deoarece este bine ca resursele să fie eliberate. Închiderea unui cursor explicit După ce a fost procesată mulţimea activă. Dacă se încearcă încărcarea datelor dintr-un cursor închis. Identificatorul nume_cursor este numele unui cursor deschis anterior. atunci apare excepţia INVALID_CURSOR. Un bloc PL/SQL poate să se termine fără a închide cursoarele. INSERT INTO temp VALUES (v_nume || TO_CHAR(v_an_nas)). CURSOR info IS SELECT DISTINCT nume. Prin această operaţie. DECLARE v_nume artist. dar acest lucru nu este indicat. Aceste resurse includ spaţiul utilizat pentru memorarea mulţimii active şi spaţiul temporar folosit pentru determinarea mulţimii active. Cursorul va fi închis prin comanda CLOSE. v_an_nas artist. Pentru a reutiliza cursorul este suficient ca acesta să fie redeschis. care are următoarea sintaxă: CLOSE nume_cursor. BEGIN OPEN info. v_an_nas. END LOOP.nume%TYPE.an_nastere%TYPE. … CLOSE alfa. EXIT WHEN info%NOTFOUND. END. LOOP FETCH info INTO v_nume. PL/SQL este informat că programul a terminat folosirea cursorului şi resursele asociate acestuia pot fi eliberate.

cod_galerie%TYPE:=&p_galerie. media NUMBER. %NOTFOUND va fi TRUE. COMMIT. să se obţină media valorilor operelor de artă expuse în galeria respectivă. operaţiile de deschidere. numit ciclu cursor. Comanda are următoarea sintaxă: FOR nume_înregistrare IN nume_cursor LOOP secvenţă_de_instrucţiuni. utilizând un ciclu cursor cu subcereri. %FOUND va fi FALSE. încărcare şi închidere ale acestuia fiind implicite. END LOOP. i INTEGER. WHILE. Procesarea liniilor unui cursor explicit se poate realiza şi cu ajutorul unui ciclu FOR special. SET SERVEROUTPUT ON ACCEPT p_galerie PROMPT 'Dati codul galeriei:' DECLARE v_cod_galerie galerie. De asemenea. Exemplul care urmează este concludent în acest sens. Pentru acest ciclu este necesară doar declararea cursorului. Procesarea liniilor unui cursor explicit Pentru procesarea diferitelor linii ale unui cursor explicit se foloseşte operaţia de ciclare (LOOP. Comanda EXIT poate fi utilizată pentru ieşirea din ciclu. FOR). Variabila nume_înregistrare (care controlează ciclul) nu trebuie declarată. iar %ROWCOUNT este 0. iar în acest caz nu mai este necesară nici declararea cursorului. val NUMBER. END. prin care la fiecare iteraţie se va încărca o nouă linie. Domeniul ei este doar ciclul respectiv. . Valorile atributelor unui cursor explicit sunt prezentate în următorul tabel: După prima încărcare. dacă mulţimea rezultat este vidă. Exemplu: Să se calculeze. Pot fi utilizate cicluri cursor speciale care folosesc subcereri. CLOSE info. valoarea operelor de artă expuse într-o galerie al cărei cod este introdus de la tastatură. iar valoarea atributului %ROWCOUNT pentru terminarea ciclului.

Pentru a putea lucra cu nişte cursoare ale căror comenzi SELECT ataşate depind de parametri ce pot fi modificaţi la momentul execuţiei. DBMS_OUTPUT. nume_parametru …] ) ] [RETURN tip_returnat] . END. IF i=0 THEN DBMS_OUTPUT. FOR numar_opera IN (SELECT cod_opera.--închidere implicită DBMS_OUTPUT. Un astfel de cursor este mult mai uşor de interpretat şi de întreţinut. END LOOP. / SET SERVEROUTPUT OFF Cursoare parametrizate Unei variabile de tip cursor îi corespunde o comandă SELECT.valoare. ELSE media := val/i. un cursor parametrizat este un cursor în care comanda SELECT ataşată depinde de unul sau mai mulţi parametri.PUT_LINE('Media valorilor operelor de arta din galeria cu numarul ' || TO_CHAR(v_cod_galerie) || ' este ' || TO_CHAR(media)). oferind şi posibilitatea reutilizării sale în blocul PL/SQL. Declararea unui astfel de cursor se face respectând următoarea sintaxă: CURSOR nume_cursor [ (nume_parametru[. i:=0.BEGIN val:=0. care nu poate fi schimbată pe parcursul programului. END IF. Transmiterea de parametri unui cursor parametrizat se face în mod similar procedurilor stocate.PUT_LINE('Galeria nu are opere de arta'). Prin urmare. în PL/SQL s-a introdus noţiunea de cursor parametrizat. valoare FROM opera WHERE cod_galerie = v_cod_galerie) LOOP val := val + numar_opera. i := i+1.PUT_LINE('Valoarea operelor de arta din galeria cu numarul ' || TO_CHAR(v_cod_galerie) || ' este ' || TO_CHAR(val)).

v_cod employees. În determinarea mulţimii active se vor folosi valorile actuale ale acestor parametri. . tip_returnat reprezintă un tip înregistrare sau linie de tabel.last_name%TYPE. var_dept NUMBER) IS SELECT employee_id. CURSOR ang_cursor (var_salary NUMBER. Parametrii sunt specificaţi similar celor de la subprograme. nu pot returna valori parametrilor actuali. Ei nu suportă constrângerea NOT NULL. Identificatorul comanda_select este o instrucţiune SELECT fără clauza INTO. dar cu o corespondenţă de forma parametru formal => parametru actual. Sintaxa pentru deschiderea unui cursor parametrizat este: OPEN nume_cursor [ (valoare_parametru [. salary FROM employees WHERE salary<var_salary AND department_id=var_dept ORDER BY last_name. last_name. Parametrii formali sunt de tip IN şi. Rezolvarea se va face în trei moduri (cursor explicit. toţi parametrii au valori implicite (DEFAULT). Asocierea dintre parametrii formali şi cei actuali se face prin: poziţie – parametrii formali şi actuali sunt separaţi prin virgulă. ciclu cursor. Exemplu: Să se declare un cursor parametrizat (parametrii fiind var_salary şi var_dept) prin care să se afişeze in ordine alfabetica numele. IS comanda_select. v_sal employees. DECLARE v_nume employees.PUT_LINE('---Cursor explicit---'). salariul si codul salariaţilor pentru care salary<var_salary şi department_id=var_dept. BEGIN DBMS_OUTPUT. ciclu cursor cu subcereri). cursorul poate fi deschis fără a specifica vreun parametru. atributul tip_parametru reprezintă tipul parametrului. Deschiderea unui astfel de cursor se face asemănător apelului unei funcţii.salary%TYPE. valoare_parametru] …) ]. care este un tip scalar. nume – parametrii actuali sunt aranjaţi într-o ordine arbitrară. prin urmare. specificând lista parametrilor actuali ai cursorului.employee_id %TYPE. iar nume_parametru are sintaxa: nume_parametru [IN] tip_parametru [ {:= | DEFAULT} expresie] În această declaraţie. Dacă în definiţia cursorului.

Rezultatele să fie inserate în tabelul mesaje.PUT_LINE('---Ciclu cursor---'). FOR v_ang_cursor IN ang_cursor(10000.last_name|| ' are salariul ' ||vv_ang_cursor. END LOOP.cod_sala%TYPE. END.salary||' cod '|| vv_ang_cursor.last_name || ' are salariul ' || v_ang_cursor. last_name. v_nume. / Exemplu: Utilizând un cursor parametrizat să se obţină codurile operelor de artă din fiecare sală. EXIT WHEN ang_cursor%NOTFOUND.cod_galerie%TYPE. LOOP FETCH ang_cursor INTO v_cod. DECLARE v_cod_sala sala.v_sal. . salary FROM employees WHERE salary<10000 AND department_id=80 ORDER BY last_name) LOOP DBMS_OUTPUT. END LOOP.PUT_LINE('Salariatul '|| v_ang_cursor. v_cod_galerie galerie. DBMS_OUTPUT. FOR vv_ang_cursor IN (SELECT employee_id. DBMS_OUTPUT. END LOOP.80).PUT_LINE('Salariatul '|| v_nume|| ' are salariul ' ||v_sal||' cod '||v_cod). CLOSE ang_cursor. identificatorul sălii şi al galeriei.salary ||' cod '|| v_ang_cursor. OPEN ang_cursor(10000.employee_id).80) LOOP DBMS_OUTPUT.PUT_LINE('Salariatul '|| vv_ang_cursor.PUT_LINE('---Ciclu cursor cu subcereri---'). DBMS_OUTPUT.employee_id).

cod_galerie FROM sala. CLOSE sala_cursor. END LOOP. BEGIN OPEN sala_cursor. END LOOP. CURSOR ope_cursor (v_id_sala NUMBER. CURSOR sala_cursor IS SELECT cod_sala. END. LOOP FETCH ope_cursor INTO v_car. Cursoare SELECT FOR UPDATE Uneori este necesară blocarea liniilor înainte ca acestea să fie şterse sau reactualizate. Blocarea se poate realiza (atunci când cursorul este deschis) cu ajutorul comenzii SELECT care conţine clauza FOR UPDATE. INSERT INTO mesaje (rezultat) VALUES (v_car). v_cod_galerie).v_cod_galerie. COMMIT. EXIT WHEN ope_cursor%NOTFOUND. EXIT WHEN sala_cursor%NOTFOUND. LOOP FETCH sala_cursor INTO v_cod_sala. IF ope_cursor%ISOPEN THEN CLOSE ope_cursor.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. CLOSE ope_cursor. Declararea unui astfel de cursor se face conform sintaxei: CURSOR nume_cursor IS comanda_select FOR UPDATE [OF lista_câmpuri] [NOWAIT]. . v_car VARCHAR2(75). OPEN ope_cursor (v_cod_sala. END IF.

Dacă altă sesiune a blocat deja liniile din mulţimea activă. atunci se declanşează eroarea ORA-30006. Identificatorul lista_câmpuri este o listă ce include câmpurile tabelului care vor fi modificate. În Oracle9i este utilizată sintaxa: SELECT … FROM … FOR UPDATE [OF lista_campuri] [ {WAIT n | NOWAIT} ]. sistemul aşteaptă până ce linia este deblocată şi atunci returnează rezultatul comenzii SELECT. fără referirea explicită a cheii primare sau pseudocoloanei ROWID. După închiderea cursorului este necesară comanda COMMIT pentru a realiza scrierea efectivă a modificărilor. Valoarea lui n reprezintă numărul de secunde de aşteptare. determinate de clauza SELECT. liniile corespunzătoare mulţimii active. această situaţie este utilă când se reactualizează o valoare a unei linii şi trebuie avută siguranţa că linia nu este schimbată de alt utilizator înaintea reactualizării. Prin urmare. . permiţând ca reactualizările şi ştergerile să se efectueze asupra acestei linii. Dacă nu este specificată nici una din clauzele WAIT sau NOWAIT. În felul acesta este realizată consistenţa la citire a sistemului. Dacă liniile nu sunt deblocate în n secunde. atunci comanda SELECT … FOR UPDATE va aştepta (sau nu) ca aceste blocări să fie eliberate. deoarece cursorul lucrează doar cu nişte copii ale liniilor reale existente în tabele. Atributul NOWAIT returnează o eroare dacă liniile sunt deja blocate de altă sesiune. Pentru a trata această situaţie se utilizează clauza WAIT. Această clauză referă linia curentă care a fost găsită de cursor. În momentul deschiderii unui astfel de cursor. respectiv NOWAIT. după cum este specificată clauza WAIT. De subliniat că instrucţiunile 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ă. Dacă un cursor este declarat cu clauza FOR UPDATE. sunt blocate pentru operaţii de scriere (reactualizare sau ştergere). De exemplu. respectiv NOWAIT. respectiv eroarea ORA-00054. alte sesiuni nu pot schimba liniile din mulţimea activă până când tranzacţia nu este permanentizată sau anulată. ROWID-ul fiecărei linii poate fi încărcat într-o variabilă PL/SQL (declarată de tipul ROWID sau UROWID). atunci comenzile DELETE şi UPDATE corespunzătoare trebuie să conţină clauza WHERE CURRENT OF nume_cursor. iar această variabilă poate fi utilizată în clauza WHERE (WHERE ROWID = v_rowid). Liniile unui tabel sunt blocate doar dacă clauza FOR UPDATE se referă la coloane ale tabelului respectiv.

END LOOP. 'yyyy') = 2000. ROLLBACK. Orice FETCH executat după COMMIT va eşua. prin urmare. În cazul în care cursorul nu este definit prin SELECT…FOR UPDATE. salary FROM emp_*** WHERE TO_CHAR(hire_date. Deoarece blocările implicate de clauza FOR UPDATE vor fi eliberate de comanda COMMIT. Se va folosi un cursor SELECT FOR UPDATE. nu sunt probleme în acest sens şi. DECLARE CURSOR emp_cursor IS SELECT * FROM emp_*** WHERE TO_CHAR(hire_date. / SELECT last_name. '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. hire_date. 'yyyy') = 2000. hire_date. SELECT last_name. END. salary FROM emp_*** WHERE TO_CHAR(hire_date. Exemplu: Să se mărească cu 1000 salariile angajaţilor care au fost angajaţi în 2000 din tabelul emp_***. . Exemplu: Să se dubleze valoarea operelor de artă pictate pe pânză care au fost achiziţionate înainte de 1 ianuarie 1956. nu este recomandată utilizarea comenzii COMMIT în interiorul ciclului în care se fac încărcări de date. în interiorul ciclului unde se fac schimbări ale datelor poate fi utilizat un COMMIT.

END.se permanentizeaza actiunea si se elibereaza blocarea COMMIT. BEGIN FOR x IN calc LOOP UPDATE opera SET valoare = valoare*2 WHERE CURRENT OF calc.'DD-MON-YY') FOR UPDATE OF valoare NOWAIT. . -.DECLARE CURSOR calc IS SELECT * FROM opera WHERE material = 'panza' AND data_achizitie <= TO_DATE('01-JAN-56'. END LOOP.