You are on page 1of 81
ee Viad DIACONITA lon LUNGU Anda VELICANU BAZE DE DATE Bye Bea INTRODUCERE iN PL/SQL (PROCEDURAL LANGUAGE EXTENSION TO SQL) L1 Introducere PL/SQL este un limbaj procedural structurat pe bloc, programele putand fi imparfite in blocuri logice, constructiile PL/SQL continand structuri de control procedurale si comenzi descriptive SQL. Blocurile PL/SQL sunt procesate de motorul PL/SQL care poate fi rezident pe serverul ORACLE sau pe un alt instrument de dezvoltare (ex.: Oracle Forms, Reports, JDeveloper etc.). Programarea in PL/SQL este modularizata, in acest sens se utilizeazi blocurile care grupeaza instructiunile. Tipurile de date din SQL pot fi folosite in PL/SQL. Pentru a putea lucra cu limbajul PL/SQL avem nevoie in primul rand de o conexiune la o bazi de date Oracle si de un instrument de dezvoltare prin care si interactionim cu serverul Oracle. Pentru instalarea si configurarea instantei bazei de da date se poate utiliza ghidul electronic pus la dispozitie de catre compania Oracle odata cu kit-ul de instalare. Ca instrument de dezvoltare se poate utiliza SQL*plus care se instaleazi impreund cu instanfa Oracle sau se poate utiliza SQL Developer, un mediu de dezvoltare mai prietenos disponibil gratuit pe site-ul companiei Oracle (www. oracle .com). Schema bazei de date pe care se bazeazi exemplele din aceasta carte se poate vedea in figura 1. Scriptul pentru crearea acestor tabele este redat in Anexa 1 si se poate descirea de pe site-ul http://bd.ase.ro impreuni cu scriptul pentru adiugarea inregistrarilor. Baze de date, Limbajul PL/SQL | Introducere in PL/SQL (Procedural Language Extension to SQL) 11 1.2 Blocuri PL/SQL | Orice unitate PL/SQL contine unul sau mai multe blocuri, complet separate sau imbricate. Fiecare bloc este compus din sectiuni care pot fi obligatorii sau optionale. Componentele unui bloc PL/SQL | Un bloc PL/SQL este compus din pana la 3. sectiuni: declarativa (optionald), executabilé (obligatorie) si de tratare a exceptiilor (optionala). Structura unui bloc PL/SQL poate fi prezentata astfel: DECLARE (Optional) 2 | variabile, cursori, exceptii e BEGIN (Obligatoriu) i g comenzi SQL (asigura accesul la baza de date) z & structuri de programare procedurala PL/SQL 82 3 EXCEPTION (Optional) 2 actiuni ce se executi cfnd apare o eroare 5 END; (Obligatoriu) = z fn cadrul blocului pot aparea instructiuni SQL care asigurai accesul la baza | 3 de date, de exemplu pentru efectuarea unor actualizari, dar operatiile efectuate cu : 3 | variabilele PL/SQL in cadrul instructiunilor procedurale nu presupun accesarea - | bazei de date. 5 3 Observatii: Lt * se foloseste (;) dupa fiecare instructiune SQL sau instructiune de £ control PL/SQL; Se = + blocul PL/SQL se termina cu (;); * comentariile se pot realiza folosind (--) pentru fiecare linie de cod comentata sau (/* */) pentru mai multe linii comentate; * se foloseste (/) pentru a lansa un bloc anonim in bufferul SQL; * — instructiunile nu sunt CASE SENSITIVE. Blocurile PL/SQL pot fi executate 0 singuré dat& (cazul blocurilor anonime), pot fi stocate in baza de date pentru a fi apelate ulterior (cazul funetiilor, Tipe ‘MuweeR IREGIUNE VAnCHAPZa procedurilor si pachetelor stocate) sau pot fi realizate si apelate la nivelul programelor de aplicatii (functii, proceduri, triggeri de aplicatii). in paragrafele DEHN Toc nEGiNE urmatoare ne vom referi pe scurt la fiecare dintre aceste tipuri de blocuri, urménd si le detaliem in capitole separate. 12 Baze de date. Limbajul PL/SQL Blocurile anonime prezintd urmiatoarele caracteristici: * nu sunt stocate in baza de date; * se declara inline, in locul in care se doreste executia lor; * se executé in momentul rularii; Daca dorii si reutilizati un bloc anonim va sféituim s& salvati codul sursi pe hard disk intr-un fisier de tip .sql sau xt. La modul general un bloc PL/SQL anonim poate avea urmittoarea forma: DECLARE eventualele initializari; ~ de exemplu: v_variabila tip_de_date; BEGIN -- instructiuni executabile; EXCEPTION WHEN exceptie THEN actiune; END; / pentru a putea fi ulterior accesate variabilele, adresare lor fiind realizata astfel: eticheta_bloc.variabila DECLARE << eticheta_bloc >> DECLARE BEGIN END eticheta_bloc; END; -- declararea variabilelor se face precizand tipul de date si realizandu-se Blocurile PL/SQL se pot imbrica gi se pot eticheta cu <> Introducere in PL/SQL (Procedural Language Extension to SQL) 13 Proceduri si functii Sunt blocuri PL/SQL care au nume si pot fi stocate la nivelul bazei de date sau la nivel de aplicatie (de exemplu in mediul de dezvoltare Oracle Developer Suite — Forms si Reports). in cazul procedurilor si functiilor sectiunea DECLARE este inlocuita de definitia acesteia prin sintaxa CREATE [or replace] PROCEDURE/FUNCTION (lista parametrilor $i tipul acestora), de exemplu: CREATE [OR REPLACE] PROCEDURE nume > procedura (lista parametri) Is CREATE [OR REPLACE] FUNCTION nume_functie (lista parametri) RETURN tip_data RETURN valoare [EXCEPTION] END; / Pachetele de subprograme sunt utilizate pentru a grupa mai multe proceduri si functii utilizate pentru un anumit tip de prelucrari. Declansatorii pe baza de date sunt blocuri PL/SQL asociate tabelelor (de baz& sau virtuale) si lansate automat in executie cand are loc 0 comanda de manipulare (insert/update/delete). 14 Baze de date. Limbajul PL/SQL Declansatorii de aplicatie sunt blocuri PL/SQL asociate unor evenimente din cadrul aplicatiei (de exemplu: deplasarea mouse-ului, apasarea unui buton) si lansate in executie automat la aparitia acestor evenimente. 13 Utilizarea operatorilor si functiilor in PL/SQL Operatorii utilizagi In cadrul blocurilor PL/SQL se pot utiliza aceiasi operatori ca gi in cazul limbajului SQL. In plus fafa de acestia apare operatorul de atribuire (: | Operator Caracteristici +5%4, ** (op. Operatori aritmetici exponential) AND, OR, NOT Operatori logici | _S>=>5< numeric gi caracter <->data. Pentru conversii explicite se utilizeaza funcfiile TO_DATE, TO_NUMBER, TO_CHAR in mod aseminator cu limbajul sau. Afisarea pe ecran a valorilor variabilelor sau a mesajelor in PL/SQL se realizeazi prin apelarea functiilor PUT LINE sau PUT din pachetul standard DBMS_OUTPUT. Aceste doua functii primesc ca parametru un sir de caractere gi- | afigeazi. Diferenta dintre cele doua functii este aceea c& prima afiseazi mesajul dupa care trece la linia urmitoare, iar cea de a doua afigeazi mesajul pe linia curent, Functiile PUT_LINE si PUT sunt necesare deoarece nu putem accesa variabilele declarate in blocurile PL/SQL direct din mediul SQL (pentru a le afiga cu PROMPT). in unele medii de dezvoltare, ca de exemplu in SQL*Plus, trebuie utilizata la inceputul sesiunii comanda SET SERVEROUTPUT ON care activeazi bufferul pentru afisare. SET SERVEROUTPUT ON DBMS_OUTPUT.PUT_LINE ('... DBMS_OUTPUT.PUT ( END; 1 De exemplu, vom realiza un bloc PL/SQL care si afigeze un mesaj. Pentru a executa blocul utilizim mediul de dezvoltare SQL Developer. Mai intai este hecesara stabilirea conexiunii cu serverul Oracle: click dreapta pe eticheta Connections din stanga ecranului si completim detaliile referitoare la conexiune: ume utilizator si parola, adresa IP a serverului si portul, numele instantei bazei de date, , Introducere in PL/SQL (Procedural Language Extension to SQL) 17 4 oa Oza TY TEESE ea Mow tore het Sapce Ys Bhratin Tee ep o (St Yew Honote Bin Saye Verney Mein Toe BO G58a 9 xem 0-0-5. Goa 9° kam 0-0-5. — : vss ‘ et 1 Oe Baze de date. Limbajul PL/SQL = 9 4 [pi omor.ror unm (tesbeat USL ete taba) precedes ee he [Prfenas sort one, WQerpan|fprniace | owns cam | Cov cape eae crew et ap sept mh ene om ab) proce! cenit comune tet secs DECI, ECU, 96 TATA A ecEEIOR | Figura 2 Executia blocurilor PL/SQL in SQL. Developer Figura 1 Realizarea conexiunii cu serverul Oracle Codul sursa al blocului este redat mai jos: SET SERVEROUTPUT ON Dupi conectare apare fereastra in care putem serie si executa blocurile, in ~-INTRODUCEM COMENTARII: in acest caz sectiunea mod asemanator cu modul de lucru cu instructiunile SQL. Pentru executia a ¢ DECLARE lipsegte blocurilor se da click pe unul dintre butoanele Execute Statement (F9) sau Run --blocul afigeaz& un mesaj Script (F5). BEGIN DBMS_OUTPUT.PUT_LINE ('Limbajul PL/SQL este un limbaj procedural!') ; DBMS_OUTPUT.PUT_LINE('Un bloc PL/SQL contine trei seciuni: DECLARATIVA, EXECUTABILA, DE TRATARE A EXCEPTIILOR') ; END; / Baze de date. Limbajul PL/SQL ~S Exercitii propuse 1. Recapitulati notiunile de baz ale limbajului SQL, precum si comenzile utilizate. 2. Creati o conexiune noua in SQL Developer si testati programul clasic Hello World! VARIABILE PL/SQL IL1 Declarare si initializare Declararea variabilelor se realizeazi in zona declarativa delimitata prin DECLARE a blocului sau sub-blocului. Initializarea variabilelor se poate face la declarare sau in zona de executie intre BEGIN si END. Variabilele vor fi vizibile si in blocurile imbricate, incluse in el, mai putin in sub-blocurile in care numele lor este redefinit, la fel ca in cazul limbajelor de Programare structurate, unde semnificatia unui nume definit de utilizator intr-un bloc/sub-bloc este data de cea mai apropiata declaratie anterioara locului folosirii. La declararea variabilelor PL/SQL se precizeaza obligatoriu tipul de dat si optional anumite restrictii si un sir valid de valori. Declararea si initializarea se realizeaza astfel: nume_variabila [CONSTANT] TIP_DATA [NOT NULL] [:= | DEFAULT expresie]; Constantele in PL/SQL trebuie obligatoriu initializate, iar ulterior nu isi vor putea schimba valoarea. Variabilele pentru care se precizeaza restrictia NOT NULL trebuie obligatoriu initializate, iar ulterior nu vor putea primi valoarea NULL. Pentru a putea urmari mai usor codul sursi se foloseste urmatoarea conventie de notare: ¢_nume Constanta - pentru declararea constantelor; v_nume Variabila - pentru declararea variabilelor; _nume Variabila_globala —pentru variabilele globale definite in zona de specificatii a unui pachet de subprograme si valabile pentru toate procedurile si functiile pachetului. eee 20 Baze de date. Limbajul PL/SQL 11.2. Tipuri de variabile Variabile PL/SQL = Scalare = Compozite + Referinti + LOB (Large Objects): NCLOB, CLOB, BLOB, BFILE + Obiect Variabile non-PL/SQL: variabile de mediu (BIND VARIABLES) 11.2.1 Variabile scalare Tipurile scalare contin valori simple, o variabila scalara poate confine la un moment dat 0 singura valoare si corespunde in principal tipurilor de date Oracle. Cele mai cunoscute si utilizate tipuri de date sunt prezentate in tabelul urmator: char (lung_max) - lungime fix de max 32.767 bytes = varchar? (lung_max) — lungime variabild de max 32.767 bytes = long [sir de caractere de lungime variabili 2GB] = number (precizie,scala) = boolean (true, false, null) = date = binary_integer si pls_integer (numere intregi intre - 2147483647 $i 2147483647) = binary_float si binary_double (pentru numere reale in varianta Oracle 10g) = timestamp (pentru fractiuni de secunda) Pentru alte tipuri de date puteti consulta capitolul 3 din [ORAPL]. Exemple: v_functie varchar2 (9); v_numar binary integer v_totalsal number (9,2) v_datainceput date:=sysdate+7; c_taxa constant number (3,2) :=8.25; v_valid boolean not null:=true; Variabile PL/SQL 21 Afisarea variabilelor PL/SQL se realizeaza prin intermediul functiei PUT_LINE din pachetului DBMS OUTPUT. Se poate utiliza operatorul de concatenare ( || ) pentru a afiga mai multe mesaje sau variabile pe aceeasi linie, de exemplu: DBMS_OUTPUT.PUT_LINE ('VALOAREA VARIABILEI ESTE: |\variabila); Popularea variabilelor cu valori din tabelele bazei de date Se utilizeazi comanda SELECT cu clauza INTO pentru popularea variabilelor PL/SQL cu valori ale atributelor din tabele, insd pentru aceasta cererile SELECT din cadrul blocurilor PL/SQL trebuie si furnizeze o singura linie rezultat, in caz contrar se va semnala o eroare. Pentru a putea accesa o anumit inregistrare sau valoare a unei coloane dintr-o tabeld este obligatoriu s& utilizim variabile PL/SQL, aga cum se observa si in exemplul urmator: Exemplu: Sd se afiseze numele angajatului cu codul 100: SET SERVEROUTPUT ON DECLARE --declaram variabila v_nume in care vom returna numele angajatului: v_nume VARCHAR2 (20) ; BEGIN -- utilizam instructiunea SELECT cu clauza INTO pentru a popula variabila: SELECT nume INTO v_nume FROM angajati WHERE id_angajat = 100; -- afisam valoarea varibilei: DBMS_OUTPUT. PUT_LINE('NUMELE ANGAJATULUI ESTE: ' || v_nume) ; END; / Rezultatele executiei blocului sunt prezentate in figura 2.1 _— 22 Baze de date. Limbajul PL/SQL Variabile PL/SQL 23 oe Ss Exemple: 2500 9¢ xan 0-0-5- eee ‘Sd se afiseze numele si prenumele angajatului cu codul 100: ae 1a. DECLARE Sree --declaram doua variabile pentru nume si 2G we come YOR): — i v_nume angajati.nume%TYPE; rae suai ir v_prenume angajati.prenume%TYPE; | aa [ BEGIN paar Figura 2.1 Executia blocului PL/SQL 1. Atributul %TYPE Pentru a declara o variabili PL/SQL care sa aiba acelasi tip de date cu o anumit& coloana dintr-o tabeli sau cu o alti variabilé declarati anterior putem: folosi atributul %TYPE. Declararea unei variabile cu % TYPE se face astfel: variabila sau variabilal tip_data; variabila2 _variabilal%TYPE; tabela.nume_coloani%TYPE; -- populam si afisam variabilele: SELECT nume, prenume INTO v_nume, v_prenume FROM angajati WHERE id_angajat = 100; DBMS_OUTPUT. PUT_LINE('NUMELE ANGAJATULUI ESTE:' || v_nume||' '| |v_prenume) ; END; Observatie: Restrictia NOT NULL a unei coloane nu se aplica si variabilei declarate prin folosirea atributului %TYPE. 2. Variabile de mediu sau variabile de legdtura ale aplicatiilor gazdi (BIND VARIABLES) Acestea sunt variabile de legaturi cu aplicatia in care ruleazi motorul PL/SQL si trebuie declarate in aplicatie (in mediul gazda), putand fi accesate i modificate in cadrul blocurilor PL/SQL. Dupa terminarea executiei blocului PL/SQL, variabila rimane in mediul gazdi cu valoarea primité in urma rul&rii blocului si poate fi pasati altui bloc, realizind astfel transmiterea de valori intre blocurile PL/SQL. Variabilele de mediu nu pot fi utilizate in cadrul procedurilor, functiilor sau pachetelor. Variabilele de mediu se declara in afara blocului PL/SQL cu ajutorul cuvantului cheie VARIABLE: VARIABLE g_numevariabili TIP Observagie: pentru declararea unei variabile de mediu de tip NUMBER nu se specifica precizia si scala; "4 Baze de date. Limbajul PL/SQL Variabilele de mediu se utilizeaza in cadrul unui bloc PL/SQL sau intr-o instructiune SQL din afara blocului prefixate cu (:) astfel: shost_variabila:=v_variabila; Variabilele de mediu se afigeazi fn afara blocului cu ajutorul comenzii PRINT (la afigare variabila nu se va prefixa cu “:”); PRINT g_numevariabili Exemple: 1. St se afigeze mesajul: Bloc PL/SQL cu ajutorul unei variabile de mediu: --declaram variabila de mediu: VARIABLE g _mesaj varchar2 (30) BEGIN -- atribuim mesajul variabilei "Bloc PL/SQL'; -- afisam valoarea variabilei in afara blocului: PRINT g_mesaj 2. Séi se afiseze numarul de comenzi a cdror modalitate de completare este online: SET SERVEROUTPUT ON -- declaram variabila de mediu: VARIABLE g_comenzi varchar2 (30) BEGIN -- utilizam o cerere SQL pentru a calcula si popula variabila cu numarul de comenzi: select count(*) into :g comenzi from comenzi where modalitate END; a -- afisam variabila: PRINT g_comenzi ‘online! ; Variabile PL/SQL 25 | Observatie: Se poate auto-afisa variabila prin setarea AUTOPRINT ON Exemplu: Sa se selecteze produsele si pretul acestora pentru acele produse care au prepul < preful mediu al produsului cu codul 3133 faré a utiliza 0 cerere imbricaté: SET SERVEROUTPUT ON SET AUTOPRINT ON VARIABLE g pret number BEGIN select avg(pret) into :g pret from rand_comenzi where id _produs = 3133; END; / --utilizam variabila de mediu intr-o interogare SQL: select * from rand_comenzi where pret< :g_pret; Figura 2.2 Executia blocului PL/SQL 26 Baze de date. Limbajul PL/SQL 3. Variabile de substitutie De regula, variabilele de substitutie sunt folosite pentru a transmite valori dinspre mediul SQL*Plus spre comenzile SQL sau blocurile PL/SQL, in timp ce variabilele de legatura (bind variables) sunt folosite pentru a transmite valori in sens invers sau pentru a transfera valori intre blocuri PL/SQL lansate succesiv (primul bloc seteaz variabila, urmatorul o consult). Prin variabilele de substitutie se pot transmite valori comenzilor SQL sau blocurilor PL/SQL lansate (folosind "&" sau "&&"). Se pot utiliza in comenzile SQL sau in blocurile PL/SQL prin "&nume_variabila" sau "&&nume_variabila". Variabilele de substitutie sunt locale sesiunii SQL in care au fost declarate. in mediul SQL variabilele de substitutie pot fi usor utilizate prin introducerea de valori de Ja tastatura utilizind ACCEPT, se pot defini cu DEFINE sau afisa pe ecran cu PROMPT; Exemplu: Sa se afigeze numarul de comenzi ale angajatului al cérui cod este introdus de la tastaturé de cdtre utilizator prin intermediul variabilei de substitutie &id_angajat: DECLARE v_nr_comenzi number (2) ; BEGIN --introducem id-ul angajatului cu ajutorul variabilei &id_angajat select count (nr_comanda) into v_nr_comenzi from comenzi where id_angajat=&id_angajat; dbms_output.put_line('Angajatul are: '| | v_nr_comenzi||' comenzi') ; END; / Variabile PL/SQL 27 (ea i f i 3 STR ml = Figura 2.3 Executia blocului PL/SQL fntr-un bloc PL/SQL se pot utiliza toate tipurile de variabile, respectind ins caracteristicile si regulile de utilizare ale acestora. in exemplul urmator se utilizeazi atat variabila de substitutie s_mume definita si initializata prin comanda DEFINE, cat si variabila de legatura g_salariul, dar si variabila local v_prenume de acelasi tip cu coloana nume din tabela Angajati. Variabila de substitutie definita cu DEFINE va fi implicit de tipul CHAR. Exemplu: Si se afiseze salariul si prenumele angajatului cu numele Abel: SET SERVEROUTPUT ON -- definim variabila de mediu VARIABLE g_salariul number -- definim variabila de substitutie DEFINE s_nume=Abel DECLARE --declaram variabila locala PL/SQL v_prenume angajati.nume%type; BEGIN select prenume,salariul into v_prenume, | salariul from angajati where nume='&s_nume'; 28 Baze de date. Limbajul PL/SQL Variabile PL/SQL 29 DBMS_OUTPUT.PUT_LINE ('Prenumele angajatului rE St ie = = a : este: '||v_prenume) ; foae 90 xe END; aio is EA ; aay Z | commas print g salariul 11.2.2 Tipuri de date compuse u radu eve Span di3138; 1. Tipul RECORD Reprezinti un grup de date logic corelate (de exemplu, datele despre un client: code, nume, adresa sunt diferite ca tip dar corelate logic). Atunci cand se declard un PL/SQL record pentru aceste cimpuri, ele pot fi manipulate ca o unitate, ficcare cémp (element al structurii) avand un nume si un tip de data, Atributele unui record sunt referite astfel: nume_record.nume_cémp. Declarea unui tip record se face astfel: | TYPE nume record IS RECORD (numecimp TIP_DATA [,nume_cimp TIP_DATA:=|DEFAULT valoare]...); Tar declararea unei variabile de acest tip: Variabili. NUME_RECORD; Figura 2.4 Executia blocului PL/SQL Exemplu: Utilizand un tip de data record definit de utilicator sd se afiseze ' Pretul minim al produsului cu codul 3133. Pentru a defini un record pe baza coloanelor unei tabele se foloseste : Yorowtype . In acest caz numele elementelor din record au acelagi nume ca si coloanele tabelei, acelasi tip de date si se gisesc in aceeasi ordine. NUME_RECORD tabelaY%SROWTYPE; DECLARE TYPE tip _produse IS RECORD (v_cod produse.id produsttype NOT NULL:=3000, v_den produse.denumire_produs%type, Exemplu: Sa se rescrie exemplul de mai sus utilizand atributul v_pret_min produse.pret_minttype) ; YROWTYPE:. vrec_prod tip _produse; a eS DECLARE dusetrowtype; SELECT id_produs, denumire produs, pret_min — prod produ; ‘YP! into vrec_prod from produse where id_produs=3133; dbms_output .put_line('Produsul: | II vrec_prod.v_den|| ' are pretul minim: "| |vrec_prod.v_pret_min) ; END; / SELECT * into vrec_prod from produse where id_produs=3133; dbms_output .put_line('Produsul: '| | vrec_prod.denumire produs|| ' are pretul minim: "| |vrec_prod.pret_min) ; END; / 30 Baze de date. Limbajul PL/SQL Variabile PL/SQL at 2. Specificati ce se va afiga la rularea urmatorului bloc PL/SQL (care contine blocuri imbricate, ilustrand domeniul de vizibilitate al unor variabile care au acelasi nume): DECLARE EXERCITI PROPUSE var NUMBER; BEGIN var 1; . DBM: , 1. Specificati ce se va afisa la rularea urmiitorului bloc PL/SQL: S_OUTPUT. PUT_LINE (var) ; ‘bli DECLARE see a v_varl NUMBER :=100; nae v_var2 NUMBER; | NUMBER ; 7 BEGIN v_var3 NUMBER := v_var2; | 3 7 : : var :=2; v_var4 VARCHAR (20) : 'variabila PL/SQL'; a our v_var5 NUMBER NOT NULL := v_varl; | _ OUTPUT. PUT_LINE (var) ; Ee END bloc1; c_const1 CONSTANT DATE TO_DATE ('12/02/2007', 'dd/mm/yyyy') + | DBMS_OUT: A c¢_const2 CONSTANT NUMBER NOT NULL := 2; }_OUTPUY. PUT_LINE (var) ; c_const3 CONSTANT NUMBER := NULL; | v_var6é NUMBER DEFAULT NULL; } ee - | DECLARE var NUMBER; BEGIN a2 ER; DBMS_OUTPUT. PUT_LINE('variabila 1 = '||v_varl); a DBMS_OUTPUT. PUT_LINE('variabila 2 "| |v_var2) ; oe ae DEMS_OUTPUT. PUT_LINE('variabila 3 = '||v_var3); s_OUTPUT. PUT_LINE (var) ; DBMS_OUTPUT.PUT_LINE('variabila 4 = ' | |v_var4); I DBMS OUTPUT. PUT LINE('variabila 5 = '||v_var5); <> 7 7 DECLARE DBMS_OUTPUT. PUT_LINE('constanta 1 = | |e_const1) ; var NUMBER; 7 BEGIN DBMS_OUTPUT. PUT_LINE('constanta 2 = var :=4; "| |c_const2) ; i DBMS_OUTPUT.PUT_LINE('constanta 3 = DBMS_OUTPUT. PUT_LINE (var) ; ‘| |e_const3) ; DBMS_OUTPUT. PUT_LINE (bloc2.var) ; DBMS_OUTPUT.PUT_LINE('variabila 6 = '||v_var6); END b1OSeiToe STU ., END; DBMS_ouUTPYfePUT_LINE (var) ; a DEPARTAMENTU BIBLIOTECA = atl Te Baze de date. Limbajul PL/SQL 32 DBMS_OUTPUT . PUT_LINE (var) ; END; / 3. Specificati ce se va afiga la rularea urmitorului bloc PL/SQL: DECLARE stoc NUMBER (3) :=600; mesaj VARCHAR2 (50) :='Produsul 101'; BEGIN DECLARE stoc NUMBER (3) :=10; mesaj VARCHAR2 (50) :='Produsul 102'; um VARCHAR2(10):= ' bucati '; BEGIN stoc:= stoc+l; mesaj:='Stocul pentru ' | |mesaj||' este de: '||stoc| |um; DBMS_OUTPUT. PUT_LINE (mesaj) ; END; stoc:= stoc+100; mesaj:='Stocul pentru '| |mesaj||' este de: '||stoc| |um; DBMS_OUTPUT. PUT_LINE (mesaj) 7 END; i 4, Sa se calculeze suma a doua numere, iar rezultatul si se divid’ cu 3. ‘Numerele se vor introduce de la tastatura: 5, S& se afigeze salariul marit cu un procent. Salariul si procentul se introduc de la tastatura. INTERACTIUNEA CU SERVERUL ORACLE PRIN COMENZI SQL Interactiunea se realizeazi prin intermediul comenzilor de definire a datelor - DDL (Data Definition Language), limbajul de gestiune a utilizatorilor - DCL (Data Control Language), limbajul de manipulare a datelor - DML (Data Manipulation Language), limbajul de control al tranzactiilor - TPL (Transaction Processing Language) astfel: ° PL/SQL suport toate comenzile din limbajul de manipulare a datelor (DML) si din cel de control al tranzactiilor (TPL). Un bloc PL/SQL nu e o tranzactie, comenzile Commit/ Rollback/ Savepoint sunt independente de bloc dar pot sa apara in interiorul sau. Comenzi DMI/TPL Execufie SELECT, INSERT, UPDATE, Se execut normal in cadrul bloculul DELETE, MERGE COMMIT, ROLLBACK, Pot aparea in bloc dar au efect asupra SAVEPOINT tuturor —tranzacfiilor din _interiorul si din afara acestuia. * PL/SQL nu suporti comenzi de definire a datelor sau de gestiune a utilizatorilor (DDL sau DCL) in cadrul unui bloc. Pentru executarea acestor comenzi se utilizeaz’ comanda EXECUTE IMMEDIATE ‘comanda DDL’: Comenzi DDL/DCL Execujie CREATE, ALTER, DROP EXECUTE IMMEDIATE ' CREATE GRANT, REVOKE TABLE.... ' Exemple: 1. Sa se creeze tabela PROD in cadrul unui bloc PL/SQL: SET SERVEROUTPUT ON BEGIN EXECUTE IMMEDIATE 'CREATE table prod AS SELECT * FROM produse where 1=2'; END; / 34 Baze de date. Limbajul PL/SQL i " 1 a) uegepaBy p =50; i410; end loop; END; / 48 Baze de date, Limbajul PL/SQL (5 ec SOL Botner orca = — ere Gcad 9e-¥GG O-0- 8. i Figura 4.3 Execufia blocului PL/SQL Structura WHILE.....LOOP....END LOOP are urmdtoarea structuré WHILE cond LOOP Secventa comenzi 1; Secventa comenzi 2; EXIT [WHEN cond] ; END LOOP; Exemple: 1. Sa se afiseze pe ecran utilizand structura WHILE LOOP...END LOOP numerele 9,7, 4, 0. set serveroutput on DECLARE v_nr number (2) i number (2 BEGIN while v_nr > 0 loop v_nr:=v_nr-i; itl; dbms_output .put_line(v_nr) ; end loop; = END; / 0; Structuri fundamentale de programare 49 2. Sa se afigeze in ordine angajatii cu codurile in intervalul 100-110 atét timp cat salariul acestora este mai mic decat salariul mediu: DECLARE v_sal angajati.salariulstype; v_salMediu v_sal%type; i number (4) 00; BEGIN --calculam salariul mediu: SELECT avg(salariul) into v_salmediu from angajati; dbms_output .put_line('Salariul mediu este: "| |v_salmediu) ; while i<=110 loop select salariul into v_sal from angajati where id_angajat: dbms_output .put_line('Salariatul cu codul "| [4] |" ave salariul: '||v_sal); 41; --iesim din ciclul WHILE la indeplinirea conditiilor exit when v_sal> LOOP v_var:=v_var+tl; 52 Baze de date. Limbajul PL/SQL EXIT WHEN v_var>10; <> LOOP EXIT LOOP_EXTERN WHEN condi; EXIT WHEN cond2; _—— Exercifii propuse 1. Utilizand structuri alternative, rezolvati ecuatia de gradul 2. 2, Afisati numele si telefonul aferente fiec&rui client cu id-ul numar par. 3. Construiti un bloc anonim pentru a exemplifica structurile LOOP imbricate, CURSORI $I TABELE INDEX BY Cursorul reprezinté 0 variabila PL/SQL speciala utilizata atunci cand se executi 0 comand SQL, iar Oracle Server deschide 0 zona de memorie (context area) in care comanda este executatd. Cursorul este un pointer catre aceasti zona. in PL/SQL se utilizeazi doua tipuri de variabile de tip cursor: cursorul implicit declarat pentru toate instructiunile de manipulare (INSERT/UPDATE/ DELETE); cursorul explicit declarat si gestionat de programator pentru utilizarea comenzii SELECT in cazul in care aceasta returneaza mai multe inregistrari. V.1 Cursorul implicit Cursorul implicit este declarat de Oracle Server pentru toate comenzile de manipulare a datelor (INSERT, UPDATE, DELETE) prezente jntr-un bloc PL/SQL. Daca o instructiune DML nu afecteaz& nicio linie a tabelei nu se genereazd eroare, insd exceptia trebuie tratati folosind atributele speciale ale cursorilor. Cursorul implicit prezinta o serie de atribute care pot fi utilizate intr-un bloc PL/SQL pentru verificarea executiei instructiunilor DML. Aceste atribute sunt: ¥ SQL%ROWCOUNT- reprezinté numiarul liniilor returnate pana in momentul curent; vy SQL%NOTFOUND - evaluat la TRUE in cazul in care cea mai recenta instructiune de manipulare nu a afectat nicio linie; ¥ SQL%FOUND - opusul lui SQL%NOTFOUND. 54 Baze de date. Limbajul PL/SQL. Exemple: 1. Sé se steargd un produs din tabela PRODUSE si sd se contorizeze numarul de randuri sterse. SET SERVEROUTPUT ON DECLARE v_rez NUMBER (2) ; BEGIN DELETE FROM produse WHERE id _produs=111; }QLROWCOUNT ; DBMS_OUTPUT.PUT_LINE (v_rez || ' randuri sterse') ; COMMIT ; END; / 2. Sa se incerce modificarea denumirii produsului cu codul 3, in cazul in care acest produs nu existd (comanda update nu afecteazétnicio inregistrare) va fi Gfisat un mesaj corespunzdtor. BEGIN UPDATE produse SET denumire_produs='cafea' WHERE id_produs=3; IF SQL&NOTFOUND THEN DBMS_OUTPUT.. PUT_LINE('Nu exista produsul cu acest cod') ; END IF; END; / Cursori si tabele index BY 55 ce es Ta =EaE ag et Bow Deter bn Se You Wow Tew bo G58 9% X85 0-0- 8- kk aul OG) asic: Deerteire soi aS eS, Op ney DEGRS UN ¢ amu soc J 2 Ge gaa ‘lod complete : = | Figura 5.1 Executia blocului PL/SQL 3. Sai se steargd din tabela EMP_SAL salariatul al caérui ID este introdus de utilizator prin intermediul variabilei g_angid: ACCEPT g_angid PROMPT 'Introduceti id-ul salariatului:' VARIABLE nr_sters varchar2 (100) DECLARE BEGIN DELETE FROM emp_sal WHERE id_angajat-&g_angid; imr_sters:=TO_CHAR(SQL%ROWCOUNT) | |! INREGISTRARI STERSE END; i PRINT nr_sters Rollback; 56 Baze de date, Limbajul PL/SQL 4m O-G: 8- Denne Denes 014 FT neces Sal aloe" seca varchar) PLETE Fa eap_el ERE 14 ages Figura 5.2 Executia blocului PL/SQL V.2. Cursorul explicit Cursorul explicit se utilizeaz& pentru a procesa individual fiecare linie Ginregistrare) returnati de o instrucfiune SELECT ce returneazi mai multe inregistriri, Multimea inregistrarilor returnate de o cerere este numit& mulfime rezultat. Cursorul pastreaz& un pointer citre linia curenta in cadrul unei multimi rezultat. Verificarea stiri unui cursor explicit se realizeazi prin intermediul urmitoarelor atribute: v nume_cursor%ISOPEN - evaluat la TRUE in cazul in care cursorul este deschis; ¥ nume_cursor %NOTFOUND - evaluat la TRUE in cazul in care cel mai recent citire a valorilor din cursor nu a returnat nicio linie; ¥ nume_cursor %FOUND - opusul lui %NOTFOUND; ¥ nume_cursor %ROWCOUNT - are ca valoare numarul liniilor returnate pana in momentul curent. Cursori gi tabele index BY 57 Prelucrarea cursorului explicit presupune parcurgerea urmatoarelor etape: © se declara variabilele in care vor fi incarcate valorile corespunzatoare unei linii din cursor; © se declara cursorul explicit, specificdndu-se un nume pentru acesta gi definindu-se interogarea de procesat in cadrul lui: nume_cursor IS SELECT. © se deschide cursorul prin intermediul instructiunii OPEN, care executi interogarea si legarea tuturor variabilelor referite. fnregistririle returnate de interogare sunt desemnate drept set activ de date, care pot fi de acum incarcate. OPEN nume_cursor; ¢ —utilizéndu-se instructiunea FETCH, se citeste linia curenta din cursor si se atribuie valorile variabilor. Fiecare inc&rcare determina mutarea pointerului cursorului la linia urmatoare din setul activ de date. FETCH nume_cursor INTO varl, var2,.. ; este inchis cursorul prin instructiunea CLOSE, care dezafecteazi setul activ de lini. Cursorul poate fi din nou deschis pentru a stabili un now set activ de lini. CLOSE nume_cursor; Pentru a procesa liniile unui cursor explicit se utilizeazi de obicei o structur’ repetitiva pentru executarea unei inc&rciri a variabilor pe baza valorilor din cursor (FETCH) in fiecare iteratie. fn final, toate liniile din setul activ sunt procesate si o comand’ FETCH executatd fara succes pozitioneazi atributul °%~NOTFOUND pe TRUE. inaintea primei citiri din cursor (prima comandi FETCH) valoarea cursor%NOTFOUND se evalueazi la NULL, ca si in cazul in care comanda FETCH nu se executi niciodat cu succes. Exemple: 1. Sa se afiseze lista cu numele $i salariul angajatilor din departamentul 60 folosind un cursor explicit: set serveroutput on DECLARE cursor ang cursor is select id_angajat, nume, salariul from angajati where id_departament=60; ang_id angajati.id_angajat%type; ang_nume angajati.numestype; 58 Baze de date. Limbajul PL/SQL, ang_sal angajati.salariulttype; BEGIN dbms_output .put_line('Lista cu salariariile angajatilor din departamentul 60'); open ang cursor; loop fetch ang_cursor into ang_id, ang_nume, ang_sal; exit when ang_cursor%not found; dbms_output .put_line('Salariatul '| |ang_nume||' are salariul: '||ang_sal); end loop; close ang_cursor; end; / [+ Orde SOC Deir erate = ‘ie Eat Ve Undone an Soute Versioning Morton Te He 3580 9% xamO-o 8 fet sogcatsoc into op $4) ang, esas * idee it en oy uttoteatad: | tae (Haart “Vmg.mae "ce edesels “tants 3 Bee a asa | eacteal Laces cee ealetul 200, Figura 5.3 Executia blocului PL/SQL Cursori si tabele index BY 59. Pentru o flexibilitate mai mare se poate utiliza o variabila de tip record pentru fnedrcarea valorilor din cursor. Aceasti variabila de tip record poate avea aceleasi atribute ca si cursorul prin specificarea proprietitii %ROWTYPE. in acest caz incitcarea din cursor se va face direct prin instructiunea FECH var_cursor INTO var_record. Exemplul de mai sus poate fi rescris astfel: set serveroutput on declare cursor ang cursor is select id_angajat, nume, salariul from angajati where id_departament=60; --tipul record pt incarcarea valorilor cursorului ang_rec ang_cursortrowtype; begin dbms_output .put_line('hista cu salariariile angajatilor din departamentul 60') ; open ang_cursor; loop fetch ang cursor into ang_rec; exit when ang_cursor%not found; dbms_output .put_line('salariatul ‘| Jang_xec.nume||' are salariul: '| |ang_rec.salariul) ; end loop; close ang_cursor; end; / 2. Sti se incarce in tabela T_ANG primii 5 angajati (id si nume) -- se creeazi tabela mesaje CREATE TABLE T_ANG (cod varchar2 (7), nume varchar2 (20) ) ; DECLARE v_id angajati.id_angajat%type; 60 Baze de date. Limbajul PL/SQL. v_nume angajati.nume%type; CURSOR cl IS SELECT id angajat, nume FROM angajati; BEGIN OPEN cl; FOR i IN 1..5 LOOP FETCH cl INTO v_id, v_nume; INSERT INTO t_ang VALUES(v_id, v_nume) ; END LOOP; CLOSE cl; END; / SELECT * FROM T_ANG; [wader See 5 ae tae © Eat Yew Uoote bm Sore Vows town Tole Dp 7 BO8e 9e:XG 0-9: 4- Qi 1D ODewke Pawnee | oe Bay DOSES UR G wemnnne i occ *gucu ag. 3 Dams Figura 5.4 Executia blocului PL/SQL Cursor! si tabele index BY 61 Testul de iesire din ciclul LOOP in acest caz se poate face gi cu ajutorul atributului %ROWCOUNT: --se gterg inregistr&rile din tabela T_ANG: Delete from T_ANG; DECLARE v_id angajati.id_angajat%type; v_nume angajati.numestype; CURSOR cl IS SELECT id angajat, nume FROM angajati; BEGIN OPEN cl; LOOP FETCH cl INTO v_id, v_nume; EXIT WHEN c1%ROWCOUNT>5 OR c1%NOTFOUND; INSERT INTO T_ANG VALUES (v_id, v_nume) ; END LOOP; CLOSE cl; END; / SELECT * FROM T_ANG; 3. Sai se afiseze primele 3 comenzi care au cele mai multe produse comandate. In acest caz inregistrarile vor fi ordonate descrescditor in functie de numérul produselor comandate: SET SERVEROUTPUT ON DECLARE CURSOR c_com IS select c.nr_comanda, count (r.id_produs) Numar from comenzi c, rand_comenzi r where c.nr_comanda=r.nr_comanda group by c.nr_comanda order by count (r.id_produs) desc; xec_com c_comtrowtype; BEGIN DBMS_OUTPUT. PUT_LINE('Numarul de produse pentru fiecare comanda:') ; IF NOT c_com%ISOPEN THEN 02 Baze de date. Limbajul PL/SQL OPEN c_com; END IF; LOOP FETCH c_com INTO rec_com; EXIT WHEN c_com%NOTFOUND OR c_com%ROWCOUNT>3 ; DBMS_OUTPUT. PUT_LINE('Comanda ‘| |xec_com.nr_comanda||' are: ‘| |zec_com.numar||' produse') ; END LOOP; CLOSE ¢c_com; END; / [erate Sax Oeveloper orate Ip = ene Pern (Eo Mew Bite fn Sees versiing Wiraion Toco Hep 308d 9= KON 0-0-8. @ 5k Obes Bar 2 Qos ise 5 Sree arity ens GlsettOunt enka: ga] Sonacag Figura 5.5 Executia blocului PL/SQL Cursori si tabele index BY 63 4, Sié se afiseze cota de impozitare si valoarea impozitului platit de primi n angajati. Se va utiliza un cursor, iar n se citeste de la tastatura. set serveroutput on; DECLARE CURSOR c is select nume,salariul from angajati; var cérowtype; imp number (4,2) ; val angajati.salariulttype; BEGIN OPEN c; LOOP FETCH c INTO var; imp:= CASE when var.salariul<1000 THEN 0.5 when var.salariul between 1000 AND 3000 then 1.25 when var.salariul between 3000 and 5000 then 1.5 when var.salariul between 500 and 7000 then 1.75 else 2 END; val:=imp*var.salariul; DBMS_OUTPUT.PUT_LINE('Angajatul '||var.nume||' are impozitul pe salariu de '||imp||'% - "| |val|[ |" RON'); EXIT WHEN c%rowcount>&n OR c%notfound; END LOOP; CLOSE c; END; Z 64 [Foret ewe oad a (le Kat ow teiote a Sogee erry ton Toa Bp BeBe 9e Rao .9-9-°5- Baze de date. Limbajul PL/SQL © Der DEGAS ORO tumeme ny _OUTPC. POT AEA “Ilva ae ape pe estat Ss {DOT Wen eteoromen Ot cottons Sei |epraanes | eeiscupe| Gove aane [cpus ios eepletad 1 Arpjetal King ate toperita pe satan se 2 } opteal Hoeae ee gota pe alas de 2 = 3609 FOE Jogeata be Bann ace Sapocttal pe selects ae 28 = 3400 Ra | Jangeatal Binal wresaprsca pe sunt e 24 ~ 1600 ba JnogeeeatEeve ee apeeeut pe selesu 61758 ~ 10560 Rat | I Inge atten ce sopersel pe snags de 1.58 ~ 70 Jageaca Peaella xe sopesscal pe onli Go 1.56 = 7200 Rl Joga Loent ace ingoeteal pe seas de 1-54 ~ 6200 Rab Figura 5.6 Executia cursorului pentru n=7 Gestiunea implicité a cursorului prin utilizarea unui ciclu FOR FOR nume_record IN nume_cursor LOOP In acest caz, tipul RECORD nu trebuie declarat. Se realizeazi in mod implicit deschiderea (OPEN), incdrcarea (FETCH) gi inchiderea cursorului (CLOSE). Exemplu: Si se afiseze printr-un ciclu FOR numele si salariile angajagilor din departamentul 50: set serveroutput on declare Cursori si tabele index BY 65 cursor ang_cursor is select id_angajat, nume, salariul from angajati where id_departament=50; begin dbms_output .put_line('Lista cu salariariile angajatilor din departamentul 50') ; for ang_rec in ang cursor loop dbms_output .put_line('Salariatul "| |ang_xec.nume||' axe salariul: "| |ang_rec.salariul) ; end loop; end; 8289 9% ¥BO OS 8- Ig © Doaciiy | DEAS BBG came eres ee - ——— i {3 00.00 ‘set serveroutput ox EB wr cresor eogousoe is select AA aaj, nant, sali fem engejeh wee 1 depactmen $0; ¥: 21D ett sin F = See eee ee i cone fe oes nog Io t = orem Gropp ie lene sme ccs ‘Ihe aais ' BiBven ‘end Loop: 6 Lindos em; © (Greingns t Breese © rectors i Bowes Baan ~ - SSomm tier (pats Biowomd (ean gatiee|Seasae| Connoae e re aa ‘a S ee 2 p_val i ie Os eatin aad | _—— [Pin produsui 289, RB 101/25, s-m ORDER BY total desc; Seen ieugtit rom, tone CE, os var HL test 1G Bee is ssama ste, ner may 0 so ae nes 2 iBReeSimavme on pvaual ZBL, Ps 20" st, 991 stats v_val NUMBER (5) ; eee ag: Pix Petal 225, Manes Soc ty orn ene 942 tant xrec_prod c_prod%rowtype; BEGIN v_val:=500; DBMS_OUTPUT. PUT_LINE('Produsele al caror cantitate vanduta este mai mare decat '| | v_val); IF NOT c_prod’ISOPEN THEN OPEN ¢ prod (v_val); END IF; LOOP FETCH c_prod into rec_prod; EXIT WHEN c_prod%not found; DBMS_OUTPUT.PUT_LINE('Din produsul "| |xec_prod.id_produs||', '| |xec_prod.denumire_produs||', s-au vandut ' | |zec_prod.total||' unitati'); END LOOP; CLOSE c_prod; END; / Figura 5.9 Executia blocului PL/SQL 2. Sai se afiseze pentru fiecare comandé produsele comandate. in acest caz se utilizeazé doud variabile de tip cursor: SET SERVEROUTPUT ON DECLARE --cursorul care va prelua comenzile incheiate CURSOR c_com IS SELECT nr_comanda, data FROM comenzi Where modalitate= ‘online! ORDER BY nr_comanda; --cursorul care, pentru fiecare comanda, va afisa produsele din cadrul acesteia, ordonate descrescator CURSOR c_prod (p_nr_comanda NUMBER) IS 70 Baze de date. Limbajul PL/SQL SELECT r.id_produs, p.denumire_produs, x.cantitate FROM produse p, rand_comenzi r WHERE p.id_produs=r.id_produs AND r.nr_comanda=p_nr_comanda ORDER BY r.id_produs desc; rec_com c_comtrowtype; --variabila record pentru campurile din primul cursor xrec_prod c_prod%rowtype; --variabila record pentru campurile din al doilea cursor BEGIN OPEN c_com; LOOP FETCH c_com into rec_com; EXIT WHEN c_com*not found; DBMS_OUTPUT. PUT_LINE('Comanda ' | | rec_com.nr_comanda ||! incheiata la data de "| |rec_com. data) ; OPEN c_prod (rec_com.nr_comanda); --cursorul primeste drept parametru numarul comenzii care a fost afisata LOOP FETCH c_prod into rec_prod; EXIT WHEN c_prod%not found; DBMS_OUTPUT. PUT_LINE('Din produsul ‘| |rec_prod.id_produs||', '| |rec_prod.denumire_produs||', s-au comandat ' | |rec_prod.cantitate||' bucati'); END LOOP; CLOSE c_prod; DBMS_OUTPUT. PUT_LINE(' END LOOP; CLOSE c_com; END; Z Cursori si tabele index BY 7 7 Oracle 5 Developer Feracie - 7 (Be (Ot Low Wvowe Bm Soe Veegnny Mraon Teele te 8589 9° XGRe@ @ 48- REO ¢ prod ite Teepe, Pin protusa 7300, Yee Cat [fos produ 225, @ 10, ex conta 195 cath comstat 200 eB on ‘BOY WHO ¢ prodiesttoud; eo eo An et“ eyo, tpt dea yo, 8 ores ‘a Loop} BG bet jainse ¢ prods et he) eon one sre em 2 Bie ‘mir Sane SRhreciages © WB Proceases || erections gone — © Bonet LLL BR tapes Denas (Bs Oded. teria |Sreaeoe | Goens 044] owe cunt & —— 78a fae | ——-—__ er Be caiet Eaten [ec 0 aa 2-9 0.2.5.9800 | ite i etn 259 Ra Hm emma Zt ane i ea 299, tere nn cma ea, BAB Ate Sponms: | Eeskonar os pe te, noe serps ema te See Pix ened ent, vara ememroe sie tia [bin prodasul 2323, Screws &n; END LOOP; ‘Be Em ew lvoe Bin Segre Verna Mawes Joa be Gada 9° xan 6-o Beals jaay [P Ghormies S Gum Ben euroment) ont 8 Omran OTT. SE Hate in epee veh. eae, epee: 8 Bree ‘Bo ir * orca Yc tet) om : 2 Screen see fitted ace fmceiaPresidest | BB Pde Somme yajatul :Rechhar seleriul :17000 | | @iosimut Joon i aqeeencrnccnis |; Sibert Ii oneennteeacen ct Pst | eee fer oe sce ese im sepacemnnuneestive ace cwscesetnnsetation Vise Peesldene "Figura 5.11 Executia cursorului pentru n=5 CURSOR c1(id_¢ clienti.id_client%type) is select ¢.nr_comanda,c.modalitate,p.denumire_produs, r.cantitate from comenzi c, rand_comenzi r, produse p where id_c=c.id client and r.nr_comanda= c.nr_comanda and r.id_produs= p.id_produs order by c.modalitate; BEGIN FOR var IN c LOOP DBMS_OUTPUT. PUT_LINE('Clientul: '| |var.nume_client||! a achizitionat urmatoarele produse: FOR varl IN cl(var.id_client) LOOP DBMS_OUTPUT. PUT_LINE(varl.denumire_produs| | ' '||varl.modalitate||' '||varl.cantitate) ; END LOOP; EXIT WHEN c%notfound; END LOOP; END; i 74 Baze de date. Limbajul PL/SQL Cursori gi tabele index BY 5 (Be ft Yew ovowo Bin Soy Vermenea Wordnet top 354d 9¢ XG @-0-.3- 7 aod a gay { 8 comectons: secon 7H 20am pi perey tore. rot cers “iar eet «seein mute ps * Bee | bere imrcne | Sams en, GOPT.e Iaceneepod' vataiatel ileum): eae bo inn; 1 Bow {at wet enn 5 Larson ‘Bo ua; I 5 Orton Be; k Wrenn ; Spee E Bone Pi orn crea or — SEC [bres Bonawe Goan lean Ganc onal Gowan I |) 2 Bee Cy | 8 Gi seemoe [Ciientul: Hasan e achisitionat uzaatoarele mo Rice rst =18 THEN RAISE e_ora_nepermisa; END IF; EXCEPTION WHEN e_ora_nepermisa THEN dbms_output .put_line('Este ora '||TO_CHAR(SYSDATE, 'HH24')); dbms_output .put_line('Operatiune permisa doar '||" in timpul programului') ; END; / 2. Si se modifice denumirea produsului cu id-ul 3. Dacd nu se produce nicio actualizare (valoarea atributului SOL4%.ROWCOUNT este 0) sau daca apare o alté eroare (OTHERS) atunci sd se declanseze exceptii prin care sd fie avertizat utilizatorul: DECLARE invalid_prod EXCEPTION; BEGIN incercam sa actualizam produsul cu codul 3 UPDATE produse SET denumire_produs='Laptop ABC! WHERE id produs=3; --vefificam executia comenzii UPDATE si declasam exceptia IF SQL%NOTFOUND THEN RAISE invalid_prod; END IF; EXCEPTION WHEN invalid_prod THEN 98 Baze de date. Limbajul PL/SQL. DBMS_OUTPUT.PUT_LINE('Nu exista produsul cu acest ID'); WHEN OTHERS THEN DBMS_OUTPUT. PUT_LINE('A aparut o eroare! Nu se poate actualiza denumirea produsului!') ; END; 7 (ore tirade — wee Be [a Yew tose fan Source Vorebning Mgrice Joos MD _ Go8d 90 xGn 0-0: 5- if + Bis 1B Oem [Dawns 2s aa E000 BMF mmm pea — oo oe are prose SY denice prose Lapey M8" mes Gls od ®ce) pane |Seoe pe] am ouse 7aa Figura 6.9 Tratarea excepfillor definite de utilizator Erorile definite de utilizator pot fi tratate la nivelul aplicatiilor ca si erorile Oracle Server prin atribuirea de coduri cu ajutorul functiei: RAISE_APPLICATION_ERROR (NR_EROARE, MESAJ); unde NR_EROARE poate fi un numar negativ cuprins intre -20000 si -20999, iar MESAJ este textul explicativ al erorii, ‘Tratarea excepfiilor 99 jn acest caz tratarea se realizeazi asemandtor cu erorile non-predefinite Oracle Server. Exemplu: Sa se atribuie exceptiei din exemplul anterior un cod si un mesaj de eroare gi sd se adauge aceste valori in tabela ERORI. DECLARE cod NUMBER; mesaj VARCHAR2 (255) ; invalid_prod EXCEPTION; PRAGMA EXCEPTION_INIT(invalid_prod, -20999) ; BEGIN UPDATE produse SET denumire_produs='Laptop ABC! WHERE id_produs=3; IF SQL%NOTFOUND THEN --declansam exceptia RAISE_APPLICATION_ERROR (-20999, 'Cod produs invalid!'); END IF; EXCEPTION WHEN invalid_prod THEN DBMS_OUTPUT.PUT_LINE('Nu exista produsul cu acest ID'); -- preluam codul si textul erorii cod: =SQLCODE; mesaj :=SQLERRM; INSERT INTO ERORI VALUES(USER, SYSDATE, cod, mesaj) ; END; / SELECT * FROM ERORI; Baze de date. Limbajul PL/SQL, [Fen Dont aap a = Ge Lm View tied Bin Source Versgning Moreton Too eo : 7 Jct 8084 96 xanO-0 48- Qs O[Ponene [Denne : [ERT MY ERE VALS ‘ser, STAT, co nese): ‘super + rm sa Figura 6.10 Tratarea exceptiilor definite de utilizator Propagarea exceptiilor Odata exceptia declansata in sectiunea executabila a unui bloc, se cauti in cadrul sectiunii de tratare a exceptiilor (EXCEPTION) o rutind de tratare asociata. Daca PL/SQL poate trata exceptia, ea nu este propagat’ in blocul exterior sau in mediul apelant, caz in care se considera c& executia blocului s-a desfasurat cu succes, _Atunci cand un sub-bloc trateazi o exceptie, acesta se termina normal iar executia se reia in blocul ce-I cuprinde imediat dupa instructiunea END a sub- blocului. : Daca apare o exceptie, iar in blocul curent nu exist o rutina pentru tratarea Sa, executia blocului se termina cu esec, iar exceptia se propaga succesiv in blocurile exterioare pana este gasita intr-unul din ele o rutin pentru tratarea ei, Daci nu se giseste nici una, tn mediul apelant apare © situatie de exceptie ‘Tratarea exceptiilor 101 nerezolvata, utilizator puténd observa mesajul de eroare care a intrerupt executia normali. EXEMPLU Utilizand blocuri imbricate, sd se afiseze comanda incheiatit de un anumit client al cérui nume este citit de la tastatura, Sa se trateze exceptiile aparute, set serveroutput on; declare nr_com comenzi.nr_comanda’type ; v_nume clienti.nume_client%type; v_id_client clienti.id_client%type; begin v_id_client: & id_client; -- returndm numele clientului intr-un sub-bloc, iar exceptia este tratat& local begin select nume_client into v_nume from: clienti where id_client = v_id_client; dbms_output.put_line('Clientul: '||v_id_client) ; exception when no_data_found then dbms_output.put_line('Nu exista informatii privind acest client!'); end; -- returnam comanda clientului intr-un sub-bloc, iar exceptiile sunt propagate in blocul exterior begin select nr_comanda into nr_com from comenzi where id_client = v_id client; dbms_output.put_line('Clientul' | |v_nume| | 'are comanda: ') ; A Baze de date. Limbajul PL/SQr, dbms_output .put_line (nr_com) ; end; exception when no_data_found then dbms_output .put_line('Nu exista informatii priving comenzile incheiate de acest client!'); when too_many rows then dbms_output.put_line('Clientul are mai multe comenzi!') ; end; / Exercifiu propus Creafi o tabelii numité Mesaje, avand un camp unic, de tip Varchar?, Scrieti un bloc PL/SQL pentru a selecta codul comenzilor incheiate in anul 2000, a, Daca interogarea returneazi mai mult de o valoare pentru numarul comenzii, tratati exceptia cu o rutind de tratare corespunzatoare si inserati in tabela Mesaje mesajul “Atentie! In anul 2000 s-au incheiat mai multe comenzi?”. b. Daca interogarea nu returneaza nicio valoare pentru numarul comenzii, tratati exceptia cu o rutind de tratare Corespunzatoare gi inserati in tabela Mesaje mesajul “Arentie! In anul 2000 nu s-au incheiat comenzi!”. e Daca se returneaz o singura linie, introduceti in tabela Mesaje numarul comenzii. ' d. Tratati orice alta exceptie cu o rutind de tratare corespunzitoare gsi inserati in tabela Mesaje mesajul “4 aparut 0 altd eroare!”. SUBPROGRAME PL/SQL Subprogramele sunt blocuri PL/SQL care au un nume, o definitie, o lista de parametri, pot fi proceduri sau functii si se pot stoca la nivelul bazei de date (proceduri/functii stocate) sau la nivelul aplicatiei (de exemplu procedurile sau functiile utilizate in aplicatiile realizate in Developer Suite). Variabilele declarate in zona declarativi a subprogramelor se numesc parametri formali (formal parameters), iar pentru acestia se pot specifica valori implicite (DEFAULT). Variabilele utilizate in apelul procedurii/functiei se numesc parametri actuali (actual parameters). in corpul procedurilor/functiilor nu se pot utiliza variabile globale sau de substitutie, acestea vor fi transmise in subprograme cu ajutorul parametrilor. VIL1 Proceduri Parametrii utilizati in cadrul unei proceduri pot fi de mai multe tipuri: ¢ _IN- parametru de intrare; valoarea sa se utilizeaz in cadrul procedurii far a putea fi modificat. Acesta reprezinta tipul implicit. © OUT - parametru de iesire; acesta primeste valoare la iesirea din cadrul procedurii, valoare ce va putea fi utilizata in mediul apelant. e IN OUT ~ parametru de intrare/iegire; valoare sa este utilizata atat ca parametru de intrare utilizat in procedura cat si ca parametru de iesire pentru transmiterea valorii sale in mediul apelant. Se impun urmitoarele concluzii in ceea ce priveste modul de utililizare a parametrilor: un parametru IN poate aparea numai in partea dreapta a (: un parametru OUT poate aparea numai in partea stnga a (:5); un parametru IN OUT poate aparea in ambele parti ale (:=). 104 Baze de date. Limbajul PL/SQL Sintaxa pentru crearea unei proceduri: CREATE [OR REPLACE] PROCEDURE NUME_PROC ((param1 [IN|OUT|IN OUT] TIP1, Param2[IN|OUT|IN OUT] TIP2, ....) ] -- tipul variabilelor este precizat fra dimensiune (ex: NUMBER sau VARCHAR2) Is|as ~- zona de declarare a variabilelor utilizate in subprogram -- NU se utilizeaza DECLARE BEGIN [EXCEPTION] END [NUME_PROC] ; Pentru a sterge procedura se utilizeazé urmitoarea sintaxt: DROP PROCEDURE NUME_PROC; Apelul unei proceduri se poate realica in urmétoarele moduri: ° prin apelul dintr-un bloc PL/SQL anonim sau un alt subprogram; * prin apel direct cu EXECUTE nume_proc sau EXEC nume_proe sau CALL nume_proe; * din alt mediu Oracle (Oracle Developer Suite). Pentru afigarea erorilor aparute la compilare se utilizeazA SHOW ERRORS. Exemplu: Sd se realizeze o procedurd pentru a adéuga in tabela PRODUSE 0 noud inregistrare cu id-ul produsului egal cu valorea ultimului id incrementat cu 0 unitate si denumire ‘Monitor LCD’. Sa se apeleze procedura dintr-un bloc PL/SQL, dar $i direct. SET SERVEROUTPUT ON ~" erearea procedurii. Nu avem parametri: CREATE or REPLACE PROCEDURE adauga_produs is ~-declararea variabilelor locale id_produs produse. id_produs %type; denumire_produs produse. denumire_produs %type; Subprograme PL/SQL 105 maxim produse. id_produs %type; BEGIN -- returnam ultimul id al produselor select max(id_produs) into maxim from produse; denumire_produs:='Monitor LCD'; -- adaugam o noua inregistrare: insert into produse (id _produs, denumire_produs) values (maxim+1, denumire_produs) ; DBMS_OUTPUT.PUT_LINE ('S-au adaugat '| |SQL%rowcount||' inregistrari') ; end; / SHOW ERRORS err ‘ fae i 8 2 ie . sect {| 2 awe Ad produs produse, id produs tte: { Gites ‘demmire_produs pre demmice pecdas stype? 2 Bier cn pit. Sse es 3 rem 2 i recages on | SUarecrire | eaet mse pet) to ncn fom rote 3 Grace | eBomer } geomtem | reobin (iy, eae pa) van in, deni jet; I eee (DORS OUTPUT.FOT LIME ('S-au inzecar *||SWLAcowcount|' inceytstract') = ian f Figura 7.1 Crearea procedurii 106 Baze de date. Limbajul PL/SQL apelul procedurii prin bloc PL/SQL anonim: begin adauga_produs; end; / -- apelul direct: call adauga_produs() ; F track Sh tention om Co Dew moss pe Soy Vowy Hai Ia be Goad 9 xXaDO-o 3- i ry ate Spnee — |Doreoas Glsewount teen ig atic | Ao cana] Qo owe = Bowen 693 ' 2 Prmeswes ecco boc camped ter sr inecet Linge | Figura 7.2 Apelul procedurii intr-un bloc PL/SQL Procedura creata este stocata in baza de date si poate fi apelat oricAnd, atat din blocuri PL/SQL cat si din alte medii de dezvoltare. Codul sursi gi lista parametrilor pot fi vizualizate in SQL Developer din meniul din dreapta->lista Procedures (figura 7.3). Subprograme PL/SQL 107 sind eeeor eer ee (m EM ew Howe im Sauce Vesoiy Borten Im Me 3080 9% XBR O-0-13- i Bom 1D ODevne (Memenmow | oo (ae Corts nrc Ite sae i , cateyoea protester pnts Ss i Sao ~ | snseet ate peeve (1490 ease pots) valuee (asta, denmire prot) | at USisorcome" scepitees'} Figura 7.3 Vizualizarea codului sursi al procedurii create Utilizarea parametrilor de tip IN — in acest caz valorile primite vor fi transferate si utilizate in cadrul procedurii fara a putea fi modificate. Exemplu: Sa se realizeze 0 procedura prin care si se majoreze salariul unui anumit angajat al cdrui id este primit ca parmetru cu un procent specificat tot printr-un parametru de intrare. Procedura MODIFICA_SALARIUL primeste doi parametri: p_id_angajat si procent si majoreazi cu procentul specificat salariul angajatului cu id_angajat=p_id_angajat: CREATE OR REPLACE oe PROCEDURE modifica_salariul_procent (p_id_angajat IN angajati.id_angajat%type, procent IN number) 109 108 Baze de date. Limbajul PL/SQL, Subprograme PL/SQL a ace mae aca v_salariul angajati.salariulttype; B088 9 KOR 0-018. ae BEGIN a GEES --returnam salariul angajatului cu id-ul specificat Select salariul into v_salariul from angajati where id_angajat=p_id_angajat; dbms_output .put_line('angajatul are salariul de "| |v_salariul) ; --realizam majorarea salariului Update angajati Set salariul=salariul* (1+procent/100) Where id_angajat=p_id angajat; -- returnam noul salariu Select salariul into v_salariul from angajati where id_angajat=p_id_angajat; Dbms_output .put_line('Angajatul are acum salariul de '||v_salariul) ; / show errors; Atentie! Salariul angajatului nu va fi modificat definitiv pana cand tranzactia nu este finalizati prin instructiunea COMMIT care poate aparea in Procedura (nerecomandat), in blocul apelant sau in mediul de dezvoltare, Anularea tranzactiei poate fi realizati in acelasi mod prin instructiunea ROLLBACK, asa cum se observa in apelul urmiator: EXECUTE modifica_salariul_procent (176, 10) Rollback; (cme Ss feroisen ce one sera 1006 Figura 7.4 Apelul procedurii MODIFICA_SALARIUL Utilicarea parametrilor de tip OUT — in acest caz valorile din procedura vor fi tranferate si utilizate in afara procedurii. Exemple: 1. Sa se realizeze 0 proceduré ce primeste ca parametru de intrare id-ul unui angajat si returneazd numele si salariul sau. : Procedura primeste ca parametru de tip IN id-ul angajatului si returneazi prin parametrii de tip OUT numele gi salariul acestuia: / CREATE OR REPLACE PROCEDURE cauta_angajat (p_id_angajat IN angajati.id_angajat%type, P_nume OUT angajati.nume%type, p_salariul OUT angajati.salariul%type) Is BEGIN --valorile parametrilor de tip OUT pot fi atribuite direct in clauza SELECT 110 Baze de date. Limbajul PL/SQL Select nume, salariul into p_nume, p salariul from angajati where id_angajat=p id angajat; END; / Apelul procedurii se va face intr-un bloc anonim care va afiga numele si salariul returnat de aceasta: SET SERVEROUTPUT ON DECLARE v_nume angajati.nume%type; v_salariul angajati.salariul%type; BEGIN Cauta_angajat (150, v_nume, v_salariul); -- afisam valoarea parametrilor actuali: DBMS_OUTPUT.PUT_LINE(' Angajatul '| |v_nume| | ' are salariul de: '||v_salariul); in PUEORE seas n° Figura 7.5 Apelul procedurii CAUTA_ANGAJAT Subprograme PL/SQL 111 2. Sa se realizeze 0 proceduré prin care se calculeazé salariul mediu al angajagilor: Procedura calculeaz salariul mediu si il returneaza printr-un parametru de tip OUT: CREATE or REPLACE PROCEDURE sal_mediu (p_sal_mediu OUT number) Is BEGIN Select AVG(salariul) into p_sal_mediu from angajati; END; / show errors; Apelul il realizim prin EXECUTE, direct in mediul apelant prin utilizarea unei variabile de gazda (de mediu): VARIABLE v_sal_mediu NUMBER EXECUTE sal_mediu(:v_sal_mediu) Print v_sal_mediu Figura 7.6 Apelul procedurii SAL_MEDIU 112 Baze de date. Limbajul PL/SQL 3. Sé se realizeze o procedura care primeste ca parametru de intrare id-ul unui produs si returneazd denumirea si pretul minim cu care s-a vandut acest produs. Procedura primeste ca parametru de tip IN id-ul produsului si returneaza prin parametrii de tip OUT denumirea si pretul minim al acestuia: CREATE or REPLACE PROCEDURE interogare_produse (p_codp IN produse.id_produs%type, p_denp OUT produse.denumire_produs%type, p_pret_min OUT produse.pret_minstype) Is BEGIN Select p.denumire produs, MIN(r.pret) into p_denp, p_pret_min from produse p, rand_comenzi r where r.id_produs=p.id_produs and p.id_produs=p_codp group by p.denumire_produs; END; / show errors; Apelul procedurii intr-un bloc anonim pentru un id introdus de la tastatur’: SET SERVEROUTPUT ON DECLARE v_denp produse.denumire_produs%type; v_pret_min produse.pret_min’type; BEGIN interogare_produse(&cod_produs, v_denp, v_pret_min) ; DBMS_OUTPUT. PUT_LINE('Produsul '||v_denp||' are pretul minim: '||v_pret_min) ; END; / Subprograme PL/SQL 113, [Fae reset ie te ewe be eae Ooo Moe Dee BoEe8 92 XBHO-0 43- ‘Qowne. t deny prods. demic prosuetime pet sin prot pet site! vm resect prone (ecod pro, w Sp, wpe sO. ATE osu! “11 911" sat “Ilnpeet sims ee ee ieeara Honstor 191 ace petal sina 462 Figura 7.7 Apelul procedurii INTEROGARE_PRODUSE pentru id_produs=2245 Utilizarea parametrilor de tip IN OUT = in acest caz valorile vor fi transferate si utilizate in cadrul procedurii si in afara acesteia. Exemplu: Sd se realizeze o procedurd prin care se modifica salariul unui angajat doar in cazul in care este mai mic decdt media prin apelarea procedurii create mai sus. Procedura MODIFICA_SALARIUL_MEDIU primeste id-ul angajatului ca parametru de intrare si salariul mediu actual si returneaza prin parametrul de tip IN OUT salariul mediu modificat prin apelul procedurii SAL_MEDIU. Baze de date, Limbajul PL/SQL, CREATE or REPLACE PROCEDURE modifica_salariul_mediu (p_id_angajat IN angajati.id_angajat%type, p_sal_mediu IN our number) Is nume angajati.nume%type; sal angajati.salariulstype; BEGIN Select nume, salariul into nume, sal from angajati where id_angajat= p id angajat; IF sal p_sal_mediu then return true; ELSE return false; end if; EXCEPTION WHEN no_data_found THEN return NULL; 120 Baze de date, Limbajul PL/SQL end; / show errors FORE Pama ‘ = (Be Kat ow triode fn Sete Yesang tiraien Too ED Gogg 9° xGn 0-0. 3- t O Demme a [ewer werner PUTIN veriion salaiah (td aognee DV engsjei. sd geet, p_sl nti mer) sens tooo, [tet ere tarts (Suber selacil into vateriul fem angst wese a. engjate td ageets 1 y.snteriut > pel sedis en Figura 7.11 Crearea functiei VERIFICA_SALARTUL Vizualizarea tipului returnat se poate face cu comanda: describe verifica_salariul; iar codul sursi al acesteia se poate vizualiza astfel: Select text From user_source Where name='VERIFICA_SALARIUL' and type=' FUNCTION! ; Acesta se poate vizualiza si edita din meniul dreapta> Functions. 7 Subprograme PL/SQL 121 [race Sa. Developer ———e ae "Lek (OF ft Yew Hoots im Sone Voom Mowin Tose ee 8580 9% kB 0-0 8. _— = Figura 7.12 Vizualizarea codului sursa al functiei VERIFICA_SALARIUL Apelul functiei se poate face dintr-un bloc PL/SQL. Vom apela mai intai procedura SAL_MEDIU pentru a returna salariul mediu, apoi vom apela functia creat cu 3 valori pentru ID_angajat pentru a verifica executia sa: SET SERVEROUTPUT ON DECLARE v_sal_mediu number; BEGIN --apelul procedurii pentru calculul salariului mediu: SAL_MEDIU (v_sal_mediu) ; --primul apel al functiei IF (verifica_salariul(11, v_sal_mediu) IS NULL) then dbms_output .put_line('Angajat cu ID invalid!'); 122 Baze de date. Limbajul PL/SQL. elsif (verifica_salariul(11, v_sal_mediu)) then dbms_output .put_line('Salariatul are salariul mai mare decat media!'); else dbms_output .put_line(' Salariatul are salariul mai mic decat media!'); end if; --al doilea apel IF (verifica_salariul(110, v_sal_mediu) Is NULL) then dbms_output .put_line('Angajat cu ID invalid!'); elsif (verifica_salariul(110, v_sal_mediu)) then dbms_output .put_line('Salariatul are salariul mai mare decat media!') ; else dbms_output.put_line(' Salariatul are salariul mai mic decat media!'); end if; --al treilea apel IF (verifica_salariul(104, v_sal_mediu) Is NULL) then dbms_output .put_line('Angajat cu ID invalid!'); elsif (verifica_salariul (104, v_sal_mediu)) then dbms_output .put_line('Salariatul are salariul mai mare decat media!'); else dbms_output .put_line(' Salariatul are salariul mai mic decat media! end if; END; / Subprograme PL/SQL [iF rede Deeper a (fe (a Yew Bote ie Sone Veninmy evn Ins Be 3080 9e XaR.O-0. 8- Gem. 2 18 8 Panne DB906 ORO ummm (eas oopa. palin Slat ae slur na mare cet we (soup. putin" aloiatl ae sland ant ate cee we Figura 7.13 Apelul functiei VERIFICA_SALARIUL 2. Sai se realizeze o functie pentru verificarea si validarea codului numeric personal (CNP): CREATE OR REPLACE FUNCTION CC_CNP (pcnp varchar2) RETURN VARCHAR2 Is ponderi VARCHAR2(12) := '279146358279'; i NUMBER (2) ; ‘s NUMBER(4) ; mod_11 NUMBER (2) ; cc VARCHAR2 (1) ; mesaj VARCHAR2 (200) ; BEGIN ibprograme PL/SQL us a Baze de date. Limbajul PL/SQI, Saberee Funetia va primi drept parametri valoarea la care se aplica taxa si procentul IF length(penp) != 13 THEN 7 : taxei respective. mesaj "CNP nu are 13 caractere'; CREATE OR REPLACE FUNCTION taxa (value IN cote NUMBER, proc IN NUMBER) _ RETURN NUMBER IS FOR i IN 1..12 LOOP oat — 7 RETURN (value*proc) ; IF substr(penp,i,1) IN END taxa; CON, 114, 124,139, 141,951,161, 171, 181, 191) THEN 7 8 i= 8 + to_number(substr(ponderi,i,1)) * show errors to_number (substr(penp,i,1)) ; ELSE Apelul functiei se va realiza intr-o interogare care afigeaz id_ul unui mesaj := 'CNP ne numeric!; produs, cantitatea, pretul si TVA-ul acestuia. RETURN mesaj; END IF; SELECT id_produs, cantitate, pret, taxa (pret, END; 0.19) as tva END LOOP; FROM rand_comenzi; mod_11 := mod(s,11); 08 = IF mod_11 = 10 THEN cc := '1'; SE Bor Orme, On sy Yowenne Waste Eee ELSE Gaga er xen o-e- 8. Bom 3 OR cece := to_char(mod_11) ; aay . END IF; IF ce = substr(penp,13,1) then mesaj ‘CNP Corect'; ELSE mesaj 'Cifra de control eronata (CNP eronat) '; END IF; = END; > END IF; ° RETURN mesaj; a END cc_enp; ae / see Utilizarea functiilor PL/SQL in SQL — se realizeazd asemandtor cu apelul JSunctiilor predefinite Oracle. Exemplu: Seer ib SE abs Sai g = Sd se creeze o functie PL/SQL pentru calculul taxelor. Figura 7.14 Apelul functiei TAXA intr-o interogare 126 Baze de date. Limbajul PL/SQL, Aceste functii pot fi utilizate in toate clauzele SELECT sau in comenzi de manipulare a datelor, spre exemplu: : SELECT id_produs, cantitate, pret, taxa (pret, 0.19) FROM rand_comenzi WHERE taxa(pret, 0.19)>(select avg (taxa (pret, 0.19)) from rand_comenzi) ORDER BY taxa(pret, 0.19) DESC; Observatii! 1. Funcfiile utilizate in expresii SQL trebuie s& accepte doar parametri de tip IN $i sa returneze numai tipuri de date specifice SQL. 2. Funotiile apelate in cadrul expresiilor SQL pot fi continute in pachete de subprograme, 3. Punctiile nu trebuie sa contin comenzi DML (update, delete, insert) sau comenzi DDL sau pentru controlul tranzacfiilor (commit, rollback) si nici mu trebuie sa apeleze alte subprograme care sa incalce aceste restrictii. in cazul in care aceste conditii nu sunt respectate se va genera 0 eroare, asa cum se poate observa din exemplul urmator; Exemplu: Apelul urmatoarei functii dintr-o instructiune SQL va returna croarea: ORA-04091: table {nume_tabela] is mutating, trigger/function may not see it din cauza faptului ca in cadrul functiei se deschide o tranzactie nefinalizata: CREATE OR REPLACE FUNCTION produs nou (pret_min NUMBER) RETURN NUMBER Is BEGIN INSERT INTO produse (id_produs, denumire_produs, pret_min)values (1, 'cafea', pret_min) ; RETURN (pret_min+100) ; END; / ‘Subprograme PL/SQL 127 In ciuda faptului ca functia se va crea fara probleme, putand a apelata intr- un bloc PL/SQL, apelul su in instructiuni SQL va genera eroare precizata: UPDATE produse SET pret_min= produs_nou (2000) WHERE id_produs=2245; + le (at Yew tigate fan Snte Varoing Mgon Toate tp BORE Fe XGR0-0-15- oor 3 1B. B Ponty Bay ane O ven ects Snes ee Figura 7.15 Apelul functiei PRODUS_NOU va genera o eroare Stergerea unei functii se realizeaz’ utilizind comanda: | DROP FUNCTION nume_functie; Vizualizarea tuturor functiilor din dictionarul metadatelor pentru utilizatorul curent se realizeaza prin comanda: Select object_name From user_objects Where object_type='FUNCTION'; ps Baze de date. Limbajul PL/SQL, Pentru a vizualiza codul sursa al functiei, de exemplu pentru functia TAXA: ; Select text From user_source Where name='TAXA' and type='FUNCTION' ORDER BY line; Exercifii propuse 1. Creati o functie care primeasc& drept parametru codul angajatului gi care sa returneze numirul de funcfii distincte (id_functie) pe care acesta le-a avut de-a lungul timpului. Testafi functia intr-un bloc anonim, 2. Parcurgeti printr-un cursor toti angajatii departamentului IT si pentru fiecare dintre acestia specificati numarul de functii distincte detimute in cadrul organizatiei (prin apelul functiei create la exercitiul 1), 3. Transformati functia creati la exercitiul 1 fntr-o proceduré cu parametri de tip IN, respectiv OUT. Apelati procedura intr-un bloc anonim. es : elaae PACHETE DE SUBPROGRAME Pachetele de subprograme grupeazi variabile, functii, proceduri, tipuri de date PL/SQL care, de obicei, sunt corelate logic, Pachetele sunt formate din 2 sectiuni: specificatia pachetului care este 0 zona publica in care sunt definitiile de variabile gi exceptii publice si corpul subprogramelor puclice si declarat pachetului care este 0 zona privata in care este implementat codul sursa al tuturor subprogramelor. In aceasti zon pot aparea si proceduri sau functii private, utilizate pentru diferite prelucrari interne ale pachetului. Specificatia pachetului se realizeazé utili¢and urmétoarea sintaxi: CREATE [OR REPLACE] PACKAGE nume_pachet Is|as --declaratii de variabile si tipuri publice --specificatii ale subprogramelor publice END [nume_pachet] ; Corpul pachetului se realizeazé utilizand urmdtoarea sintaxd: CREATE [OR REPLACE] PACKAGE BODY nume_pachet Is|as declaratii de variabile si tipuri private codul sursa al subprogramelor publice si private BEGIN --Este optional si se executa o singura data la primul apel END [nume_pachet] ; 130 Baze de date. Limbajul PL/SQy, Observatie: in cadrul pachetelor pentru a utiliza o functie/procedura jy cadrul unui alt subprogram din pachet, aceasta trebuie declarata inainte de utilizare (principiul forward declarations), Exemple: 1. Si se realizeze un pachet care sa confind o functie pentru cdutarea unui client, o functie pentru céutarea unui produs, 0 proceduré pentru introducerea unei comenzi noi, 0 proceduré de actualizare a unei comenti Si 0 functie pentry slergerea unei comenzi. Toate subprogramele vor fi publice. create or replace PACKAGE gestiune_comenzi Is function cauta_client (p_nume_client clienti.nume_client%type) return number; function cauta_produs (p_denumire »_produs Produse.denumire_produs*type) return number; Procedure introduce_comanda (p_nr_comanda comenzi.nr_comanda%type, p_id client comenzi.id clientstype, p_id_produs rand_comenzi.id_produsttype, P_pret rand_comenzi.pret%type, P_cantitate rand_comenzi. cantitate%type) ; Procedure actualizeaza_comanda (p_nr_comanda comenzi.nr_comanda%type, p_id_produs rand_comenzi.id_produsttype, Pcantitate rand_comenzi.cantitatestype) ; function sterge_comanda (p_nr_comanda comenzi .nr_comanda%type) return boolean; 31 Ppachete de subprograme comanda_inexistenta exception; END, / Show errors - a - cee (1 ‘race 0 Dee ananire lo alesee Ioee sone ROGERS 5% XE o-s- % Denice DEGAS BM SF omeermene tae cnn ae, ‘aie (Steves Kowtoon eve lijuamnslgeon cami Goma Figura 8.1 Crearea specificatiei pachetului GESTIUNE_COMENZI Pachetul este creat si stocat in baza de date, acest lucru poate fi vizualizat din meniul din stanga >Packages. / _ Corpul pachetului se va crea separat si va contine implementarea procedurilor si functiilor declarate in specificatie: create or replace / PACKAGE BODY gestiune_comenzi IS GAG 'Yrimeste ca parametru si returnea%a,id-ul acestuia DEPARTAMENTUL BIBLIOTECA /* functia caut. numele clientyfu! 132 Baze de date. Limbajul PL/SQL sau null in cazul in care nu exista clientul cautat sau sunt mai multi clienti cu acelasi nume */ function cauta_client (p_nume_client clienti.nume_client$type) return number is v_id_client clienti.id_clientstype; begin select id client into v_id client from clienti where nume_client =p nume client; return v_id client; exception when no_data_found then return null; when too_many rows then return null; end cauta_client; /* functia cauta_produs primeste ca parametru denumirea produsului si returneaza id-ul acestuia sau null in cazul in care nu exista Produsul cautat sau sunt mai multe produse cu aceeasi denumire */ function cauta_produs (p_denumire_produs Pproduse.denumire_produs*type) return number is v_id_produs produse.id_produs%type; begin select id produs into v_id_produs from produse where denumire_produs like P_denumire_produs| |'%'; return v_id produs; exception pachete de subprograme 133 when no_data_found then return null; when too_many rows then return null; end cauta_produs; /* procedura introduce_comanda adauga o comanda noua de tip online in tabela comenzi si tdetaliile acesteia - cantitatea si pretul in tabela rand_comenzi */ procedure introduce_comanda (p_nr_comanda comenzi.nr_comanda%type, p_id_client comenzi.id_client%type, p_id_produs rand_comenzi.id_produs%type, p_pret rand_comenzi.pret%type, p_cantitate rand_comenzi.cantitate%type) is begin insert into comenzi (nr_comanda, id_client, data, modalitate) values (p_nr_comanda, p_id_client, sysdate, ‘online'); insert into rand_comenzi (nr_comanda, id_produs, pret, cantitate) values (p_nr_comanda, p_id produs, p_pret, p_cantitate) ; exception when others then raise comanda_inexistenta; end introduce_comanda; /* procedura actualizeaza detaliile unei comenzi: cantitatea sau pretul*/ procedure actualizeaza_comanda (p_nr_comanda comenzi.nr_comanda%type, p_id_produs rand_comenzi.id_produs%type, p_cantitate rand_comenzi.cantitate%type) is 134 Baze de date. Limbajul PL/SQL begin update rand_comenzi set cantitate Pp_cantitate =p_id_produs and nr_comanda=p_nr_comanda; if sql®notfound then raise comanda_inexistenta; end if; end actualizeaza_comanda; where id_produ: /* functia realizeaza stergerea unei comenzi din tabelele comenzi si din rand_comenzi si returneaza true daca au fost sterse si false in caz contrar */ function sterge_comanda (p_mr_comanda comenzi.nr_comanda%type) return boolean is begin delete from rand_comenzi where nr_comanda = p_nr_comanda; delete from comenzi where nr_comanda = p_nr_comanda; if sqlfound then return true; else return false; raise comanda_inexistenta; end if; end sterge_comanda; BEGIN dbms_output .put_line('Se executa pachetul!') ; EXCEPTION when comanda_inexistenta then dbms_output .put_line ("Nu se poate procesa comanda cu acest numar! ye end gestiune_comenzi; 135 pachete de subprograme / Show errors A cparceenaa em ‘Gyeous cet os payee ‘ns_output. put_Line('Se execute pacheut!*); ee eS eee ee en ee Figura 8.2 Crearea corpului pachetului GESTIUNE_COMENZI Corpul pachetului se va stoca in baza de date, acest luera puta vizualizat din meniul din stinga->Packages: sub denumirea _pachetului ENZI apare si corpul acestuia, Ocal poate / ae din cadrul pachetului se poate face in sod aseminator cu cel al subprogramelor, cu mentiunea ci numele acestora va prefixat cu numele pachetului din care fac parte: declare v_id_client number; v_id_produs number; ee :=gestiune_comenzi.CAUTA_CLIENT (initca p('&nume_client')); if v_id_client is null then 136 Baze de date. Limbajul PL/SQI, dbms_output .put_line ('Nu exista acest client!') else v_id_produs:=gestiune_comenzi.cauta_produs('&deny mire') ; if v_id_produs is null then dbms_output .put_line ('Nu exista acest produs!'); else gestiune_comenzi . introduce_comanda (&nr_comanda, v_id_client, v_id produs, spret, gcantitate) ; end if; end if; end; / [Fora a Bema a Be Em Yew Riots in Source Versoning Migration Toole teip 7 G284 9¢ xOn 0-9-8. aa Boma Ce 13 amt oe Bay DEO06 Ue Oo nammmon nents CAT, CLIT sae eee}: | Suppor Line (Rs exate ace cies): [rtd peotuageotiune conn. caste puissant c!|2 ‘Wt td prods ta nal On | nosey tine (ms exits est peo}: ene nett ntotce, counts cnmte, vce, 654 pods, eet, somtstate); Figura 8.3 Apelul subprogramelor din cadrul Pachetului GESTIUNE_COMENZI Pachete de subprograme 137 Stergerea pachetului se realizeaz cu comenzile: DROP PACKAGE nume_pachet; DROP PACKAGE BODY nume_pachet; Vizualizarea specificatiilor pachetelor in dictionarul metadatelor poate face din meniul stanga->Packages sau direct prin comanda: Select text From user_source Where name='GESTIUNE_COMENZI' and type='PACKAGE' (Be Eat ew Rede in Seoee Venom Mom Inte Oy Gd8d 90'xG0 0-0: 5- Figura 8.4 Vizualizarea codului sursi al specificatiei pachetului GESTIUNE_COMENZI 138 Baze de date. Limbajul PL/SQL Pachete de subprograme —__139 function creste_salariul (nr_c number, id_a angajati.id_angajat%type) return angajati.salariul%type is Pentru a vizualiza corpul pachetului se utilizeazi comanda: Select text From user_source Where name='GESTIUNE_COMENZI' and type='PACKAGE eee var angajati.salariul%type; sal angajati.salariul%type; 2. Séi se creeze un pachet numit GESTIUNE_ANGAJATI care sa contind 0 begin Suncfie numité nr_comenzi, 0 functie crestere_sal si o proceduré numiti select salariul into sal modificare_sal. Scopul pachetul e de a modifica salariul tuturor angajatilor care from angajati au realizat comenzi. Regula de modificare a salariului e urmétoarea: 7 where id _angajat= id_a; + Intre 1 $i 3 de comenzi 0 majorare de 5% sal:=sal* (case + Intre 3 si 8 de comenzi 0 majorare de 10% when nr_c between 1 and 3 then 0.05 + Intre 8 $i 20 de comenzi 0 majorare de 15% when nr_c between 3 and 6 then 0.1 + Peste 20 de comenzi 0 majorare de 20% when nr_c between 8 and 20 then 0.15 Funetia nr_cerere va numéra comenzile fiectrui angajat, functia crestere else 0.2) va determina suma cu care va creste salariul fiectirui angajat in parte dupé regula end) ; prezentata, iar procedura modif. sal va realiza modificarea efectiva a salariului. return sal; Specificatia pachetului este: end creste_salariul; create or replace PACKAGE gestiune_angajati is function nr_comenzi (id_a angajati.id_angajat® function nr_comenzi(id_a TYPE) angajati.id_angajat%type) return number is v_nr NUMBER; return number; begin function creste_salariul(nr_c number, ida select count (nr_comanda) into v_nr angajati.id_angajat%type) ~ from comenzi return angajati.salariul%type; where id_angajat=id_a; Procedure modifica_salariul(id_a in return v_nr; angajati.id_angajat%type,pr in end nr_comenzi; angajati.salariulttype) ; end; procedure modifica_salariul (id_a in / angajati.id_angajat%type,pr in Show errors angajati.salariul%type) is sal Corpul pachetului: angajati.salariulttype; create or replace begin package body gestiune_angajati is select salariul into sal from angajati where id_angajat=id_a; update angajati set salariul-sal+pr

You might also like