Sunteți pe pagina 1din 97
Adela BARA luliana BOTHA ™ VET oy Neto) Thy.) lon LUNGU fe) Anda VELICANU Aa a LIMBAJUL aeRY I [A8e ‘area Li ier INTRODUCERE iN PL/SQL (PROCEDURAL LANGUAGE EXTENSION TO SQL) 11 Introducere PL/SQL este un limbaj procedural structurat pe bloc, programele putand fi impirtite 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 modularizati, 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 baza de date Oracle si de un instrument de dezvoltare prin care sa interactionam 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 instanta 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 bazeaza exemplele din aceasta carte se poate vedea in figura 1. Scriptul pentru crearea acestor tabele este redat in Anexa 1 si se poate descarea de pe siteul http://bd.ase.ro impreuna cu scriptul pentru adaugarea inregistrarilor. Baze de date. Limbajul PL/SQL 10 aydwiaxe uy wyeziyN ajep op fezeq BUIIYIg=] BANSI ~ = FSleeeuyouva aNnIORY-3NnIN0 ‘UZOMON —ANNIDSE at att T wenn aynvosta: ta fp lovwyvOuva veel SHIMONSO Oa “tahuvies "vue atf mL “are Tiemann, (ohuaanee Ca (W)uganNN ANaNWLEN20-O OD (onuwiauva auanna-al GB fp eusanow Introducere in PL/SQL (Procedural Language Extension to SQL) uu 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: declarativd (optionala), executabila (obligatorie) si de tratare a exceptiilor (optionald). Structura unui bloc PL/SQL poate fi prezentata astfel: DECLARE (Optional) variabile, cursori, exceptii BEGIN (Obligatoriu) comenzi SQL (asigura accesul la baza de date) structuri de programare procedural PL/SQL EXCEPTION (Optional) actiuni ce se executd cand apare o eroare END; (Obligatoriu) in cadrul blocului pot apiirea instructiuni SQL care asigura accesul la baza de date, de exemplu pentru efectuarea unor actualizari, dar operatiile efectuate cu variabilele PL/SQL in cadrul instructiunilor procedurale nu presupun accesarea bazei de date. Observatii: + se foloseste (;) dupa fiecare instructiune SQL sau instructiune de control PL/SQL; + 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 o singura data (cazul blocurilor anonime), pot fi stocate in baza de date pentru a fi apelate ulterior (cazul functiilor, procedurilor si pachetelor stocate) sau pot fi realizate si apelate la nivelul programelor de aplicatii (functii, proceduri, triggeri de aplicatii). in paragrafele urmatoare ne vom referi pe scurt la fiecare dintre aceste tipuri de blocuri, urmand sa le detaliem in capitole separate. 12 Baze de date. Limbajul PL/SQL Blocurile anonime prezintaé urmatoarele caracteristici: * nu sunt stocate in baza de date; « se declara inline, in locul in care se doreste executia lor; © se executa in momentul rularii; Daca doriti sa reutilizati un bloc anonim va sfatuim s& salvati codul sursa pe hard disk intr-un fisier de tip .sql sau .txt. La modul general un bloc PL/SQL anonim poate avea urmétoarea formi: pentru DECLARE -- declararea variabilelor se face precizand tipul de date si realizandu-se eventualele initializari; -- de exemplu: v_variabila tip_de_date; BEGIN -- instructiuni executabile; EXCEPTION WHEN exceptie THEN actiune; END; ih Blocurile PL/SQL se pot imbrica si se pot eticheta cu. <> a putea fi ulterior accesate variabilele, adresare lor find realizata astfel: eticheta_bloc.variabila. DECLARE << eticheta_bloc >> DECLARE BEGIN END eticheta_bloc; END; Introducere in PL/SQL (Procedural Language Extension to SQL) 13 Proceduri si functii Sunt blocuri PL/SQL care au nume gi 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 si tipul acestora), de exemplu: CREATE [OR REPLACE] PROCEDURE nume _procedura (lista parametri) [EXCEPTION] CREATE [OR REPLACE] FUNCTION nume_functie (lista parametri) RETURN tip_data Is RETURN valoare 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 aplicat (de exemplu: deplasarea mouse-ului, apasarea unui buton) si lansate in executie automat la aparitia acestor evenimente. 1.3 Utilizarea operatorilor si functiilor in PL/SQL Operatorii utilizati In cadrul blocurilor PL/SQL se pot utiliza aceiasi operatori ca si in cazul limbajului SQL. In plus fata de acestia apare operatorul de atribuire (:=). Operator Caracteristici +,-, *, /, ** (op. Operatori aritmetici exponential) AND, OR, NOT Operatori logici <>, =) >=) <=, SF Operatori de comparatie BETWEEN ... AND ... Operator de verificare a apartenentei la un interval de valori IN(lista) Operator de verificare a apartenentei la o lista de valori LIKE Operator de comparare cu un sablon % - oricate caractere; _ - un caracter; IS NULL Operator care verificd daca o variabila are valoarea NULL : ll Operator de concatenare @ Operator de conectare la distant & Operatori pentru adresarea variabilelor de substitutie sau && Operator de atribuire Funcfii SQL suportate in PL/SQL {fn cadrul instructiunilor descriptive sunt suportate toate tipurile de functii SQL (inclusiv functiile de grup in cadrul instructiunii SELECT). Instructiunile PL/SQL suporta functii la nivel de inregistrare (single-row): numerice, caracter, data, de conversie etc., dar NU suporta functii de grup (SUM, MIN, MAX, AVG, COUNT, STDDEV) sau functia DECODE decat in cadrul Introducere in PL/SQL (Procedural Language Extension to SQL) 15 instructiunilor SQL din bloc. De exemplu, nu se pot utiliza intr-un bloc PL/SQL constructii de forma: IF DECODE(...) THEN ... sau IF AVG(...) THEN ... . Limbajul PL/SQL converteste tipurile de date dinamic (de exemplu: 0 valoare numerica la o variabila char), in acest sens avand loc conversii implicite: caracter <-> numeric si caracter <->data. Pentru conversii explicite se utilizeaza functiile TO_DATE, TO_NUMBER, TO_CHAR in mod asemanitor cu limbajul SQL. 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 si- 1 afiseaza. Diferenta dintre cele doua functii este aceea c4 prima afiseazi mesajul dupa care trece la linia urmitoare, iar cea de a doua afiseazi mesajul pe linia curenta. Functiile PUT_LINE si PUT sunt necesare deoarece nu putem accesa variabilele declarate in blocurile PL/SQL direct din mediul SQL (pentru a le afisa 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 afigare. SET SERVEROUTPUT ON DBMS_OUTPUT.PUT_LINE (’ DBMS_OUTPUT.PUT (' END; 5 “ De exemplu, vom realiza un bloc PL/SQL care sa afigeze un mesaj. Pentru a executa blocul utilizim mediul de dezvoltare SQL Developer. Mai intai este necesara stabilirea conexiunii cu serverul Oracle: click dreapta pe eticheta Connections din staénga ecranului si completam detaliile referitoare la conexiune: nume utilizator si parola, adresa IP a serverului si portul, numele instantei bazei de date. 16 Baze de date. Limbajul PL/SQL Figura 1 Realizarea conexiunii cu serverul Oracle Dupa conectare apare fereastra in care putem scrie si executa blocurile, in mod asem&nitor cu modul de lucru cu instructiunile SQL. Pentru executia blocurilor se di click pe unul dintre butoanele Execute Statement (F9) sau Run Script (F5). Introducere in PL/SQL (Procedural Language Extension to SQL) 17 ‘Be Eat ew Heiode Bin Sopce Verngnng Marion oa toe oma. a IB [nuns over. ror. nM (tisha PL/SOL este uy Liabe) peoveduel!'}2 ‘ms_OOTOT.FOTLIME( ‘Bs Bice /SOE cntise eek seins: DECLARATIVE, DECUTABLA, DE TATARE A teas sow oes Ween tne [Gros cuna| Chora joan escxre incecoietsa abe ath cre nb) puoctea! f fn blce Myatt sontine seen sechins DECLAMTNA, IECOANLA, BE TATISE A BEEPTILER Figura 2 Executia blocurilor PL/SQL in SQL Developer Codul sursa al blocului este redat mai jos: SET SERVEROUTPUT ON --INTRODUCEM COMENTARII: in acest caz sectiunea DECLARE lipseste --blocul afigeaz& un mesaj 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; / 18 Baze de date. Limbajul PL/SQL 2 ‘q _—— Exercitii propuse 1. Recapitulati notiunile de baza ale limbajului SQL, precum si comenzile utilizate. 2. Creati o conexiune noua in SQL Developer si testati programul clasic Hello World! VARIABILE PL/SQL IL.1 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 data si optional anumite restrictii si un sir valid de valori. Declararea gi 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 precizeazi restrictia NOT NULL trebuie obligatoriu initializate, iar ulterior nu vor putea primi valoarea NULL. Pentru a putea urmari mai usor codul sursd se foloseste urmatoarea conventie de notare: ¢_nume Constanta - pentru declararea constantelor; v_nume Variabila - pentru declararea variabilelor; g_nume Variabila_globala — pentru variabilele globale definite in zona de specificatii a unui pachet de subprograme si valabile pentru toate procedurile si functiile pachetului. 20 Baze de date. Limbajul PL/SQL 1.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) 1.2.1 Variabile scalare Tipurile scalare contin valori simple, o variabila scalara poate contine la un moment dat o singura valoare si corespunde in principal tipurilor de date Oracle. Cele mai cunoscute gi utilizate tipuri de date sunt prezentate in tabelul urmator: = char (lung_max) - lungime fixd de max 32.767 bytes = varchar2 (lung_max) — lungime variabil4 de max 32.767 bytes = long [sir de caractere de lungime variabila 2GB] = number (precizie,scala) = boolean (true, false, null) = date = binary_integer si pls_integer (numere intregi intre - 2147483647 si 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; ¢_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 afisa 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, ins& 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 anumita inregistrare sau valoare a unei coloane dintr-o tabeld este obligatoriu sa utilizim variabile PL/SQL, asa cum se observa si in exemplul urmator: Exemplu: Sa 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; if Rezultatele executiei blocului sunt prezentate in figura 2.1 22 Baze de date. Limbajul PL/SQL Boag 9° xan aigie oo aay i 2 caretre es S Qa [Pete vcisnin wpe in cara wen mst male eect 2 waa |emme Vann (20); £Gce com faa 7 Pprer Gime & room |S pose dra Eipreciee tome | SER [nee lions iaaeews Geos onl Gowan | 8 Bim 1B 68S | ane seezan _] ha | Geen SE ae fiom accor eet Figura 2.1 Executia blocului PL/SQL 1. Atributul %TYPE Pentru a declara o variabila PL/SQL care sa aiba acelasi tip de date cu o anumita coloana dintr-o tabeld sau cu o alti variabild declarata anterior putem folosi atributul %TYPE. Declararea unei variabile cu % TYPE se face astfel: variabila _ tabel.nume_coloani%TYPE; sau yariabilal tip_data; variabila2 variabilal%TYPE; Variabile PL/SQL 23 Exemple: Sai se afiseze numele si prenumele angajatului cu codul 100: DECLARE --declaram doua variabile pentru nume si prenume: v_nume angajati.nume%TYPE; v_prenume angajati.prenume%TYPE; BEGIN -- populam gi 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; 7 Observatie: Restrictia NOT NULL a unei coloane nu se aplica si variabilei declarate prin folosirea atributului %TYPE. 2. Variabile de mediu sau variabile de legituré ale aplicatiilor gazda (BIND VARIABLES) Acestea sunt variabile de legituri cu aplicatia in care ruleazi motorul PL/SQL si trebuie declarate in aplicatie (in mediul gazda), putand fi accesate si modificate in cadrul blocurilor PL/SQL. Dupa terminarea executiei blocului PL/SQL, variabila ramAne in mediul gazda cu valoarea primita in urma rularii blocului gi poate fi pasata altui bloc, realizand 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_numevariabila TIP Observatie: pentru declararea unei variabile de mediu de tip NUMBER nu se specifica precizia si scala; 24 Baze de date. Limbajul PL/SQL Variabilele de mediu se utilizeaz in cadrul unui bloc PL/SQL sau intr-o instructiune SQL din afara blocului prefixate cu (:) astfel: thost_variabila:=v_variabila; Variabilele de mediu se afigeaz in afara blocului cu ajutorul comenzii PRINT (la afisare variabila nu se va prefixa cu “:”): PRINT g_numevariabili Exemple: 1. Sa se afiseze mesajul: Bloc PL/SQL cu ajutorul unei variabile de mediu: --declaram variabila de mediu: VARIABLE g_mesaj varchar2 (30) BEGIN -- atribuim mesajul variabilei :g_mesaj:='Bloc PL/SQL'; END; / -- afisam valoarea variabilei in afara blocului: PRINT g_mesaj 2. Sai se afigeze numdrul 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 = 'online'; END; 7 -- afisam variabila: PRINT g_comenzi Variabile PL/SQL 25 Observatie: Se poate auto-afisa variabila prin setarea AUTOPRINT ON Exemplu: Sa se selecteze produsele si preful acestora pentru acele produse care au pretul < preful mediu al produsului cu codul 3133 fard a utiliza 0 cerere imbricata: 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; Z --utilizam variabila de mediu intr-o interogare SQL: select * from rand_comenzi where pret< :g pret; F Cradle $0. Developer Tn 7 ee ee ee ee ee a BeBe 9* 4Gb ee 5- e @ 2s 2 Pace Sa gear PEDRO Ue GO tmnmnne Sy 5 Di comectere Sa Set Lean aad aan eas ak eee aa | ‘gam ore a BS | SAT ¥ gc Te ir ate ee ed ‘trom rend_comennt } ‘ao { Si j 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 legitura (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 0 consulta). 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. fn mediul SQL variabilele de substitutie pot fi usor utilizate prin introducerea de valori de la tastatura utilizind ACCEPT, se pot defini cu DEFINE sau afisa pe ecran cu PROMPT; Exemplu: Sa se afiseze numdrul de comenzi ale angajatului al cdrui cod este introdus de la tastaturd 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 ao loctst cous tac found ine ve sonnet Bren conned Figura 2.3 Executia blocului PL/SQL intr-un bloc PL/SQL se pot utiliza toate tipurile de variabile, respectnd ins& caracteristicile si regulile de utilizare ale acestora. in exemplul urmitor se utilizeaza atat variabila de substitutie s_nume definita si initializata prin comanda DEFINE, cat gi variabila de legatura g_salariul, dar si variabila locala v_prenume de acelasi tip cu coloana nume din tabela Angajati. Variabila de substitutie definita cu DEFINE va fi implicit de tipul CHAR. Exemplu: Sd 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.numestype; BEGIN select prenume,salariul into v_prenume, :g_salariul from angajati where nume='&s_nume'; 28 Baze de date. Limbajul PL/SQL DBMS_OUTPUT.PUT_LINE ('Prenumele angajatului este: '||v_prenume) ; END; if print g_salariul 41.2.2 Tipuri de date compuse 1. Tipul RECORD Reprezinta 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 declara un PL/SQL record pentru aceste campuri, ele pot fi manipulate ca o unitate, fiecare camp (clement al structurii) avand un nume si un tip de data. Atributele unui record sunt referite astfel: nume_record.nume_camp. Declarea unui tip record se face astfel: TYPE nume_record IS RECORD (nume_cimp TIP_DATA [,nume_cimp TIP_DATA:=|DEFAULT valoare]...); Tar declararea unei variabile de acest tip: Variabila NUME_RECORD; Exemplu: Utilizand un tip de data record definit de utilizator sa se afigeze pretul minim al produsului cu codul 3133. DECLARE TYPE tip_produse IS RECORD (v_cod produse.id produs%type NOT NULL:=3000, v_den produse.denumire_produs%type, v_pret_min produse.pret_min%type) ; vrec_prod tip_produse; BEGIN SELECT id_produs, denumire_produs, pret_min into vrec_prod from produse where id_produs=3133; dbms_output.put_line('Produsul: '|| vrec_prod.v_den|| ' are pretul minim: '| |vrec_prod.v_pret_min) ; END; i Variabile PL/SQL 29 ‘le EO ew Mpue fin Sauce Vergnng rion Joo tp Boua 9° xae 0-6 48- aa eee 335 itt SSURCY S4_promu, decimice prod, poet Ain Sate wees prod fiom produne were $4 pr0aiee3139¢ (ns curt: put ine Peosnals "| wrec eae deal] "ae pectal nina "Ivete prod pee ain)? mar He i [Drenas (so Oana, tein [Enasurse[Roews cup | Chana came tl i = Eten ore i Figura 2.4 Executia blocului PL/SQL Pentru a defini un record pe baza coloanelor unei tabele se foloseste Yorowtype . In acest caz numele elementelor din record au acelasi nume ca si coloanele tabelei, acelasi tip de date gi se gasesc in aceeasi ordine. NUME_RECORD tabela%ROWTYPE; Exemplu: Sd se rescrie exemplul de mai sus utilizand atributul YROWTYPE:. DECLARE vrec_prod produse%rowtype; BEGIN 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; / Baze de date. Limbajul PL/SQL EXERCITII PROPUSE 1. Specificati ce se va afisa la rularea urmatorului bloc PL/SQL: DECLARE v_varl NUMBER :=100; v_var2 NUMBER; v_var3 NUMBER := v_var2; v_var4 VARCHAR (20) 'variabila PL/SQL'; v_var5 NUMBER NOT NULL := v_varl; ¢_const1 CONSTANT DATE TO_DATE('12/02/2007', 'dd/mm/yyyy') ; ¢_const2 CONSTANT NUMBER NOT NULL : ¢_const3 CONSTANT NUMBER := NULL; v_var6 NUMBER DEFAULT NULL; BEGIN DBMS_OUTPUT. PUT_LINE('variabila 1 = '||v_var1); DBMS_OUTPUT. PUT_LINE('variabila 2 = '||v_var2); DBMS_OUTPUT.PUT_LINE('variabila 3 '| |v_var3); DBMS_OUTPUT.PUT_LINE('variabila 4 = '||v_var4); DBMS_OUTPUT.PUT_LINE('variabila 5 = '||v_var5); DBMS_OUTPUT.PUT_LINE('constanta 1 = "| |e_const1) ; DBMS_OUTPUT.PUT_LINE('constanta 2 = '| |e_const2) ; DBMS_OUTPUT.PUT_LINE('constanta 3 = "| |c_const3) ; DBMS_OUTPUT.PUT_LINE('variabila 6 = '||v_var6); END; 7 Variabile PL/SQL 31 2. Specificati ce se va afisa la rularea urmatorului bloc PL/SQL (care contine blocuri imbricate, ilustrand domeniul de vizibilitate al unor variabile care au acelasi nume): DECLARE var NUMBER; BEGIN var := 1; DBMS_OUTPUT. PUT_LINE (var) ; <> DECLARE var NUMBER; BEGIN var :=2; DBMS_OUTPUT. PUT_LINE (var) ; END bloc1; DBMS_OUTPUT . PUT_LINE (var) ; <> DECLARE var NUMBER; BEGIN var :=3; DBMS_OUTPUT. PUT_LINE (var) ; <> DECLARE var NUMBER; BEGIN var i: DBMS_OUTPUT. PUT_LINE (var) ; DBMS_OUTPUT. PUT_LINE (bloc2.var) ; FOE STU \_LINE (var) ; DEPARTAMENTU:. BIBLIOTECA 2 32 Baze de date. Limbajul PL/SQL DBMS_OUTPUT. PUT_LINE (var) ; END; i 3. Specificati ce se va afisa la rularea urmatorului bloc PL/SQL: 4, 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+1; mesa} 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) ; END; / Sa se calculeze suma a doua numere, iar rezultatul si se dividi cu 3. Numerele se vor introduce de la tastatura: 5. Sa se afiseze salariul marit cu un procent. Salariul si procentul se introduc de la tastatura. i i ae INTERACTIUNEA CU SERVERUL ORACLE PRIN COMENZI SQL Interactiunea se realizeazd 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 DML/TPL Executie SELECT, INSERT, UPDATE, Se executii normal in cadrul blocului DELETE, MERGE COMMIT, ROLLBACK, Pot apirea in bloc dar au efect asupra SAVEPOINT tuturor tranzactiilor din _interiorul sidin afara acestuia. © PL/SQL nu suporté comenzi de definire a datelor sau de gestiune a utilizatorilor (DDL sau DCL) in cadrul unui bloc. Pentru executarea acestor comenzi se utilizeazi comanda EXECUTE IMMEDIATE ‘comanda DDL’: Comenzi DDL/DCL Executie CREATE, ALTER, DROP EXECUTE IMMEDIATE ' CREATE GRANT, REVOKE TABLE.... ' Exemple: 1. S@ 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 Figura 3.1 Executia blocului PL/SQL 2. S& se adauge in tabela PROD inregistrari din tabela PRODUSE: DECLARE v_codp produse.id_produs%type; v_denp produse.denumire_produs%type; v_cat produse.categorie%type; v_des produse.descriere%ttype; BEGIN --populam variabilele: SELECT id_produs, denumire_produs, categorie, descriere INTO v_codp, v_denp, v_cat, v_des FROM produse where id_produs=3133; --adaugam valorile variabilelor in tabela: INSERT INTO prod (id _produs, denumire_produs, categorie, descriere) VALUES (v_codp, v_denp, v_cat, v_des); --afisam valoarea variabilelor: DBMS_OUTPUT.PUT_LINE ('S-a adaugat in tabela prod produsul: '||v_codp||' '||v_denp||' "| |vlcat); end; / select * from prod; Interactiunea cu serverul Oracle prin comenzi SQL. 38 Em wer Howe bm Somes verneng Mowe iow Bow (Ua sdouot Sntabele od produ: S11 emlo11 “He Omit? nea) See ccom Figura 3.2 Executia blocului PL/SQL Pentru o mai mare flexibilitate se poate declara o variabila de tip sir de caractere care si primeasca comanda de definire a datelor care va fi executata prin Execute Immediate: Exemple: 1. Sa se creeze tabela EMP_SAL prin intermediul unei variabile de tip VARCHAR2. La creare, in tabela EMP_SAL se va adéuga o noud inregistrare. SET SERVEROUTPUT ON VARIABLE G_EID NUMBER DECLARE V_SIR VARCHAR2 (200) ; BEGIN :G@ EID:=110; --atribuim girul de caractere variabilei V_SIR:='CREATE table emp_sal AS SELECT id_angajat, nume, prenume, salariul FROM angajati where id_angajat='||:G_EID; DBMS_OUTPUT.PUT_LINE (V_SIR) ; 36 Baze de date. Limbajul PL/SQL --utilizam comanda cu valoarea variabilei v_sir: EXECUTE IMMEDIATE V_SIR; END; Z select * from emp_sal; 2. Sé se adauge 0 noud coloand - STOC in tabela PRODUSE: DECLARE V_SIR VARCHAR2 (200) ; BEGIN V_SIR (MG DBMS_OUTPUT.PUT_LINE (V_SIR) ; EXECUTE IMMEDIATE V_SIR; END; / select * from PRODUSE; ALTER TABLE PRODUSE ADD (STOC NUMBER Figura 3.3 Executia blocului PL/SQL Interactiunea cu serverul Oracle prin comenzi SQL 37 Manipularea datelor in PL/SQL se face prin limbajul de manipulare a datelor (comenzile INSERT, UPDATE, DELETE) care pot fi lansate fir restrictii in PL/SQL. Exemple: Comanda INSERT: 1. Si se adauge 0 noud inregistrare in tabela EMP_SAL: BEGIN : INSERT INTO EMP_SAL (id_angajat, nume, prenume, salariul) VALUES (200, 'Pop', 'Marian', 7500); END; / select * from emp_sal; 2. Sa se adauge o noud inregistrare in tabela PRODUSE prin introducerea valorilor cu ajutorul variabilelor de substitutie: BEGIN INSERT INTO produse (id_produs, denumire_produs, categorie, stoc) VALUES (&id, '&denumire', '&categorie', &stoc) ; END; / select * from produse; 3. Sa se adauge clientul Ionescu George. Datele personale (telefon, nivelul veniturilor si emailul) se vor citi de la tastaturd. Id-ul acestuia va avea valoarea ultimului id de client incrementat cu o unitate. set serveroutput on declare v_id clienti.id_client% TYPE; begin select max(id_client) into v_id from clienti; insert into clienti values (v_id+1, '&prenume', '&rume', &tel, NULL, '&email',NULL, NULL, NULL, &venit); x 38 Baze de date. Limbajul PL/SQL Comanda UPDATE: 4. Sé se mareascii cu un procent salariul angajafilor din tabela EMP_SAL care au in prezent salariul mai mic decat valoare variabilei v_prag: DECLARE v_procent number:=0.1; v_prag angajati.salariulttype:=10000; BEGIN UPDATE emp_sal SET salariul=salariul* (1+v_procent) WHERE salariul=50; 48 Baze de date. Limbajul PL/SQL ari eS (le EA Mew tie Bn Soros Versnng Moron Ime tip Boga 9e ROB O-e- 48- 2 cueeeenn’! ia: sere 4 sevaceamentts | Ti sree sept epaceanant|| Gee amines cet |inzeeaepesenumsee € Tete arr inn ‘Shama vo a ——- Sno = pe fu se ban Bsserne | ‘sommes | [Scpremereals 50 ars aromisne nt Sipe | i lefranetmmeite Figura 4.3 Executia blocului PL/SQL Structura WHILE.....LOOP....END LOOP are urmdtoarea structura 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) : while v_nr > 0 loop v_nr:=v_nr-i; i dbms_output .put_line(v_nr) ; end loop; END; / Structuri fundamentale de programare 49 2. Si se afiseze in ordine angajafii cu codurile in intervalul 100-110 atat timp cdt salariul acestora este mai mic decdt salariul mediu: DECLARE v_sal angajati.salariulttype; v_salMediu v_salttype; 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=i; dbms_output .put_line('Salariatul cu codul '||a] |" are salariul: '||v_sal); ist; iesim din ciclul WHILE la indeplinirea conditiilor exit when v_sal> LOOP v_var:=v_var+l; 2 Baze de date. Limbajul PL/SQL EXIT WHEN v_var>10; <> LOOP EXIT LOOP_EXTERN WHEN cond1; EXIT WHEN cond2; ~ Exercitii propuse 1. Utilizand structuri alternative, rezolvati ecuatia de gradul 2. 2. Afisati numele si telefonul aferente fiecdrui client cu id-ul numar par. 3. Construifi un bloc anonim pentru a exemplifica structurile LOOP imbricate. CURSORI SI TABELE INDEX BY Cursorul reprezinta o variabila PL/SQL speciala utilizata atunci cand se executi 0 comanda SQL, iar Oracle Server deschide 0 zond de memorie (context area) in care comanda este executata. Cursorul este un pointer catre aceasta zona. in PL/SQL se utilizeazd dowd 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 returneazi 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 intr-un bloc PL/SQL. Daca o instructiune DML nu afecteaza nicio linie a tabelei nu se genereazi eroare, ins 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: vy SQL%ROWCOUNT- reprezinté numiarul liniilor retumnate pana in momentul curent; ¥ 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. Sa se steargd un produs din tabela PRODUSE $i si se contorizeze numarul de rénduri sterse. SET SERVEROUTPUT ON DECLARE v_rez NUMBER (2) ; BEGIN DELETE FROM produse WHERE id_produs=111; v_rez:=SQL%ROWCOUNT; 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 exista (comanda update nu afecteaza nicio inregistrare) va fi afigat un mesaj corespunzator. 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 [Grate SqLDeveoper oracle Be Om Mow Hotere Saye Youve Mowin Tom Be Q00@ 9 XGR O-o: 8- Qos ©) Deets _[Drenacte ton | aay PBSAS BB F sero 5 Bhometine (ows Samet Bcc FB owe WERTE produze © operat ‘SET decumize_produss'cefea’ Senn ‘sem apna Bm 2 saan en = fh once ‘ORS_OUTPUT, YOT_LINE( "Ru exisva produsul cu acest ced!) Grew 2 Bivens 3 aiden 2 Brecons (2 LB rrocedures GG rinctons # Bowes ne — — ieee emer as Roce igeomtnns| Sons ome) Soracma Figura 5.1 Execufia blocului PL/SQL 3. Sa se steargd din tabela EMP_SAL salariatul al carui 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; :mr_sters:=TO_CHAR (SQL%ROWCOUNT) | |' INREGISTRARI STERSE!; END; / PRINT nr_sters Rollback; 56 Baze de date. Limbajul PL/SQL ’ cr lo EM New tigate fn Soyiee VorekroNs Moreton Tone Bap Gnaa ee xan o-6. 4. fe @ 21D QDewny Dramnrs Se agar PUSGO BB = Bemeans a egaem Er aed TE on fondo eosin iy 9 armen zon Qe DELETE Pu en GORE 4 sngeparas ets | i ogc See ‘praeseun sees | i ta | ca am ai 4 i i Figura 5.2 Executia blocului PL/SQL V.2. Cursorul explicit Cursorul explicit se utilizeaz’ pentru a procesa individual fiecare linie (inregistrare) returnaté de o instructiume SELECT ce returneazi mai multe inregistrari. Multimea inregistrarilor retunate de o cerere este numiti multime rezultat, Cursorul pastreaz un pointer c&tre linia curenté in cadrul unei multimi rezultat. Verificarea starii unui cursor explicit se realizeazi prin intermediul urméatoarelor atribute: ¥ nume_cursor%ISOPEN - evaluat la TRUE in cazul in care cursorul este deschis; v nume_cursor %NOTFOUND - evaluat la TRUE in cazul in care cel mai recenta citire a valorilor din cursor nu a returnat nicio linie; ¥ nume_cursor %FOUND - opusul lui %NOTFOUND; v nume_cursor %ROWCOUNT - are ca valoare numarul liniilor retunate pana in momentul curent. Cursori si tabele index BY 37 Prelucrarea cursorului explicit presupune parcurgerea urmatoarelor etape: © se declara variabilele in care vor fi incarcate valorile corespunzatoare unei linii din cursor; * se declari cursorul explicit, specificandu-se un nume pentru acesta si definindu-se interogarea de procesat in cadrul lui: nume_cursor IS SELECT... * se deschide cursorul prin intermediul instructiunii OPEN, care executa interogarea si legarea tuturor variabilelor referite. fnregistrarile returnate de interogare sunt desemnate drept set activ de date, care pot fi de acum incarcate. OPEN nume_cursor; * — utilizindu-se instructiunea FETCH, se citeste linia curenta din cursor si se attibuie valorile variabilor. Fiecare incdrcare determina mutarea pointerului cursorului a linia urmitoare din setul activ de date. FETCH nume_cursor INTO varl, var2yucsee} © este inchis cursorul prin instructiunea CLOSE, care dezafecteaza setul activ de lini, Cursorul poate fi din nou deschis pentru a stabili un nou set activ de linii. CLOSE nume_cursor; Pentru a procesa liniile unui cursor explicit se utilizeaz’ de obicei o structura 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 comandi FETCH executaté fara succes pozitioneazd atributul °%NOTFOUND pe TRUE. fhaintea 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 niciodati 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.nume%type; 58 Baze de date. Limbajul PL/SQL ang_sal angajati.salariul%type; 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; / |b Oracle Sl Devloperieracei te ie fst ow niode Gin Saye Versing Ripwion Took ip Reba 9e XODO-G 5- Figura 5.3 Execufia blocului PL/SQL Cursori si tabele index BY 59 Pentru © flexibilitate mai mare se poate utiliza o variabilé de tip record pentru incrcarea valorilor din cursor. Aceasti variabila de tip record poate avea aceleasi atribute ca si cursorul prin specificarea proprietitii %ROWTYPE. in acest caz increarea 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 cursor%rowtype; begin dbms_output.put_line('Lista 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 '| |ang_rec.nume||' are salariul: '| |ang_rec.salariul) ; end loop; close ang_cursor; end; i 2. Si se incarce in tabela T_ANG primi 5 angajati (id si nume) -- se creeazi tabela mesaje CREATE TABLE T_ANG (cod varchar2(7), nume varchar2(20)) ; DECLARE v_id angajati.id_angajatttype; 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; [[E rate Sat Developer: rate p (e Eat Mew nite Bin Soyce Werognng Korion Tool tap 2388 9% 20 0-9: 3- — = oOo C>tents[lSottcune '3ebin| tare ous on Bim eaa PGiooren |cxtare Te mecca 2. Figura 5.4 Executia blocului PL/SQL Cursori si tabele index BY 61 Testul de iesire din ciclul LOOP in acest caz se poate face gi cu ajutorul atributului %ROWCOUNT: --se sterg inregistrdrile din tabela T_ANG: Delete from T_ANG; DECLARE v_id angajati.id_angajat%type; v_nume angajati.nume%type; 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; iC SELECT * FROM T_ANG; 3. Sa se afigeze primele 3 comenzi care au cele mai multe produse comandate. in acest caz inregistrarile vor fi ordonate descrescdtor in functie de numdrul 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; rec_com c_com’rowtype; BEGIN DBMS_OUTPUT.PUT_LINE('Numarul de produse pentru fiecare comanda:') ; IF NOT c_com%ISOPEN THEN 62. 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 '| |rec_com.nr_comanda||' are: '| |rec_com.numar||' produse') ; END LOOP; CLOSE c_com; END; / aia TSE RSET AREE TAT (ie Eat Yew nose Bo Serco Vorininy Miwon Tu Ro Boda 98 REN O-0 9- jf Q sis © brewee Devciro | 8 gar BESRO Bw SO crmmmecns = Biome [Donets scetcune gen |u| Joos | Conn 8 Bie leas 3 a Figura 5.5 Executia blocului PL/SQL Cursori si tabele index BY 63 4. Sa 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.salariulstype; 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 2... 75, else 2 :simp*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; / Baze de date. Limbajul PL/SQL G5288 9°: 28R6-9°8- Q 513 o pa PEGA BRO tem (eS. Stet penne emer ef aie i j s_OTPOT POT. LINE sega oae-mme||* ee smpeseal pe estarin de “Uitaptt'®~ vad Ram): ‘Bay Bem eiroueauDen OR cinottea [ngejeol King are inporitu pe ealarsu de 24 Jngeioea Rocar ace sapretul pe saiactu Jgejaon De Hon are sapsetal pe saiactu angejeal mold ere sages pe sala de 2 ~ 18000 ROW Jangejoca Erase axe tapocioal pe saci de 1.75% ~ 20500 Rah Fanejera austin ste suport pe slaein Ge 2.5% ~ 7200 RL [anajera Potsballa ere Saporital pe sadaciu Ge 1.8 ~ 7200 RaW Dogejral Lornte ore sapoieul peasants 4e 1.5% ~ 620 208 Figura 5.6 Executia cursorului pentru 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 realizeaz’ in mod implicit deschiderea (OPEN), incdrcarea (FETCH) si inchiderea cursorului (CLOSE). Exemplu: Sa 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_rec.nume||' are salariul: ‘| |ang_rec.salariul) ; end loop; end; gaa 9% ZnB Is O [Pence DEBRS BMF omomeens ms) a 24 socom + Hem 3B emp ‘crsor eng cusor is select 44 eogat, mime, seLariul fron engajec voce 14 SepctanenteS0: ¥ cme resis i = Ove ino pinista cu mauizile ener in partment 2) 5 donk fe wate cree ie ° 2 Dra denser pu saan "Venema ness esl); t é fos 5 < SG aterm Xitvat ae serial 2700 Somme fuerte exe lara 20 Byer [emia tha we ee 220 i iB eleciaa Bisee ee selec: 300 ; ee snlaziaeal Aisa ee sari; 2600 j I cect Baron ee sacs 209 | 2B see i 26 Figura 5.7 Executia blocului PL/SQL 66 Baze de date. Limbajul PL/SQL Utilizarea unui cursor direct in cadrul instructiunii FOR in acest caz cursorul nu este declarat, nu are nume, este reprezentat doar de interogarea SELECT din cadrul instructiunii FOR, astfel: FOR NUME RECORD IN (SELECT. ++) LOOP END LOOP; Dezavantajul in acest caz este ca nu se pot utiliza atributele cursorului din cauza faptului ca acesta nu are nume. Exemplu: Sai se afiseze suma aferenta salariilor din fiecare departament: set serveroutput on declare begin dbms_output .put_line('Total salarii pe fiecare departament:') ; for dep_rec in (select d. id_departament dep, sum(a.salariul) sal from angajati a, departamente d where a.id_departament=d.id_departament group by d.id_departament) loop dbms_output .put_line('Departamentul '||dep_rec.dep||' are de platit salarii in valoare de: '||dep_rec.sal||' RON'); end loop; end; 7 Cursori si tabele index BY 67 9 Orde 3 Beeoper oracle fp ‘Be fa Vow Waits fom Suse Versnng Wreion Toole Heb Boas 9 kaa oo 3- a Gi Birman soe seo a Neen merge ata a. BF 3 ‘Dasa = Roretow ® Grom * Bom * eres (orfect did epee dep, weasel) sab © Bm ‘hen mgjat a, cearcncte & 8D ae sie es fepctaimted. id deparemet D>roune Ds ode "Zentn|Sadaree ees osne| OHA une SSN negate 10 cede plies in mula des 1600 Ra Sop drama 20 cede aie let tn vere et 2600308 SeiBraeSmme lpr 30 ee cess nol fat | Siow |nepatmeneal20 cede lek slr in were et 00208 Giron patna 7 te de lee alts lence 4 60204 oes Depactanenal 50 axe de platst caleis in valoece de: 186400 ROK GSR Sree foerartmentad #0 are de plait salar in vatcne der 35060 3a mes tepacemanel a ney elec in alu er 00 FOE I es Donne 6 sted late sais Sn wloce Set 2500 5a Depertneneu 10 ae de plstst elacit sn valoace der 4400 ROD Figura 5.8 Executia blocului PL/SQL Utilizarea cursorului cu parametru Pentru o flexibilitate mai mare se pot declara si utiliza cursori cu parametru care transmit valorile parametrilor actuali in cererile SQL. Declararea cursorului cu parametru se face astfel: Cursor nume_cursor (parametrul tip_data.,.....) Is select .... a Deschidere: Open nume_cursor(valoare_parametrul..... Cursorii parametrizati nu ofera o functionalitate suplimentara ci doar o modalitate simpla si clara de a specifica valori de intrare. Tipurile parametrilor sunt scalare, dar nu li se precizeazd dimensiunea, acestea fiind referite in interogare. 68 Baze de date. Limbajul PL/SQL Exemple: 1. Sa se afigeze produsele al cdror cantitate totald comandata este mai mare decdt o valoare primitd drept parametru. SET SERVEROUTPUT ON DECLARE CURSOR c_prod (p_ val NUMBER) IS SELECT p.id_produs, p.denumire_produs, sum(r.cantitate) total FROM produse p, rand comenzi r WHERE p.id_produs d_produs GROUP BY p.id_produs, p.denumire_produs HAVING sum(r.cantitate) >p_val ORDER BY total desc; v_val NUMBER(5) ; rec_prod c_prod%rowtype; BEGIN v_val:=500; DBMS_OUTPUT. PUT_LINE('Produsele al caror cantitate vandut& este mai mare decat '| | v_val); IF NOT c_prod%ISOPEN THEN OPEN c_prod (v_val) ; END IF; LOOP FETCH c_prod into rec_prod; EXIT WHEN c_prodtnot found; DBMS_OUTPUT.PUT_LINE('Din produsul "| |rec_prod.id_produs||', '| |xec_prod.denumire_produs||', s-au vandut ' | |rec_prod.total||' unitati'); END LOOP; CLOSE c_prod; END; Z Cursori si tabele index BY 69 2 Figura 5.9 Executia blocului PL/SQL 2. Séi se afigeze pentru fiecare comand produsele comandate. In acest caz se utilizeazd 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; xec_com c_com%rowtype; --variabila record pentru campurile din primul cursor rec_prod c_prodtrowtype; --variabila record pentru campurile din al doilea cursor BEGIN OPEN c_com; LOOP FETCH c_com into rec_com; EXIT WHEN c_com%notfound; DBMS_OUTPUT. PUT_LINE('Comanda ' | | xec_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 foun DBMS_OUTPUT.PUT_LINE('Din produsul '| |rec_prod.id_produs||', ' | |rec_prod.denumire_produs||', s-au comandat ' | |vec_prod.cantitate||' bucati'); END LOOP; CLOSE c_prod; DBMS_OUTPUT. PUT_LINE ( '=: END LOOP; CLOSE c_com; END; / Cursori si tabele index BY 7 eG Yew mgs Bm Soge vosowy Heexin Io tee Goad 90 Xgne-e 5- oo ‘MOORE. POLED produ “leas pro. peou |, ‘a ion cance pets eee prod deaice prota", econo block conpieted comande 2955 inches Ln daa de 25-98 102.51, 962692 MX Din produza 2389, Uc Your 9/PH, s-au comand 204 bucati pin proms 2339, Peper - Sut Printer, s-au conndat 199 Duet oso predisal 2200, Plastic Stock - Ry $4 coment 187 baeats Din prods 2326, Mastic Stock ~ Y, sau conned 182 bucati Figura 5.10 Executia blocului PL/SQL 3. Sa se afiseze denumirea functici si departamentul in care lucreazét primii n angajati. Se va utiliza cursor cu parametru, iar n este citit de la tastaturd. set serveroutput on; DECLARE CURSOR c is select nume, salariul, id_departament, id_functie from angajati; CURSOR ci(id_dep angajati.id_departament%type) is select denumire_departament from departamente where id _dep=id_departament; CURSOR c2(id_funct angajati.id_functie%type) is select denumire_functie from functii where id_funct=id_functie; 2 Baze de date. Limbajul PL/SQL BEGIN FOR var IN c LOOP DBMS_OUTPUT.PUT_LINE('Angajatul :'||var.nume| |! salariul :'||var.salariul) ; FOR varl IN cl(var.id_departament) LOOP DBMS_OUTPUT.PUT_LINE('Este in departamentul' | |var1.denumire_departament) ; END LOOP; FOR var2 IN c2(var.id_functie) LOOP DBMS_OUTPUT. PUT_LINE('Are functia' | | var2.denumire_functie) ; END LOOP; EXIT WHEN ctnotfound or c%rowcount>&n; END LOOP; "_OOTOT. POT TIME ageseeul + Tivar-mne[1™ selarsul #Tiwaeslaeiallz aw Ls acne Be ox al terri pstmt ah Bons ‘SOP Eee npc 0 en eta; gen EE a hy eee i aaae Sa oro ern ee fei’. eum ete; it Siar ‘irs Fe [icant ee B 2c : id 2 rete 9 Wipoceane i 2 twas 2 Bom Bonnie Ete $ Btwn 2 Girone 2 Grate & Sumo ce Ere Spon $ Woacerun Sopra tts Figura 5.11 Executia cursorului pentru n=5 Cursori si tabele index BY 3 4. Sa se afiseze produsele si cantitatile cumparate de catre clienti. set serveroutput on; DECLARE SOR c is select nume_client, id_client from clienti; CURSOR cl(id_c clienti.id_client%type) is select c.nr_comanda,c.modalitate,p.denumire_produs, x.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; 7 74 Baze de date. Limbajul PL/SQL eee [Oreo PT Loweclientls *{1eerome_eienti" achieieinat urstoare peodue:'}s Om pack DH elves: elene) Lor ‘m5 _OOTUT. POT. IME (vr. emcize produs|* °|{werL.andalieate||" ‘I |varh.cmeteae); Drea soe Oa Reman |iBaasec|ooscant| Gomme 788 Ciientals Hosa « ochieiticnat wasteurle produwe: ; ‘Figura 5.12 Executia blocului PL/SQL Actualizarea inregistrarilor returnate de cererea cursorului. Clauza FOR UPDATE Pentru asigurarea consistentei si concurentei la date, actualizarea inregistrarilor din tabele care sunt incrcate in cursorul explicit se poate face prin blocarea setului de inregistrari in 2 variante: NOWAIT si WAIT n. CURSOR C IS SELECT .... FROM.... FOR UPDATE [OF COLUMN_NAME] [NOWAIT|WAIT nj; Clauza FOR UPDATE din interogarea asociat cursorului se adauga pentru a bloca liniile afectate atunci cand cursorul este deschis. Clauza NOWAIT determina aparitia unei erori daca liniile sunt blocate de o alta sesiune. Atunci cand mai multe tabele sunt implicate in interogare, se poate folosi FOR UPDATE pentru. a impune blocarea liniilor unei tabele anume. Liniile unei tabele sunt blocate numai in cazul in care clauza FOR UPDATE face o referire la 0 coloana din acea tabela. Cursori si tabele index BY Kid Exemplu: Se creeazd tabela SITUATIE care stocheaza informatii referitoare la comenzi: codul, valoarea comenzii. Se adaugd in aceasta coloana TVA, care va pistra valoarea cotei TVA (19%) pentru fiecare comanda. Se creeazi un cursor cdruia i se adaugd clauza FOR UPDATE pentru a bloca liniile afectate din tabeld, atunci cand cursorul este deschis, iar pentru fiecare comandd din cursor se va calcula valoarea TVA. DROP TABLE situatie; CREATE TABLE situatie AS SELECT c.nr_comanda cod, SUM(r.cantitate*r.pret) as valoare FROM comenzi c, rand_comenzi r WHERE c. nr_comanda =r. nr_comanda AND c.modalitate= 'online' GROUP BY c. nr_comanda; ALTER TABLE situatie ADD(tva NUMBER (10)) ; DECLARE CURSOR c_situatie IS SELECT cod, valoare, tva FROM situatie FOR UPDATE OF tva NOWAIT; BEGIN FOR rec_situatie IN c_situatie LOOP UPDATE situatie SET tva=valoare*0.19 WHERE cod=rec_situatie.cod; DBMS_OUTPUT . PUT_LINE('Comanda '||vec_situatie.cod||' are valoarea totala de '| |xec_situatie.valoare||' RON si TVA de: '||rec_situatie.tva ); END LOOP; END; / SELECT * FROM situatie; 16 Baze de date. Limbajul PL/SQL = Est Yew Howe Bim Sous Yeronnng Bowen Toor to gDuad 9° XGh ee 4- @ io = Sai i aay 1 Snares ews [Cems Z380 ace valoncen totale de 2713.6 ROY si TWA de: rea $ Gemenos vee] Cons 2264 are valoaen toate de 2920.1 RO a1 TWA de: : 8 Boren cnman 2307 ee valaze total Se 521389 POF 34 Th et || oeeetts f ipramebowmcrtns |fomnte 277 ace valence totale de SS07.6 OF oi 7h de: Figura 5.13 Executia blocului PL/SQL Atentie: in cazul de mai sus se poate observa faptul cA atributul REC_SITUATIE.TVA este NULL dupa executia comenzii UPDATE deoarece nu se actualizeaza automat si cursorul odata cu tabela. Acesta trebuie inchis si redeschis pentru a fi vizibile actualizarile din tabela. Pentru manipularea c&t mai usoara a comenzilor DML UPDATE si DELETE se poate utiliza clauza WHERE CURRENT OF care permite actualizarea inregistrarilor pe baza liniei curente din cursor. UPDATE tabela SET camp= WHERE CURRENT OF nume_ cursor; Cursori si tabele index BY 77 Se poate referi linia din tabela originara, pentru actualizare sau stergere, prin intermediul liniei curente a cursorului (cea procesata de ultima instructiune FETCH). Clauza FOR UPDATE trebuie inclusa in definitia cursorului pentru a bloca liniile in momentul executiei instructiunii OPEN. Exemplu: Exemplul de mai sus poate fi rescris, actualizarea inregistrarilor din tabela SITUATIE realizdndu-se cu clauzza WHERE CURRENT OF: DROP TABLE situatie; CREATE TABLE situatie AS SELECT c.nr_comanda cod, SUM(r.cantitate*r.pret) as valoare FROM comenzi c, rand_comenzi r WHERE c. nr_comanda nr_comanda AND c.modalitate= 'online' GROUP BY c. nr_comanda; ALTER TABLE situatie ADD(tva NUMBER(10)) ; DECLARE CURSOR c_situatie IS SELECT cod, valoare, tva FROM situatie FOR UPDATE OF tva NOWAIT; BEGIN FOR rec_situatie IN c_situatie LOOP UPDATE situatie SET tva=valoare*0.19 WHERE CURRENT OF c_situatie; DBMS_OUTPUT . PUT_LINE( 'Comanda '| |rec_situatie.cod||' are valoarea totala de '| |rec_situatie.valoare||' RON si tva de: '||rec_situatie.tva ); 78 Baze de date. Limbajul PL/SQL END LOOP; END; i, SELECT * FROM situatie; V.3 Tipuri de tabele index BY Tipul de date se utilizeaza similar cu o matrice gi este un tip de date definit de utilizator astfel: TYPE nume_tab IS TABLE OF {TIP_DATA [variabila%type | tabela.coloanattype [NOT NULL] | tabelatrowtype} INDEX BY PLS_INTEGER | BINARY_INTEGER | VARCHAR2 (dimensiune) ; Declararea unei variabile de acest tip se face astfel: v_tab nume_tab; Adresarea elementelor tabelei se realizeazi cu v_tab(index).camp. Spre deosebire de prelucrarile pe matrici, in cazul acestui tip de date indexul este unic, dar in ordine aleatorie si poate fi negativ situat intre limitele intervalului PLS_INTEGER de (-2147483647, 2147483647) Se pot utiliza urmatoarele proprietati si metode: v_tab.EXISTS(i) — returneaza TRUE daca elementul i exista si are valoare; v_tab.COUNT - returneaza numarul de elemente din variabila; v_tab. FIRST si v_tab.LAST ~ returneaza indexul primei, respectiv ultimei linii din variabila; vtab. PRIORG) si vtabNEXT(i) - retumeazi precedentul, respectiv urmatorul element al lui i; vtab.DELETE sau v_tab.DELETE() sau v_tab.DELETE(j) — sterge elementul curent, respectiv elementul i, sau elementele i-j. SUBPROGRAME PL/SQL Subprogramele sunt blocuri PL/SQL care au un nume, o definitie, 0 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 declarativa a subprogramelor se numesc parametri formali (formal parameters), iar pentru acestia se pot specifica valori implicite (DEFAULT). Variabilele utilizate in apelul procedurii/functiei se namese parametri actuali (actual parameters). in corpul procedurilor/functiilor nu se pot utiliza variabile globale sau de substitutic, acestea vor fi transmise in subprograme cu ajutorul parametrilor. | VILA Proceduri Parametrii utilizati in cadrul unei proceduri pot fi de mai multe tipuri: « _IN- parametru de intrare; valoarea sa se utilizeazi in cadrul procedurii ffird a putea fi modificata, 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 stainga a © un parametru IN OUT poate apiirea 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 fara dimensiune (ex: NUMBER sau VARCHAR2) Is|as -- zona de declarare a variabilelor utilizate in subprogram -- WU se utilizeaza DECLARE BEGIN [EXCEPTION] END [NUME_PROC]; Pentru a sterge procedura se utilizeaza urmatoarea sintaxa: DROP PROCEDURE NUME_PROC; Apelul unei proceduri se poate realiza in urmdtoarele moduri: © prin apelul dintr-un bloc PL/SQL anonim sau un alt subprogram; e prin apel direct cu EXECUTE nume_proc sau EXEC nume_proc sau CALL nume_proc; ¢ din alt mediu Oracle (Oracle Developer Suite). Pentru afisarea erorilor aparute la compilare se utilizeazi SHOW ERRORS. Exemplu: Sd se realizeze o procedurd pentru a adduga in tabela PRODUSE o 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 si direct. SET SERVEROUTPUT ON -- crearea 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; Monitor LCD'; -- adaugam o noua inregistrare: insert into produse (id_produs, denumire_produs) values (maxim+1, denumire_produs denumire_produs) ; DBMS_OUTPUT.PUT_LINE ('S-au adaugat ‘| |sQL%rowcount||' inregistrari'); end; / SHOW ERRORS G08g 9° xan Oe -O-48- @ow © Demanae Ba gar >Esa ¢ Tk | enmite pros profuse. denice prot tte: { |aecin promise, 14 peomie Wipe: | i ‘prods, emice potue) alees (ancin, demasce peu)? baw. rome (ran veey "is stwcae|*anepetae 2, Figura 7.1 Crearea procedurii 106 Baze de date. Limbajul PL/SQL -- apelul procedurii prin bloc PL/SQL anonim: begin adauga_produs; end; 7 -- apelul direct: call adauga_produs() ; (Fale taper " “Te (Eat Yew Howe Bn Sous Voosriw Moisi Tom tab 308d 9% xanoO-o 3- fe a Ip Deore So gar baaae fee] 3 cores Gea Samet 5 Figura 7.2 Apelul procedurii intr-un bloc PL/SQL Procedura creati este stocati in baza de date si poate fi apelat oricdnd, atat din blocuri PL/SQL cAt si din alte medi de dezvoltare. Codul sursa si lista parametrilor pot fi vizualizate in SQL Developer din meniul din dreapta->lista Procedures (figura 7.3). Subprograme PL/SQL 107 1 Oracle SOL Developer: PROCEDURE ADC (ie GOL Yow eto Ban Souce en 4 sa peebn prot iat Wes | ‘emmize protas protuse. deumize pro6es type: | ‘auxin profuse. id produs Vere: i sawert Sate prodve (1d prod, denice profs) valee (aotat!, deuaize pest): | DBs. OOTP. PIT LIME (‘Ss sneeat|S@LAtoncomel|"sncesserach'}2 Figura 7.3 Vizualizarea codului sursa al procedurii create Utilizarea parametrilor de tip IN — in acest caz valorile primite vor fi transferate i utilizate in cadrul procedurii fara a putea fi modificate. Exemplu: Sa se realizeze 0 proceduré prin care sd 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 | PROCEDURE modifica_salariul_procent (p_id_angajat IN angajati.id_angajat%type, procent IN number) 108 Baze de date. Limbajul PL/SQL Is v_salariul angajati.salariulstype; BEGIN --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) ; END; a show errors; Atentie! Salariul angajatului nu va fi modificat definitiv pani cand tranzactia nu este finalizaté prin instructiunea COMMIT care poate aparea in procedura (nerecomandat), in blocul apelant sau in mediul de dezvoltare. Anularea tranzactiei poate fi realizaté in acelasi mod prin instructiunea ROLLBACK, asa cum se observa in apelul urmitor: EXECUTE modifica_salariul_procent (176, 10) Rollback; Subprograme PL/SQL 109 (at Yew tinue fim Source Versi Mgrwin Tonte Hep Goad 9¢ Xam 0-0 8- set serveroatpat of ee es ere prem, 1) i & | ean lew it ngejeea ace sou salaried 10806 Figura 7.4 Apelul procedurii MODIFICA_SALARIUL Utilizarea parametrilor de tip OUT — in acest caz valorile din procedura vor fi tranferate si utilizate in afara procedurii. Exemple: 1. S& se realizeze o procedurd 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 si 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 afisa 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); END; Boaaae & Eee ap aay |] come angneci-nanestype: (coelciul enejaehslecsuitrpe? |Pouea_eueyee 0, wey w saazsel)? ee sash de: “44 placa Figura 7.5 Apelul procedurii CAUTA_ANGAJAT Subprograme PL/SQL 1m 2. Sa se realizeze 0 proceduré prin care se calculeazd salariul mediu al angajatilor: Procedura calculeaza 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 112 Baze de date. Limbajul PL/SQL 3. Sa se realizeze o procedurd 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 retumeazi 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_min%type) 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; i show errors; Apelul procedurii intr-un bloc anonim pentru un id introdus de la tastatura: 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; i Subprograme PL/SQL 113 7 Oracle Sl Doeloper oracle Ip (Be ER Yew Henge Gan Suice Versgnng Aretion Joule Hep Bo8a 9e ROB O-< Bay cers om 2g £ Qawaot ¥ i Uammaetane i0ne = i 5 owe» nterognce produse ccod prosas v pret i)? ‘ : ee creme a pes en i ® ome [ecmyuous block capleted | eden Petia Roser 19/5 ae peel mia: 452 i | 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: Sa se realizeze o procedurd prin care se modifica salariul unui angajat doar in cazul in care este mai mic decdét media prin apelarea procedurii create mai sus, Procedura MODIFICA_SALARIUL_MEDIU primeste id-ul angajatului ca parametru de intrare gi salariul mediu actual si returneaza prin parametrul de tip IN OUT salariul mediu modificat prin apelul procedurii SAL_MEDIU. 114 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 OUT number) Is nume angajati.nume%type; sal angajati.salariulttype; BEGIN Select nume, salariul into nume, sal from angajati where id_angajat= p_id_angajat; IF sal. oe aay Visdewwomerae aaa [5 Q canecns Saat Br Figura 7.9 Vizualizarea procedurilor stocate din schema curenta Pentru a vizualiza corpul unei anumite proceduri (de exemplu SAL_MEDIVU) utilizam tabela virtuala USER_SOURCE: Select text From user_source Where name=' SAL_MEDIU' and type='PROCEDURE'; 118 Baze de date. Limbajul PL/SQL (Be Ga Mew Huge Be Sopce Vomping Mipcom Ioan toe 828d 9@ XG 0-0: 3- : ae Beane 8 0 Pip Ri ae : og aay D906 UN Oma ri 2 A comectons: [emer St Setar eo eee 5B cae incre ne DOW an es ROE: \¥ 2D apes ed |B o | i em Brees i Evers | | i@oe / 1B recsages i 5 Odea : Touma reo.s 4 SB rmoowe rence I jae sol ant enn ise Qeas onl Cora ome 2 Qrcorca same |O FS : Figura 7.10 Vizualizarea codului sursa al procedurii SAL_MEDIU VIL2 Funetii Spre deosebire de proceduri, la crearea unei functii trebuie precizat obligatoriu tipul returnat de aceasta. O alt caracteriticd a functiilor este aceea ci ele se pot apela atat dintr-un mediu cu nucleu PL/SQL ct si direct din SQL, in mod asemanator cu functiile predefinite, ca spre exemplu SUM, AVG, ROUND, TO_DATE etc. Sintaxa pentru crearea unei functii: CREATE [OR REPLACE] FUNCTION nume_functie [(param1 [IN] TIP1,Param2[IN] TIP2, ....) ] RETURN TIP_DATA -- tipul variabilelor este precizat fara dimensiune (ex: NUMBER sau VARCHAR2) Is|as Subprograme PL/SQL 119 -- zona de declarare a variabilelor utilizate in subprogram -- nu se utilizeaza DECLARE BEGIN RETURN VALOARE; [EXCEPTION] END [NUME_FUNCTIE] ; Pentru vizualizarea tipului returnat se utilizeaza comanda DESCRIBE: DESCRIBE nume_functie; Pentru a sterge functia din schema dictionarul bazei de date se utilizeaza comanda DROP: DROP FUNCTION nume_functie; Exemple: 1. S& se creeze o functie care returneazd true, respectiv false daca un angajat al cdrui ID este primit ca parametru are salariul mai mare, respectiv mai mic sau egal cu salariul mediu si null in cazul in care angajatul nu exista. CREATE OR REPLACE FUNCTION verifica_salariul (p_id_angajat IN angajati.id_angajat%type, p_sal_mediu IN number) RETURN Boolean Is v_salariul angajati.salariul%type; BEGIN SELECT salariul into v_salariul from angajati where id_angajat=p_id_angajat; IF v_salariul > 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; 7 x | show errors (He Ga Mew Kooue Bim Souce Yerszing Woraton Toole He Boag 9e XaR 0-0: 48- % Gore 3 18 9 Se ear H SB come z | sgeocm ae ot NEA POET weciiog ech AL on (14 ogee BF sngpeegeecie, pe neta I wes) 8 Gamer {Ena Bless ra par = Vis} |G ieew slain ogee. anleiate; Vt ae em le Fire |semcr sears into seeziul from sngeacs ere Sd engjatp_s4_euaiets i Vere 1 & selacial > peel sedis en 2 Ganioe tern tems 2 Brome ns sete false: & aa toe rte eit aia Ct, ees” crs. 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. Subprograme PL/SQL 121 [Grae 5 Deeper fat ten tote fm Saxe Vomnng Moan Tote te 2080 9* eR 0-0 3- if D Dewey Gvewca smn Cote cots pte eer oe ieee racial fom engesaet where 6 angniaop_A¢soonsatz 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; i Subprograme PL/SQL 123 Be GH Yew Wot Gn Sere Voi Merson Too tee G88 9% XBHO-0- 3- Is Dore | 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) i NUMBER (2) ; s NUMBER (4) ; mod_11 NUMBER (2) ; cc VARCHAR2 (1) ; mesaj VARCHAR2 (200) ; BEGIN '279146358279'; 124 Baze de date. Limbajul PL/SQL IF length(penp) != 13 THEN mesaj 'CNP nu are 13 caractere'; ELSE BEGIN FOR i IN 1..12 LOOP BEGIN IF substr(penp,i,1) IN (04,924, (29,53, 144,150, 16), 97) Ss := 8 + to_number(substr (ponder: to_number (substr(penp,i,1)) ; ELSE mesaj := 'CNP ne numeric’; RETURN mesaj; END IF; END; END LOOP; mod_11 := mod(s,11); IF mod_11 = 10 THEN cc := '1'; ELSE ec := to_char(mod_11); END IF; IF cc = substr(penp,13,1) then mesaj := ‘CNP Corect’; ELSE mesaj eronat) END IF; END; END IF; RETURN mesa}; END cc_cnp; 7 Utilizarea functiilor PL/SQL in SQL — se realizeazé asemdndtor cu apelul '8','9') THEN i,1)) * 'Cifra de control eronata (CNP functiilor predefinite Oracle. Exemplu: Sa se creeze o functie PL/SQL pentru calculul taxelor. Subprograme PL/SQL 125 Funetia va primi drept parametri valoarea la care se aplicd taxa si procentul taxei respective. CREATE OR REPLACE FUNCTION taxa (value IN NUMBER, proc IN NUMBER) RETURN NUMBER IS BEGIN RETURN (value*proc) ; END taxa; 7 show errors Apelul functiei se va realiza intr-o interogare care afigeaza id_ul unui produs, cantitatea, pretul si TVA-ul acestuia. SELECT id produs, cantitate, pret, taxa (pret, 0.19) as tva FROM rand_comenzi; SE Den) some eS veneers. ne | | f= Pg | BS {ogg Foe Hee. |i 2 IIS =. go Se | | pee == = | ee) ih aiaie ni aaa 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; Observ: 1. Functiile utilizate in expresii SQL trebuie si accepte doar parametri de tip IN si s& returneze numai tipuri de date specifice SQL. 2. Functiile apelate in cadrul expresiilor SQL pot fi continute in pachete de subprograme. 3. Functiile nu trebuie si contina comenzi DML (update, delete, insert) sau comenzi DDL sau pentru controlul tranzactiilor (commit, rollback) si nici nu trebuie sa apeleze alte subprograme care sa incalce aceste restrictii. in cazul in care aceste conditii nu sunt respectate se va genera o eroare, asa cum se poate observa din exemplul urmator: Exemplu: Apelul urmatoarei functii dintr-o instructiune SQL va returna eroarea: 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 fi apelata intr- un bloc PL/SQL, apelul sau in instructiuni SQL va genera eroare precizata: UPDATE produse SET pret_min= produs_nou (2000) WHERE id_produs=2245; 8 ciate Di te (ie Et Yew ge Bn Suce Vernanng ersten Tole tp BD0ad 9% xR 0-6-5- ala S Dower a2 Ss OC 8 ‘Arona wes econtxesprtnngie eget pei. mde, ingen yn Figura 7.15 Apelul functiei PRODUS_NOU va genera 0 eroare Stergerea unei functii se realizeaz utilizand 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'; 128 Baze de date. Limbajul PL/SQL Pentru a vizualiza codul sursé al functiei, de exemplu pentru functia TAXA: Select text From user_source Where name='TAXA' and type='FUNCTION' ORDER BY line; ~~ Exercitii propuse 1. Creati o functie care primeasci drept parametru codul angajatului si care si retueze numérul de functii distincte (id_functie) pe care acesta le-a avut de-a lungul timpului. Testati functia intr-un bloc anonim. 2. Parcurgeti printr-un cursor toti angajatii departamentului IT si pentru fiecare dintre acestia specificati numirul de functii distincte detinute in cadrul organizatiei (prin apelul functiei create la exercitiul 1). 3. Transformati functia creaté la exercitiul 1 intr-o proceduré cu parametri de tip IN, respectiv OUT. Apelati procedura intr-un bloc anonim.

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