Documente Academic
Documente Profesional
Documente Cultură
BOTHA IULIANA
DIACONIŢA VLAD
LUNGU ION
VELICANU ANDA
BAZE DE DATE.
LIMBAJUL PL/SQL
BUCUREŞTI
2009
Baze de date. Limbajul PL/SQL
CUPRINS
INTRODUCERE ............................................................................................... . - 3 -
CAPITOLUL I. INTRODUCERE ÎN PL/SQL (PROCEDURAL LANGUAGE
EXTENSION TO SQL) ....................................................................................... - 4 -
I.1. INTRODUCERE............................................................................................. - 4 -
I.2. BLOCURI PL/SQL......................................................................................... - 5 -
I.3. UTILIZAREA OPERATORILOR ŞI FUNCŢIILOR ÎN PL/SQL................. - 7 -
CAPITOLUL II. VARIABILE PL/SQL ............................................................ - 10 -
II.1. DECLARARE ŞI INIŢIALIZARE ............................................................. - 10 -
II.2. TIPURI DE VARIABILE............................................................................ - 10 -
II.2.1. Variabile Scalare................................................................................... - 10 -
II.2.2. Tipuri de date compuse......................................................................... - 16 -
CAPITOLUL III. INTERACŢIUNEA CU SERVERUL ORACLE PRIN
COMENZI SQL ................................................................................................. - 20 -
CAPITOLUL IV. STRUCTURI FUNDAMENTALE DE PROGRAMARE . - 28 -
IV.1. STRUCTURI ALTERNATIVE................................................................. - 28 -
IV.2. STRUCTURI REPETITIVE ...................................................................... - 32 -
CAPITOLUL V. CURSORI ŞI TABELE INDEX BY...................................... - 37 -
V.1. CURSORUL IMPLICIT.............................................................................. - 37 -
V.2. CURSORUL EXPLICIT ............................................................................. - 39 -
V.3. TIPURI DE TABELE INDEX BY.............................................................. - 55 -
CAPITOLUL VI. TRATAREA EXCEPŢIILOR ............................................. - 58 -
VI.1. TRATAREA EXCEPŢIILOR PREDEFINITE ......................................... - 59 -
VI.2. TRATAREA EXCEPŢIILOR NON-PREDEFINITE ............................... - 63 -
VI.3. TRATAREA EXCEPŢIILOR DEFINITE DE UTILIZATOR.................. - 66 -
CAPITOLUL VII. SUBPROGRAME PL/SQL................................................. - 71 -
VII.1. PROCEDURI............................................................................................ - 71 -
VII.2. FUNCŢII................................................................................................... - 82 -
CAPITOLUL VIII. PACHETE DE SUBPROGRAME ................................... - 90 -
CAPITOLUL IX. DECLANŞATORI................................................................ - 106 -
IX.1. DECLANŞATORI PENTRU OPERAŢIILE DE MANIPULARE A
DATELOR ................................................................................................ - 106 -
IX.2. DECLANŞATORI PENTRU OPERAŢII DE DEFINIRE A DATELOR ŞI
PENTRU OPERAŢII LA NIVER DE SERVER ...................................... - 117 -
CAPITOLUL X. REALIZAREA DE VIDEOFORMATE ŞI RAPOARTE - 120 -
X.1. VIDEOFORMATE.................................................................................... - 120 -
X.2. RAPOARTE .............................................................................................. - 132 -
X.3. INTEGRARE FORMS-REPORTS ........................................................... - 137 -
CAPITOLUL XI. EXERCIŢII RECAPITULATIVE ..................................... - 145 -
CERINŢE PROPUSE........................................................................................ - 145 -
TESTE GRILĂ PROPUSE ............................................................................... - 147 -
SUBIECTE DATE LA EXAMENUL DE ADMITERE LA PROGRAMUL DE
MASTERAT DE APROFUNDARE INFORMATICĂ ECONOMICĂ Sesiunea
Septembrie 2008........................................................................................ - 164 -
BIBLIOGRAFIE ................................................................................................. - 169 -
ANEXE ................................................................................................................. - 170 -
ANEXA 1 – Scriptul pentru crearea tabelelor bazei de date............................. - 170 -
ANEXA 2 – Schema conceptuală a bazei de date utilizată în exemple ............ - 176 -
ANEXA 3 – Lista excepţiilor predefinite Oracle .............................................. - 177 -
-2 -
Baze de date. Limbajul PL/SQL
INTRODUCERE
Autorii
-3 -
Baze de date. Limbajul PL/SQL
CAPITOLUL I.
INTRODUCERE ÎN PL/SQL
(PROCEDURAL LANGUAGE EXTENSION TO SQL)
I.1. INTRODUCERE
PL/SQL este un limbaj procedural structurat pe bloc, programele putând fi împărţite în
blocuri logice, construcţiile PL/SQL conţinând structuri de control procedurale şi 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 în PL/SQL este modularizată, în acest sens se utilizează blocurile care
grupează instrucţiunile. Tipurile de date din SQL pot fi folosite în PL/SQL.
Pentru a putea lucra cu limbajul PL/SQL avem nevoie în primul rând de o conexiune
la o bază de date Oracle şi de un instrument de dezvoltare prin care să interacţionăm cu
serverul Oracle. Pentru instalarea şi configurarea instanţei bazei de da date se poate utiliza
ghidul electronic pus la dispoziţie de către compania Oracle odată cu kit-ul de instalare. Ca
instrument de dezvoltare se poate utiliza SQL*plus care se instalează împreună cu instanţa
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 bazează exemplele din această carte este următoarea:
-4 -
Baze de date. Limbajul PL/SQL
-5 -
Baze de date. Limbajul PL/SQL
BEGIN
-- instrucţiuni executabile;
EXCEPTION
WHEN excepţie THEN acţiune;
END;
/
Proceduri şi funcţii
Sunt blocuri PL/SQL care au nume şi pot fi stocate la nivelul bazei de date sau la nivel
de aplicaţie (de exemplu în mediul de dezvoltare Oracle Developer Suite – Forms şi Reports).
În cazul procedurilor şi funcţiilor secţiunea DECLARE este înlocuită de definiţia
acesteia prin sintaxa CREATE [or replace] PROCEDURE/FUNCTION (lista parametrilor şi
tipul acestora), de exemplu:
CREATE [OR REPLACE] PROCEDURE nume_procedura (lista parametri)
IS
……….
BEGIN
……….
[EXCEPTION]
……….
END;
/
-6 -
Baze de date. Limbajul PL/SQL
RETURN valoare
[EXCEPTION]
………
END;
/
Pachetele de subprograme sunt utilizate pentru a grupa mai multe proceduri şi funcţii
utilizate pentru un anumit tip de prelucrări.
Declanşatorii pe baza de date sunt blocuri PL/SQL asociate tabelelor (de bază sau
virtuale) şi lansate automat în execuţie când are loc o comanda de manipulare
(insert/update/delete).
Declanşatorii de aplicaţie sunt blocuri PL/SQL asociate unor evenimente din cadrul
aplicaţiei (de exemplu: deplasarea mouse-ului, apăsarea unui buton) şi lansate în execuţie
automat la apariţia acestor evenimente.
-7 -
Baze de date. Limbajul PL/SQL
-8 -
Baze de date. Limbajul PL/SQL
După conectare apare fereastra în care putem scrie şi executa blocurile, în mod
asemănător cu modul de lucru cu instrucţiunile SQL. Pentru execuţia blocurilor se dă click pe
unul dintre butoanele Execute Statement (F9) sau Run Script (F5).
EXERCIŢII PROPUSE
2. Creaţi o conexiune nouă în SQL Developer şi testaţi programul clasic Hello World!
-9 -
Baze de date. Limbajul PL/SQL
CAPITOLUL II.
VARIABILE PL/SQL
Constantele în PL/SQL trebuie obligatoriu iniţializate, iar ulterior nu îşi vor putea
schimba valoarea. Variabilele pentru care se precizează restricţia NOT NULL trebuie
obligatoriu iniţializate, iar ulterior nu vor putea primi valoarea NULL.
Pentru a putea urmări mai uşor codul sursă se foloseşte următoarea convenţie de
notare:
c_nume Constanta - pentru declararea constantelor;
v_nume Variabila - pentru declararea variabilelor;
g_nume Variabila_globala – pentru variabilele globale definite în zona de
specificaţii a unui pachet de subprograme şi valabile pentru toate procedurile şi
funcţiile pachetului.
- 10 -
Baze de date. Limbajul PL/SQL
Exemple:
v_functie varchar2(9);
v_numar binary_integer:=0;
v_totalsal number(9,2):=0;
v_datainceput date:=sysdate+7;
c_taxa constant number(3,2):=8.25;
v_valid boolean not null:=true;
- 11 -
Baze de date. Limbajul PL/SQL
1. Atributul %TYPE
Pentru a declara o variabilă PL/SQL care să aibă acelaşi tip de date cu o anumită
coloană dintr-o tabelă sau cu o altă variabilă declarată anterior putem folosi atributul %TYPE.
Declararea unei variabile cu %TYPE se face astfel:
variabila tabelă.nume_coloană%TYPE;
sau
variabila1 tip_dată;
variabila2 variabila1%TYPE;
Exemple:
Să se afişeze numele şi prenumele angajatului cu codul 100:
DECLARE
--declaram doua variabile pentru nume şi prenume:
v_nume angajati.nume%TYPE;
v_prenume angajati.prenume%TYPE;
BEGIN
-- populam şi 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;
/
- 12 -
Baze de date. Limbajul PL/SQL
Exemple:
1. Să se afişeze 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 în afara blocului:
PRINT g_mesaj
- 13 -
Baze de date. Limbajul PL/SQL
PRINT g_comenzi
3. Variabile de substituţie
De regulă, variabilele de substituţie sunt folosite pentru a transmite valori dinspre
mediul SQL*Plus spre comenzile SQL sau blocurile PL/SQL, în timp ce variabilele de
legătură (bind variables) sunt folosite pentru a transmite valori în sens invers sau pentru a
transfera valori între blocuri PL/SQL lansate succesiv (primul bloc setează variabila,
următorul o consultă).
Prin variabilele de substituţie se pot transmite valori comenzilor SQL sau blocurilor
PL/SQL lansate (folosind "&" sau "&&"). Se pot utiliza în comenzile SQL sau în blocurile
PL/SQL prin "&nume_variabila" sau "&&nume_variabila".
- 14 -
Baze de date. Limbajul PL/SQL
Variabilele de substituţie sunt locale sesiunii SQL în care au fost declarate. În mediul
SQL variabilele de substituţie pot fi uşor utilizate prin introducerea de valori de la tastatură
utilizând ACCEPT, se pot defini cu DEFINE sau afişa pe ecran cu PROMPT;
Exemplu: Să se afişeze numărul de comenzi ale angajatului al cărui cod este introdus de la
tastatură de către utilizator prin intermediul variabilei de substituţie &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;
/
Într-un bloc PL/SQL se pot utiliza toate tipurile de variabile, respectând însă
caracteristicile şi regulile de utilizare ale acestora. În exemplul următor se utilizază atât
variabila de substituţie s_nume definită şi iniţializată prin comanda DEFINE, cât şi variabila
de legătură g_salariul, dar şi variabila locală v_prenume de acelaşi tip cu coloana nume din
tabela Angajati. Variabila de substituţie definită cu DEFINE va fi implicit de tipul CHAR.
DECLARE
--declaram variabila locala PL/SQL
v_prenume angajati.nume%type;
BEGIN
select prenume,salariul into v_prenume, :g_salariul
from angajati where nume='&s_nume';
DBMS_OUTPUT.PUT_LINE ('Prenumele angajatului este:
'||v_prenume);
END;
/
print g_salariul
1. Tipul RECORD
Reprezintă un grup de date logic corelate (de exemplu, datele despre un client: codc,
nume, adresa sunt diferite ca tip dar corelate logic). Atunci când se declară un PL/SQL record
pentru aceste câmpuri, ele pot fi manipulate ca o unitate, fiecare câmp (element al structurii)
având un nume şi un tip de dată. Atributele unui record sunt referite astfel :
nume_record.nume_câmp.
Declarea unui tip record se face astfel:
TYPE nume_record IS RECORD (nume_câmp TIP_DATA [,nume_câmp
TIP_DATA:=|DEFAULT valoare]...);
Iar declararea unei variabile de acest tip:
Variabilă NUME_RECORD;
- 16 -
Baze de date. Limbajul PL/SQL
EXERCIŢII PROPUSE
- 17 -
Baze de date. Limbajul PL/SQL
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 = '||c_const1);
DBMS_OUTPUT.PUT_LINE('constanta 2 = '||c_const2);
DBMS_OUTPUT.PUT_LINE('constanta 3 = '||c_const3);
DBMS_OUTPUT.PUT_LINE('variabila 6 = '||v_var6);
END;
/
<<bloc1>>
DECLARE
var NUMBER;
BEGIN
var :=2;
DBMS_OUTPUT.PUT_LINE(var);
END bloc1;
DBMS_OUTPUT.PUT_LINE(var);
<<bloc2>>
DECLARE
var NUMBER;
BEGIN
var :=3;
DBMS_OUTPUT.PUT_LINE(var);
<<bloc3>>
DECLARE
var NUMBER;
BEGIN
var :=4;
DBMS_OUTPUT.PUT_LINE(var);
- 18 -
Baze de date. Limbajul PL/SQL
DBMS_OUTPUT.PUT_LINE(bloc2.var);
END bloc3;
DBMS_OUTPUT.PUT_LINE(var);
END bloc2;
DBMS_OUTPUT.PUT_LINE(var);
END;
/
- 19 -
Baze de date. Limbajul PL/SQL
CAPITOLUL III.
INTERACŢIUNEA CU SERVERUL ORACLE
PRIN COMENZI SQL
Interacţiunea se realizează 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 tranzacţiilor - TPL (Transaction Processing Language) astfel:
PL/SQL suportă toate comenzile din limbajul de manipulare a datelor (DML) şi din
cel de control al tranzacţiilor (TPL). Un bloc PL/SQL nu e o tranzacţie, comenzile
Commit/ Rollback/ Savepoint sunt independente de bloc dar pot să apară în
interiorul său.
Comenzi DML/TPL Executie
SELECT, INSERT, UPDATE, Se execută normal in cadrul blocului
DELETE, MERGE
Exemple:
1. Să se creeze tabela PROD în cadrul unui bloc PL/SQL:
SET SERVEROUTPUT ON
BEGIN
EXECUTE IMMEDIATE 'CREATE table prod AS SELECT * FROM
produse where 1=2';
END;
/
- 20 -
Baze de date. Limbajul PL/SQL
- 21 -
Baze de date. Limbajul PL/SQL
Pentru o mai mare flexibilitate se poate declara o variabilă de tip şir de caractere care
să primească comanda de definire a datelor care va fi executată prin Execute Immediate:
Exemple:
1. Să se creeze tabela EMP_SAL prin intermediul unei variabile de tip VARCHAR2.
La creare, în tabela EMP_SAL se va adăuga o nouă înregistrare.
SET SERVEROUTPUT ON
VARIABLE G_EID NUMBER
DECLARE
V_SIR VARCHAR2(200);
BEGIN
:G_EID:=110;
--atribuim şirul 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);
--utilizam comanda cu valoarea variabilei v_sir:
EXECUTE IMMEDIATE V_SIR;
END;
/
select * from emp_sal;
- 22 -
Baze de date. Limbajul PL/SQL
DECLARE
V_SIR VARCHAR2(200);
BEGIN
V_SIR:='ALTER TABLE PRODUSE ADD (STOC NUMBER (7))';
DBMS_OUTPUT.PUT_LINE (V_SIR);
EXECUTE IMMEDIATE V_SIR;
END;
/
select * from PRODUSE;
Exemple:
Comanda INSERT:
1. Să se adauge o nouă înregistrare în tabela EMP_SAL:
BEGIN
INSERT INTO EMP_SAL (id_angajat, nume, prenume,
salariul)
VALUES (200, 'Pop', 'Marian', 7500);
END;
/
select * from emp_sal;
- 23 -
Baze de date. Limbajul PL/SQL
BEGIN
INSERT INTO produse (id_produs, denumire_produs,
categorie, stoc)
VALUES (&id, '&denumire', '&categorie', &stoc);
END;
/
select * from produse;
Comanda UPDATE:
4. Să se mărească cu un procent salariul angajaţilor din tabela EMP_SAL care au în
prezent salariul mai mic decât valoare variabilei v_prag:
DECLARE
v_procent number:=0.1;
v_prag angajati.salariul%type:=10000;
BEGIN
UPDATE emp_sal
SET salariul=salariul*(1+v_procent)
WHERE salariul<v_prag;
END;
/
select * from emp_sal;
- 24 -
Baze de date. Limbajul PL/SQL
6. În urma comenzilor realizate de clienţi, monitoarele de tipul LCD sunt vândute, deci se
scade din stocul existent un număr de monitoare introdus de la tastatură:
BEGIN
UPDATE produse
SET stoc=stoc-&nr_buc_vandute
WHERE lower(denumire_produs) like 'monitor lcd%';
COMMIT;
END;
/
select * from produse;
- 25 -
Baze de date. Limbajul PL/SQL
Comanda DELETE:
7. Să se şteargă angajatul cu numele Pop din tabela EMP_SAL:
DECLARE
BEGIN
DELETE FROM emp_sal WHERE initcap(nume) like 'Pop%';
ROLLBACK;
END;
/
select * from emp_sal;
Atenţie! În cazurile de mai sus utilizarea comenzilor COMMIT respectiv ROLLBACK vor
avea efect atât asupra tranzacţiei curente cât şi asupra tranzacţiilor executate anterior, în afara
blocului.
EXERCIŢII PROPUSE
1. Creaţi un bloc PL/SQL prin care să se adauge un produs nou în tabela PRODUSE.
a. Folosiţi maximul dintre codurile produselor si adăugaţi 10 la această valoare,
folosind-o ca valoare pentru codul produsului nou introdus.
b. Folosiţi parametri (variabile de substituţie) pentru denumire, categoria şi stocul
produsului.
c. Lăsaţi descrierea produsului NULL.
d. Executaţi blocul.
- 26 -
Baze de date. Limbajul PL/SQL
2. Creaţi un bloc PL/SQL prin care să se selecteze stocul maxim pentru produsele existente în
tabela PRODUSE. Afişaţi rezultatul pe ecran.
3. Creaţi un bloc PL/SQL prin care să se şteargă un produs pe baza codului acestuia primit ca
parametru (variabilă de substituţie). Anulaţi ştergerea dintr-un alt bloc PL/SQL.
- 27 -
Baze de date. Limbajul PL/SQL
CAPITOLUL IV.
STRUCTURI FUNDAMENTALE DE PROGRAMARE
În cazul imbricării se poate folosi clauza ELSIF în loc de mai multe instrucţiuni IF
imbricate. Instrucţiunea este similară cu o instrucţiune de tip CASE.
IF cond1 THEN
secvcom1;
ELSIF cond2 THEN secvcom2;
ELSIF cond3 THEN secvcom3;
---------------------------------------
ELSIF condN THEN secvcomN;
ELSE secvcomN+1;
END IF;
v_stoc:=1.25* v_stoc;
END IF;
dbms_output.put_line('Stocul final este: '||v_stoc);
end;
/
- 29 -
Baze de date. Limbajul PL/SQL
- 31 -
Baze de date. Limbajul PL/SQL
Exemple:
1. Să se afişeze pe ecran utilizând structura LOOP…END LOOP numerele 9,7, 4, 0.
set serveroutput on
DECLARE
v_nr number(2):=10;
i number(2):=1;
BEGIN
loop
v_nr:=v_nr-i;
i:=i+1;
exit when v_nr < 0;
dbms_output.put_line(v_nr);
end loop;
END;
/
- 32 -
Baze de date. Limbajul PL/SQL
Exemple:
1. Să se afişeze pe ecran utilizând structura WHILE LOOP…END LOOP numerele
9,7, 4, 0.
set serveroutput on
DECLARE
v_nr number(2):=10;
i number(2):=1;
BEGIN
while v_nr > 0 loop
v_nr:=v_nr-i;
i:=i+1;
dbms_output.put_line(v_nr);
end loop;
END;
/
i number(4):=100;
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 '||i||' are
salariul: '||v_sal);
i:=i+1;
--iesim din ciclul WHILE la indeplinirea conditiilor
exit when v_sal<v_salmediu;
end loop;
end;
/
Observaţie: Valorile intervalului pot fi de orice tip, dar sa aibă valori care pot fi
convertite la un intreg (de exemplu 20/13 sau 11/5). Dacă aceste 2 valori vor fi egale ca
intregi atunci instrucţiunile din interiorul ciclului se execută o singură dată, ca în exemplul
următor:
FOR i IN 3..3 LOOP
- 34 -
Baze de date. Limbajul PL/SQL
Secventa comenzi;
END LOOP;
Exemple:
1. Să se încarce în tabela MESAJE numere de la 1…10 cu excepţia lui 6 şi 8.
create table mesaje (rez varchar2(30));
begin
for i in 1..10 loop
if i=6 or i=8 then
null;
else
insert into mesaje(rez) values (i);
end if;
commit;
end loop;
end;
/
- 35 -
Baze de date. Limbajul PL/SQL
EXERCIŢII PROPUSE
- 36 -
Baze de date. Limbajul PL/SQL
CAPITOLUL V.
CURSORI ŞŞII TABELE INDEX BY
Exemple:
1. Să se şteargă un produs din tabela PRODUSE şi să se contorizeze numărul de
rânduri şterse.
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;
/
- 37 -
Baze de date. Limbajul PL/SQL
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Nu exista produsul cu acest
cod');
END IF;
END;
/
- 38 -
Baze de date. Limbajul PL/SQL
Exemple:
1. Să se afişeze lista cu numele şi salariul angajaţilor 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;
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%notfound;
dbms_output.put_line('Salariatul '||ang_nume||' are
salariul: '||ang_sal);
end loop;
close ang_cursor;
end;
/
- 40 -
Baze de date. Limbajul PL/SQL
Pentru o flexibilitate mai mare se poate utiliza o variabilă de tip record pentru
încărcarea valorilor din cursor. Această variabilă de tip record poate avea aceleaşi atribute ca
şi cursorul prin specificarea proprietăţii %ROWTYPE. În acest caz încărcarea din cursor se va
face direct prin instrucţiunea FECH var_cursor INTO var_record.
(cod varchar2(7),
nume varchar2(20));
DECLARE
v_id angajati.id_angajat%type;
v_nume angajati.nume%type;
CURSOR c1 IS SELECT id_angajat, nume FROM angajati;
BEGIN
OPEN c1;
FOR i IN 1..5 LOOP
FETCH c1 INTO v_id, v_nume;
INSERT INTO t_ang VALUES(v_id, v_nume);
END LOOP;
CLOSE c1;
END;
/
SELECT * FROM T_ANG;
Testul de ieşire din ciclul LOOP în acest caz se poate face şi cu ajutorul
atributului %ROWCOUNT:
--se şterg înregistrările din tabela T_ANG:
Delete from T_ANG;
DECLARE
v_id angajati.id_angajat%type;
v_nume angajati.nume%type;
CURSOR c1 IS SELECT id_angajat, nume FROM angajati;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_id, v_nume;
- 42 -
Baze de date. Limbajul PL/SQL
3. Să se afişeze primele 3 comenzi care au cele mai multe produse comandate. În acest
caz înregistrările vor fi ordonate descrescător în funcţie 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;
rec_com c_com%rowtype;
BEGIN
DBMS_OUTPUT.PUT_LINE('Numarul de produse pentru fiecare
comanda:');
IF NOT c_com%ISOPEN THEN
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;
/
- 43 -
Baze de date. Limbajul PL/SQL
- 44 -
Baze de date. Limbajul PL/SQL
Exemplu:
Să se afişeze printr-un ciclu FOR numele şi salariile angajaţilor din departamentul 50:
set serveroutput on
declare
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;
/
- 45 -
Baze de date. Limbajul PL/SQL
Dezavantajul în acest caz este ca nu se pot utiliza atributele cursorului din cauza
faptului că acesta nu are nume.
Exemplu:
Să se afişeze suma aferentă 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');
- 46 -
Baze de date. Limbajul PL/SQL
end loop;
end;
/
Deschidere:
Open nume_cursor(valoare_parametru1,. .... );
Exemple:
1. Să se afişeze produsele al căror cantitate totală comandată este mai mare decât o
valoare primită drept parametru.
SET SERVEROUTPUT ON
DECLARE
CURSOR c_prod (p_val NUMBER) IS
SELECT p.id_produs, p.denumire_produs, sum(r.cantitate)
total
- 47 -
Baze de date. Limbajul PL/SQL
v_val NUMBER(5);
rec_prod c_prod%rowtype;
BEGIN
v_val:=500;
DBMS_OUTPUT.PUT_LINE('Produsele al caror cantitate
vândută este mai mare decat '|| v_val);
- 48 -
Baze de date. Limbajul PL/SQL
BEGIN
OPEN c_com;
LOOP
FETCH c_com into rec_com;
EXIT WHEN c_com%notfound;
DBMS_OUTPUT.PUT_LINE('Comanda '|| rec_com.nr_comanda
||' incheiata la data de '||rec_com.data);
DBMS_OUTPUT.PUT_LINE('============');
END LOOP;
CLOSE c_com;
END;
/
- 49 -
Baze de date. Limbajul PL/SQL
- 51 -
Baze de date. Limbajul PL/SQL
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 '||rec_situatie.cod||'
are valoarea totala de '||rec_situatie.valoare||' RON
si TVA de: '||rec_situatie.tva );
END LOOP;
END;
/
SELECT * FROM situatie;
- 53 -
Baze de date. Limbajul PL/SQL
UPDATE tabela
SET camp=....
WHERE CURRENT OF nume_cursor;
Se poate referi linia din tabela originară, pentru actualizare sau ştergere, prin
intermediul liniei curente a cursorului (cea procesată de ultima instrucţiune FETCH). Clauza
FOR UPDATE trebuie inclusă în definiţia cursorului pentru a bloca liniile în momentul
execuţiei instrucţiunii OPEN.
Exemplu:
Exemplul de mai sus poate fi rescris, actualizarea înregistrărilor din tabela SITUATIE
realizându-se cu clauza WHERE CURRENT OF:
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 );
END LOOP;
END;
/
- 54 -
Baze de date. Limbajul PL/SQL
Exemple:
1. Să se utilizeze o tabelă cu elemente de tipul produse.denprodus%type pentru
parcurgerea produselor cu codul între 2252 şi 2255:
DECLARE
--declararea tipului:
type num_table is table of produse.denumire_produs%type
index by pls_integer;
v_tab num_table;
i number(5):=2252;
BEGIN
--incarcarea in tabela de tipul index by:
loop
SELECT denumire_produs into v_tab(i) from produse where
id_produs=i;
i:=i+1;
exit when i>2255;
end loop;
--extragerea din tabela de tipul index by:
for i in v_tab.first..v_tab.last loop
IF v_tab.EXISTS(i) then
dbms_output.put_line('Nume produs: '|| v_tab(i));
- 55 -
Baze de date. Limbajul PL/SQL
end if;
end loop;
dbms_output.put_line('Total produse in tabela: '||
v_tab.count);
END;
/
- 56 -
Baze de date. Limbajul PL/SQL
END;
/
EXERCIŢII PROPUSE
1. Afişaţi toţi angajaţii şi comenzile încheiate de fiecare dintre aceştia. Folosiţi un cursor
pentru a încărca numele angajaţilor şi un cursor parametrizat pentru încărcarea
comenzilor încheiate de aceştia.
2. Afişaţi informaţii despre primele 3 comenzi care au cea mai mare valoare.
3. Afişaţi informaţii despre primii 5 salariaţi angajaţi (se va realiza filtrarea în funcţie de
câmpul Data_Angajare).
- 57 -
Baze de date. Limbajul PL/SQL
CAPITOLUL VI.
TRATAREA EXCEPŢIILOR
O excepţie reprezintă un identificator PL/SQL asociat unei condiţii anormale apărute
în timpul execuţiei unui bloc. Invocarea unei excepţii are ca efect terminarea blocului şi
întreruperea execuţiei normale a programului. Pentru evitarea unor situaţii de întrerupere
anormală, excepţia poate fi captată si poate fi specificată o rutină de tratare a acesteia.
Există două cazuri distincte de apariţie a erorilor:
a. Erorile sunt generate la nivelul serverului Oracle si excepţia asociata ei este automat
invocată;
b. Excepţiile sunt definite de utilizator şi declanşată în mod explicit prin instrucţiunea
RAISE utilizată în cadrul blocului.
Dacă excepţia este declanşată în secţiunea executabilă a unui bloc atunci se caută în
cadrul secţiunii de tratare a excepţiilor o rutină de tratare asociată. Daca PL/SQL poate trata
excepţia, ea nu este propagată în blocul exterior sau în mediul apelant, caz în care se
consideră că execuţia blocului s-a desfăşurat cu succes. Dacă nu există o rutină pentru tratarea
ei, excepţia este propagată în mediul apelant, caz în care execuţia blocului se termină cu eşec.
Tipuri de excepţii
În funcţie de modul în care se declanşează dar şi de modul în care sunt tratate, există
trei tipuri de excepţii:
- 58 -
Baze de date. Limbajul PL/SQL
Exemple:
1. Să se afişeze angajatul cu codul 10. Să se trateze eroarea apărută în cazul în care
nu există niciun angajat cu acest cod.
Rezolvare: în cazul în care nu există nicio înregistrare returnată de cererea SELECT şi
nu se tratează excepţia apărută (am comentat textul cu roşu din codul sursă de mai jos pentru a
simula această situaţie), va apărea pe ecran un mesaj de eroare, aşa cum se observă din figura
următoare:
- 59 -
Baze de date. Limbajul PL/SQL
Mesajul conţine codul erorii (ORA- 01403), textul explicativ: no data found, linie în
cadrul codului sursă unde a apărut (ORA-06512: at line 4). Din tabelul prezentat în Anexa 2,
observăm că acest cod al erorii este deja asociat cu o excepţie definită de Oracle –
NO_DATA_FOUND. In acest caz putem să utilizăm denumirea în secţiunea EXCEPTION
astfel:
set serveroutput on
DECLARE
v_nume VARCHAR2(20);
BEGIN
SELECT nume INTO v_nume
FROM angajati
WHERE id_angajat=10;
dbms_output.put_line(v_nume);
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Nu exista angajatul cu acest
ID!');
END;
/
In acest caz blocul se execută, dar în punctul în care se încearcă popularea variabilei
v_nume pe baza interogării SELECT se va declanşa excepţia, se caută numele acesteia în
secţiunea EXCEPTION, fiind găsită se execută instrucţiunile precizate aici, astfel blocul va
afişa mesajul ‘Nu există angajatul cu acest ID!’:
- 60 -
Baze de date. Limbajul PL/SQL
- 61 -
Baze de date. Limbajul PL/SQL
- 62 -
Baze de date. Limbajul PL/SQL
- 63 -
Baze de date. Limbajul PL/SQL
Aceste atribute pot fi încărcate în variabile şi adăugate într-o tabelă de erori pentru
vizualizare şi verificare ulterioară.
Exemplu:
Să se adauge în tabela DEPARTAMENTE un nou departament cu ID-ul 200, fără a
preciza denumirea acestuia.
Rezolvare: În acest caz, prin neprecizarea unei valori pentru coloana
denumire_departament pe care este activată restricţia de NOT NULL, va apărea eroarea cu
codul ORA-01400 prin care programatorul este avertizat de încălcarea acestei restricţii de
integritate. Codul şi textul erorii pot fi observate din mesajul apărut pe ecran (figura
următoare):
Observăm că în acest caz nu avem un nume pentru codul erorii apărute, excepţia fiind
non-predefinită. Pentru tratarea acesteia procedăm conform paşilor descrişi mai sus: asociem
în secţiunea DECLARE un nume codului apărut, iar în secţiunea EXCEPTION tratăm eroarea:
SET SERVEROUTPUT ON
DECLARE
-- se asociază un nume codului de eroare apărut
INSERT_EXCEPT EXCEPTION;
PRAGMA EXCEPTION_INIT(INSERT_EXCEPT, -01400);
BEGIN
insert into departamente (id_departament,
denumire_departament) values (200, NULL);
EXCEPTION
--se tratează eroarea prin numele său
WHEN insert_except THEN
DBMS_OUTPUT.PUT_LINE('Nu ati precizat informatii
suficiente pentru departament');
- 64 -
Baze de date. Limbajul PL/SQL
--blocul PL/SQL
DECLARE
cod NUMBER;
mesaj VARCHAR2(255);
del_exception EXCEPTION;
PRAGMA EXCEPTION_INIT(del_exception, -2292);
BEGIN
DELETE FROM produse;
- 65 -
Baze de date. Limbajul PL/SQL
EXCEPTION
WHEN del_exception THEN
dbms_output.put_line('Nu puteti sterge produsul');
dbms_output.put_line('Exista comenzi asignate lui');
cod:=SQLCODE;
mesaj:=SQLERRM;
INSERT INTO erori VALUES(USER, SYSDATE, cod, mesaj);
END;
/
SELECT * FROM erori;
- 66 -
Baze de date. Limbajul PL/SQL
e_ora_nepermisa EXCEPTION;
BEGIN
IF TO_NUMBER(TO_CHAR(SYSDATE, 'HH24'))>=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;
/
- 67 -
Baze de date. Limbajul PL/SQL
- 68 -
Baze de date. Limbajul PL/SQL
END;
/
SELECT * FROM ERORI;
Propagarea excepţiilor
Odată excepţia declanşată în secţiunea executabilă a unui bloc, se caută în cadrul
secţiunii de tratare a excepţiilor (EXCEPTION) o rutină de tratare asociată. Dacă PL/SQL
poate trata excepţia, ea nu este propagată în blocul exterior sau în mediul apelant, caz în care
se consideră că execuţia blocului s-a desfăşurat cu succes.
Atunci când un sub-bloc tratează o excepţie, acesta se termină normal iar execuţia se
reia în blocul ce-l cuprinde imediat după instrucţiunea END a sub-blocului.
Dacă apare o excepţie, iar în blocul curent nu există o rutină pentru tratarea sa,
execuţia blocului se termina cu eşec, iar excepţia se propagă succesiv în blocurile exterioare
până este găsită într-unul din ele o rutină pentru tratarea ei. Dacă nu se găseşte nici una, în
mediul apelant apare o situaţie de excepţie nerezolvată, utilizator putând observa mesajul de
eroare care a întrerupt execuţia normală.
EXEMPLU
Utilizând blocuri imbricate, să se afişeze comanda încheiată de un anumit client al
cărui nume este citit de la tastatură. Să se trateze excepţiile apărute.
set serveroutput on;
declare
nr_com comenzi.nr_comanda%type;
v_nume clienti.nume_client%type;
v_id_client clienti.id_client%type;
begin
- 69 -
Baze de date. Limbajul PL/SQL
v_id_client:=& id_client;
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;
begin
select nr_comanda into nr_com
from comenzi
where id_client = v_id_client;
dbms_output.put_line('Clientul'||v_nume||'are comanda:');
dbms_output.put_line(nr_com);
end;
exception
when no_data_found then
dbms_output.put_line('Nu exista informatii privind
comenzile incheiate de acest client!');
when too_many_rows then
dbms_output.put_line('Clientul are mai multe comenzi!');
end;
/
EXERCIŢIU PROPUS
Creaţi o tabelă numită Mesaje, având un câmp unic, de tip Varchar2. Scrieţi un bloc PL/SQL
pentru a selecta codul comenzilor încheiate în anul 2000.
a. Dacă interogarea returnează mai mult de o valoare pentru numărul comenzii, trataţi
excepţia cu o rutină de tratare corespunzătoare şi inseraţi în tabela Mesaje mesajul “Atenţie!
In anul 2000 s-au încheiat mai multe comenzi!”.
b. Dacă interogarea nu returnează nicio valoare pentru numărul comenzii, trataţi
excepţia cu o rutină de tratare corespunzătoare şi inseraţi în tabela Mesaje mesajul “Atenţie!
In anul 2000 nu s-au încheiat comenzi!”.
c. Dacă se returnează o singura linie, introduceţi în tabela Mesaje numărul comenzii.
d. Trataţi orice altă excepţie cu o rutină de tratare corespunzătoare şi inseraţi în tabela
Mesaje mesajul “A apărut o altă eroare!”.
- 70 -
Baze de date. Limbajul PL/SQL
CAPITOLUL VII.
SUBPROGRAME PL/SQL
VII.1. PROCEDURI
Parametrii utilizaţi în cadrul unei proceduri pot fi de mai multe tipuri:
IN - parametru de intrare; valoarea sa se utilizează în cadrul procedurii fără a putea
fi modificată. Acesta reprezintă tipul implicit.
OUT - parametru de ieşire; acesta primeşte valoare la ieşirea din cadrul procedurii,
valoare ce va putea fi utilizată în mediul apelant.
IN OUT – parametru de intrare/ieşire; valoare sa este utilizată atât ca parametru de
intrare utilizat în procedură cât şi ca parametru de ieşire pentru transmiterea valorii
sale în mediul apelant.
Se impun următoarele concluzii în ceea ce priveşte modul de utililizare a parametrilor:
un parametru IN poate apărea numai în partea dreapta a (:=);
un parametru OUT poate apărea numai în partea stânga a (:=);
un parametru IN OUT poate apărea în ambele părţi ale (:=).
- 71 -
Baze de date. Limbajul PL/SQL
- 72 -
Baze de date. Limbajul PL/SQL
- 73 -
Baze de date. Limbajul PL/SQL
Procedura creată este stocată în baza de date şi poate fi apelată oricând, atât din blocuri
PL/SQL cât şi din alte medii de dezvoltare. Codul sursă şi lista parametrilor pot fi vizualizate
în SQL Developer din meniul din dreapta->lista Procedures:
Utilizarea parametrilor de tip OUT – în acest caz valorile din procedură vor fi
tranferate şi utilizate în afara procedurii.
Exemple:
1. Să se realizeze o procedură ce primeşte ca parametru de intrare id-ul unui angajat
şi returnează numele şi salariul său.
Procedura primeşte ca parametru de tip IN id-ul angajatului şi returnează prin
parametrii de tip OUT numele şi salariul acestuia:
CREATE OR REPLACE PROCEDURE cauta_angajat
(p_id_angajat IN angajati.id_angajat%type,
- 75 -
Baze de date. Limbajul PL/SQL
IS
BEGIN
Select AVG(salariul) into p_sal_mediu from angajati;
END;
/
show errors;
Apelul îl realizăm prin EXECUTE, direct în mediul apelant prin utilizarea unei
variabile de gazdă (de mediu):
VARIABLE v_sal_mediu NUMBER
EXECUTE sal_mediu(:v_sal_mediu)
Print v_sal_mediu
- 77 -
Baze de date. Limbajul PL/SQL
group by p.denumire_produs;
END;
/
show errors;
nume angajati.nume%type;
sal angajati.salariul%type;
BEGIN
Select nume, salariul into nume, sal from angajati
where id_angajat= p_id_angajat;
IF sal<p_sal_mediu then
--se apelează procedura pentru modificarea salariului
cu 15%
MODIFICA_SALARIUL (p_id_angajat, 15);
END IF;
-- se apelează procedura pentru recalcularea noului
salariu mediu
SAL_MEDIU (p_sal_mediu);
End;
/
Show errors
Apelul procedurii într-un bloc anonim se va realiza atât cu un id existent, caz în care
se vor afişa pe rând salariul mediu iniţial, salariul angajatului, salariul mediu actualizat şi apoi
cu un id inexistent care va declanşa excepţia NO_DATA_FOUND tratată în cadrul blocului
apelant:
SET SERVEROUTPUT ON
DECLARE
v_id_angajat angajati.id_angajat%type;
v_nume angajati.nume%type;
v_salariul angajati.salariul%type;
v_sal_mediu number;
BEGIN
--apelul cu id valid =105
v_id_angajat:=105;
--apelul cu id invalid
v_id_angajat:=1230;
CAUTA_ANGAJAT(v_id_angajat, v_nume, v_salariul);
- 79 -
Baze de date. Limbajul PL/SQL
- 80 -
Baze de date. Limbajul PL/SQL
Pentru a vizualiza corpul unei anumite proceduri (de exemplu SAL_MEDIU) utilizăm
tabela virtuală USER_SOURCE:
Select text
From user_source
Where name='SAL_MEDIU' and type='PROCEDURE';
- 81 -
Baze de date. Limbajul PL/SQL
VII.2. FUNCŢII
Spre deosebire de proceduri, la crearea unei funcţii trebuie precizat obligatoriu tipul
returnat de aceasta. O altă caracteritică a funcţiilor este aceea că ele se pot apela atât dintr-un
mediu cu nucleu PL/SQL cât şi direct din SQL, în mod asemănător cu funcţiile 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
-- zona de declarare a variabilelor utilizate in
subprogram
-- nu se utilizeaza DECLARE
BEGIN
----
RETURN VALOARE;
[EXCEPTION]
------
END [NUME_FUNCTIE];
Exemple:
1. Să se creeze o funcţie care returnează true, respectiv false dacă un angajat al cărui
ID este primit ca parametru are salariul mai mare, respectiv mai mic sau egal cu salariul
mediu şi null în cazul în care angajatul nu există.
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
- 82 -
Baze de date. Limbajul PL/SQL
return NULL;
end;
/
show errors
- 83 -
Baze de date. Limbajul PL/SQL
Apelul funcţiei se poate face dintr-un bloc PL/SQL. Vom apela mai întâi procedura
SAL_MEDIU pentru a returna salariul mediu, apoi vom apela funcţia creată cu 3 valori
pentru ID_angajat pentru a verifica execuţia 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
- 84 -
Baze de date. Limbajul PL/SQL
BEGIN
s := 0;
mesaj:= null;
IF length(pcnp) != 13 THEN
mesaj := 'CNP nu are 13 caractere';
ELSE
BEGIN
FOR i IN 1..12 LOOP
BEGIN
IF substr(pcnp,i,1) IN
('0','1','2','3','4','5','6','7','8','9') THEN
s := s + to_number(substr(ponderi,i,1)) *
to_number(substr(pcnp,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
cc := to_char(mod_11);
END IF;
IF cc = substr(pcnp,13,1) then
mesaj := ‘CNP Corect’;
ELSE
mesaj := 'Cifra de control eronata(CNP eronat)';
END IF;
END;
END IF;
RETURN mesaj;
END cc_cnp;
/
Apelul funcţiei se va realiza într-o interogare care afişează id_ul unui produs,
cantitatea, preţul şi TVA-ul acestuia.
- 86 -
Baze de date. Limbajul PL/SQL
Aceste funcţii pot fi utilizate in toate clauzele SELECT sau în 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;
Observaţii!
1. Funcţiile utilizate în expresii SQL trebuie să accepte doar parametri de tip IN şi să
returneze numai tipuri de date specifice SQL.
2. Funcţiile apelate în cadrul expresiilor SQL pot fi conţinute în pachete de
subprograme.
3. Funcţiile nu trebuie să conţină comenzi DML (update, delete, insert) sau comenzi
DDL sau pentru controlul tranzacţiilor (commit, rollback) şi nici nu trebuie sa apeleze alte
subprograme care sa încalce aceste restricţii. În cazul în care aceste condiţii nu sunt respectate
se va genera o eroare, aşa cum se poate observa din exemplul următor:
- 87 -
Baze de date. Limbajul PL/SQL
BEGIN
INSERT INTO produse (id_produs, denumire_produs,
pret_min)values (1, 'cafea', pret_min);
RETURN (pret_min+100);
END;
/
In ciuda faptului că funcţia se va crea fără probleme, putând fi apelată într-un bloc
PL/SQL, apelul său în instructiuni SQL va genera eroare precizată:
UPDATE produse
SET pret_min= produs_nou(2000)
WHERE id_produs=2245;
- 88 -
Baze de date. Limbajul PL/SQL
EXERCIŢII PROPUSE
- 89 -
Baze de date. Limbajul PL/SQL
CAPITOLUL VIII.
PACHETE DE SUBPROGRAME
Pachetele de subprograme grupează variabile, funcţii, proceduri, tipuri de date
PL/SQL care, de obicei, sunt corelate logic. Pachetele sunt formate din 2 secţiuni: specificaţia
pachetului care este o zonă publică în care sunt definiţiile subprogramelor puclice şi
declaraţii de variabile şi excepţii publice şi corpul pachetului care este o zonă privată în care
este implementat codul sursă al tuturor subprogramelor. In această zonă pot apărea şi
proceduri sau funcţii private, utilizate pentru diferite prelucrări interne ale pachetului.
Exemple:
1. Să se realizeze un pachet care să conţină o funcţie pentru căutarea unui client, o
funcţie pentru căutarea unui produs, o procedură pentru introducerea unei comenzi noi, o
procedură de actualizare a unei comenzi şi o funcţie pentru ştergerea 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;
- 90 -
Baze de date. Limbajul PL/SQL
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);
procedure actualizeaza_comanda
(p_nr_comanda comenzi.nr_comanda%type,
p_id_produs rand_comenzi.id_produs%type,
p_cantitate rand_comenzi.cantitate%type);
function sterge_comanda
(p_nr_comanda comenzi.nr_comanda%type)
return boolean;
comanda_inexistenta exception;
END;
/
Show errors
create or replace
PACKAGE BODY gestiune_comenzi IS
- 91 -
Baze de date. Limbajul PL/SQL
function cauta_client
(p_nume_client clienti.nume_client%type)
return number
is
v_id_client clienti.id_client%type;
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;
function cauta_produs
(p_denumire_produs produse.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
when no_data_found then
return null;
when too_many_rows then
return null;
end cauta_produs;
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)
- 92 -
Baze de date. Limbajul PL/SQL
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;
BEGIN
dbms_output.put_line('Se executa pachetul!');
- 93 -
Baze de date. Limbajul PL/SQL
EXCEPTION
when comanda_inexistenta then
dbms_output.put_line ('Nu se poate procesa comanda cu
acest numar! ');
end gestiune_comenzi;
/
Show errors
Corpul pachetului se va stoca în baza de date, acest lucru putând fi vizualizat din
meniul din stânga->Packages: sub denumirea pachetului GESTIUNE_COMENZI apare şi
corpul acestuia.
Apelul procedurilor / funcţiilor din cadrul pachetului se poate face în mod asemănător
cu cel al subprogramelor, cu menţiunea că numele acestora va fi prefixat cu numele
pachetului din care fac parte:
declare
v_id_client number;
v_id_produs number;
begin
v_id_client:=gestiune_comenzi.CAUTA_CLIENT(initcap('&num
e_client'));
if v_id_client is null then
dbms_output.put_line ('Nu exista acest client!');
else
v_id_produs:=gestiune_comenzi.cauta_produs('&denumire');
if v_id_produs is null then
dbms_output.put_line ('Nu exista acest produs!');
- 94 -
Baze de date. Limbajul PL/SQL
else
gestiune_comenzi.introduce_comanda(&nr_comanda,
v_id_client, v_id_produs, &pret, &cantitate);
end if;
end if;
end;
/
- 95 -
Baze de date. Limbajul PL/SQL
- 96 -
Baze de date. Limbajul PL/SQL
/
Show errors
Corpul pachetului:
create or replace
package body gestiune_angajati is
end gestiune_angajati;
/
Show errors
- 97 -
Baze de date. Limbajul PL/SQL
rollback;
Corpul pachetului:
CREATE OR REPLACE PACKAGE BODY bonus_angajati
IS
-- functie urmatoare este privata
FUNCTION validare_bonus (p_bonus NUMBER)
RETURN BOOLEAN
IS
- 99 -
Baze de date. Limbajul PL/SQL
v_max_bonus angajati.comision%type;
BEGIN
SELECT MAX(comision) INTO v_max_bonus
FROM angajati;
RETURN (p_bonus BETWEEN 0.0 AND v_max_bonus);
END validare_bonus;
set serveroutput on
--primul apel va declansa eroarea BONUS_INVALID
EXECUTE bonus_angajati.modifica_bonus(0.95);
-- al doilea apel va modifica valoarea comisionului la 15%
EXECUTE bonus_angajati.modifica_bonus (0.15);
BEGIN
DBMS_OUTPUT.PUT_LINE('Comisionul este: '||
bonus_angajati.bonus_standard);
END;
/
- 100 -
Baze de date. Limbajul PL/SQL
create or replace
PACKAGE BODY gestiune_functii
IS
PROCEDURE adaugare_functie (p_id_functie varchar2,
p_denumire varchar2, p_sal_min number:=2000, p_sal_max
number :=10000)
IS
BEGIN
- 101 -
Baze de date. Limbajul PL/SQL
- 102 -
Baze de date. Limbajul PL/SQL
- 103 -
Baze de date. Limbajul PL/SQL
- 104 -
Baze de date. Limbajul PL/SQL
EXERCIŢII PROPUSE
- 105 -
Baze de date. Limbajul PL/SQL
CAPITOLUL IX.
DECLANŞATORI
Exemplu:
Să se realizeze un trigger care se declanşează înaintea operaţiilor de adăugare în
tabela PRODUSE şi va afişa mesajul: Operaţia s-a executat cu succes!.
Declanşatorul va fi în acest caz la nivel de tabelă şi se va executa înaintea operaţiilor
de adăugare (BEFORE INSERT):
CREATE OR REPLACE TRIGGER produse_trig
BEFORE INSERT ON produse
BEGIN
dbms_output.put_line('Operatia s-a executat cu
succes!');
END;
/
show errors trigger produse_trig
Declanşatorul se va stoca în formă compilată în schema bazei de date, iar codul sursă
poate fi vizualizat şi modificat din meniul stânga - > triggers:
Exemplu:
Să se realizeze un trigger care se declanşează la operaţiile de INSERT, DELETE sau
UPDATE pe tabela Produse. In tabela TEMP_LOG se introduce tipul operaţiei, utilizatorul
- 107 -
Baze de date. Limbajul PL/SQL
-- se realizează declanşatorul
CREATE OR REPLACE TRIGGER produse_trig_log
BEFORE INSERT or UPDATE or DELETE on produse
DECLARE
v_tip temp_log.tip%TYPE;
BEGIN
case
when INSERTING then v_tip :='I';
when UPDATING then v_tip:='U';
ELSE v_tip :='D';
END case;
INSERT INTO temp_log(tip, utilizator, data) VALUES
(v_tip, user, sysdate);
END;
/
show errors trigger produse_trig_log
- 108 -
Baze de date. Limbajul PL/SQL
Observăm din figura de mai sus că se va afişa şi mesajul ‘Operatia s-a executat cu
succes!’, acest mesaj fiind afişat la execuţia declanşatorului PRODUSE_TRIG. În acest caz se
declanşează ambii triggeri.
- 109 -
Baze de date. Limbajul PL/SQL
Exemple:
1. Să se realizeze un trigger care se execută pentru operaţiile INSERT sau UPDATE şi
care nu permite depăşirea unei limite maxime (coloana SALARIU_MAX din tabela FUNCŢII)
a salariului unui angajat pentru o anumită funcţie:
Pentru a împiedica execuţia unei operaţii de manipulare în corpul trigger-ului se va
declanşa o excepţie definită de utilizator cu ajutorul comenzilor RAISE sau
RAISE_APPLICATION_ERROR.
CREATE OR REPLACE TRIGGER restrict_salariul
BEFORE INSERT or UPDATE on angajati
FOR EACH ROW
DECLARE
v_sal_max number;
BEGIN
select salariu_max into v_sal_max from functii where
id_functie= :new.id_functie;
IF :new.salariul>v_sal_max then
RAISE_APPLICATION_ERROR (-20202, 'Nu se poate depasi
salariul maxim pentru functia data');
end if;
END;
/
show errors trigger restrict_salariul
- 110 -
Baze de date. Limbajul PL/SQL
- 111 -
Baze de date. Limbajul PL/SQL
- 112 -
Baze de date. Limbajul PL/SQL
if inserting then
insert into clienti (id_client, prenume_client,
nume_client, limita_credit) values (:new.id_client,
:new.prenume_client, :new.nume_client,
:new.limita_credit);
insert into comenzi (nr_comanda, data, id_client)
values (:new.nr_comanda, :new.data, :new.id_client);
end;
/
show errors trigger comenzi_trigger;
- 113 -
Baze de date. Limbajul PL/SQL
update clienti_v
set nume_client='Popescu'
where id_client=20;
select * from clienti where id_client in (10, 20);
Când un declanşator este creat, codul sursă este stocat în dicţionarul de date şi poate fi
vizualizat din tabela virtuală user_triggers.
Exemplu:
Să se vizulizeze toţi declanşatorii asociaţi tabelei PRODUSE:
SELECT trigger_type, trigger_name, triggering_event
FROM user_triggers
WHERE table_name='PRODUSE';
- 114 -
Baze de date. Limbajul PL/SQL
Gestiunea declanşatorilor
Un declanşator poate fi dezactivat sau activat pentru realizarea unor operaţii mai rapid,
însă fără a încălca restricţiile la care se referă declanşatorul respectiv. Comenzile sunt:
ALTER TRIGGER nume_trigger DISABLE|ENABLE;
Sau:
ALTER TABLE nume_tabela
DISABLE|ENABLE ALL TRIGGERS;
Exemplu:
Să se dezactiveze/activeze toţi declanşatorii tabelei produse:
ALTER TABLE produse
DISABLE ALL TRIGGERS;
- 115 -
Baze de date. Limbajul PL/SQL
- 116 -
Baze de date. Limbajul PL/SQL
Acest tip de declanşatori este utilizat pentru controlul unor operaţii de definire a
datelor, conectare/deconectarea la nivelul schemei sau pentru oprirea/pornirea bazei de date.
Comenzile DDL care declansează triggerul sunt cele aplicate asupra următoarelor
obiecte: CLUSTER, INDEX, PACKAGE, PROCEDURE, ROLE, SEQUENCE, SYNONYM,
TABLE, TABLESPACE, TRIGGER, TYPE, VIEW, USER.
Exemplu:
Să se realizeze un declanşator pentru monitorizarea operaţiilor de definire a datelor
realizate la nivelul schemei curente. Se va utiliza tabela TEMP_LOG în care se vor adăuga
informaţiile referitoare la operaţii:
CREATE OR REPLACE TRIGGER DDL_OP_TRIG
AFTER CREATE OR ALTER OR DROP
ON SCHEMA
BEGIN
INSERT INTO TEMP_LOG VALUES ('DDL', USER, SYSDATE);
END;
/
Testarea declanşatorului:
create view ANG_V as select * from ANGAJATI;
select * from TEMP_LOG;
- 117 -
Baze de date. Limbajul PL/SQL
Exemplu:
Să se creeze doi declanşatori care să monitorizeze conectarea/deconectarea
utilizatorului curent:
CREATE OR REPLACE TRIGGER CONECT_TRIG
AFTER LOGON
ON SCHEMA
BEGIN
INSERT INTO TEMP_LOG VALUES('CON' , USER, SYSDATE);
END;
/
- 118 -
Baze de date. Limbajul PL/SQL
EXERCIŢII PROPUSE
- 119 -
Baze de date. Limbajul PL/SQL
CAPITOLUL X.
REALIZAREA DE VIDEOFORMATE ŞŞII RAPOARTE
X.1. VIDEOFORMATE
- 120 -
Baze de date. Limbajul PL/SQL
Pas 2. Pentru alegerea tabelei se apasă butonul Browse şi se conectează la baza de date
Oracle, specificând utilizatorul, parola şi şirul de conectare.
- 121 -
Baze de date. Limbajul PL/SQL
Pas 4. Din lista de coloane din tabelă (Available Columns) se includ în lista de articole
din blocul de date, informaţiile cu care va lucra videoformatul (nu neapărat cele care vor fi
vizualizate).
Pas 5. Odată parcurşi toţi paşii din Data Block Wizard se poate trece automat la un alt
asistent de tip Wizard pentru vizualizarea datelor (Layout Wizard). Se determină tipul
suprafeţei pe care vor fi afişate elementele formularului (canvas): Content, Stacked, Vertical
Toolbar, Horizontal Toolbar sau Tab.
- 123 -
Baze de date. Limbajul PL/SQL
Pas 8. Se alege stilul de afişare, care poate fi de tip formular (Form) sau de tip tabelar
(Tabular).
Pas 10. Se intră din nou în asistentul Wizard pentru crearea blocurilor de date.
- 125 -
Baze de date. Limbajul PL/SQL
Pas 11. Se reiau paşii 1-4 cu alegerea tabelei Angajati. Se creează legătura între cele
două blocuri de date (Create Relationship), bazată pe o condiţie de tip join.
- 126 -
Baze de date. Limbajul PL/SQL
- 127 -
Baze de date. Limbajul PL/SQL
- 128 -
Baze de date. Limbajul PL/SQL
- 129 -
Baze de date. Limbajul PL/SQL
- 130 -
Baze de date. Limbajul PL/SQL
- 131 -
Baze de date. Limbajul PL/SQL
X.2. RAPOARTE
Drept exemplu, vom proiecta raportul Angajati în Oracle Reports Builder, utilizând
asistentul de tip Report Wizard.
- 132 -
Baze de date. Limbajul PL/SQL
Pas 3. Pentru selectarea tabelei este necesară conectarea la baza de date. Se scrie fraza
SELECT, se preia dintr-o cerere salvată sau se construieşte cu ajutorul componentei Query
Builder. În cadrul Query Builder se includ tabelele din care se preiau datele, în cazul nostru:
Angajati, Departamente.
- 133 -
Baze de date. Limbajul PL/SQL
Pas 5. Cererea creată poate fi salvată pentru o utilizare ulterioară. Se revine în Report
Wizard şi se vizualizează interogarea creată.
- 134 -
Baze de date. Limbajul PL/SQL
Pas 8. Se pot calcula totaluri de tipul: sumă, medie aritmetică, număr de înregistrări,
valoarea minimă, valoarea maximă, procente din total. În cazul raportului exemplificat ne
interesează numarul de angajaţi din fiecare departament:
- 135 -
Baze de date. Limbajul PL/SQL
Pas 10. Pentru stabilirea culorilor folosite, a caracterelor, a tipului liniilor sau a
structurii de aranjare a informaţiei se poate prelua un şablon (template) predefinit în Oracle
sau unul creat de utilizator. Se recomandă crearea unui şablon pe baza căruia să se genereze
toate rapoartele:
- 136 -
Baze de date. Limbajul PL/SQL
Raportul Total angajati pe departamente generat este cel din figura 112. Ulterior
realizării raportului cu ajutorul Report Wizard câmpurile pot fi rearanjate şi reformatate.
- 137 -
Baze de date. Limbajul PL/SQL
declare
report_id Report_Object;
ReportServerJob VARCHAR2(100);
vc_rep_status VARCHAR2(100);
vjob_id VARCHAR2(100);
repsvr varchar2(21) := 'rwmaster';
BEGIN
report_id:= find_report_object('REPORT19');
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_COMM_MODE,S
YNCHRONOUS);
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_DESTYPE,fil
e);
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_DESNAME,'rp
out1.html');
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_DESFORMAT,'
HTMLCSS');
- 138 -
Baze de date. Limbajul PL/SQL
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_SERVER,reps
vr);
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_OTHER,
'paramform=no');
ReportServerJob:=run_report_object(report_id);
vjob_id := substr(reportserverjob,length(repsvr)+2);
vc_rep_status := REPORT_OBJECT_STATUS(ReportServerJob);
WHILE vc_rep_status in
('RUNNING','OPENING_REPORT','ENQUEUED', null)
LOOP
vc_rep_status:=REPORT_OBJECT_STATUS(ReportServerJob);
END LOOP;
IF vc_rep_status='FINISHED' THEN
WEB.SHOW_DOCUMENT('/reports/rwservlet/getjobid'||
vjob_id ||'?server='||repsvr,'_blank');
ELSE
message ('Report failed with error message '||
vc_rep_status);
END IF;
END;
Pas 4. Dacă nu avem un IP fix se recomandă adăugarea unei interfeţe de tip Microsoft
Loopback cu un astfel de IP:
Control Panel->Add Hardware->Add a new Hardware device-> Install the hardware
manually->Network Adapter->Microsoft->Microsoft Loopback Adapter.
Asignăm acestei interfeţe adresa 192.168.1.1
Ar trebui să apară mesajul care să specifice faptul că pornirea s-a realizat cu succes. Nu
închideţi fereastra respectivă.
Putem verifica dacă serverul funcţioneză rulând următoarea comandă:
rwdiag -findall
- 140 -
Baze de date. Limbajul PL/SQL
Apăsăm Lansare Raport şi dacă nu exista vreun Pop-Up Blocker activ ar trebui să se deschidă
un nou tab cu raportul apelat:
- 141 -
Baze de date. Limbajul PL/SQL
Observaţie: Dacă dorim transmiterea unui parametru între videoformat şi raport urmăm şi
următorii doi paşi:
- 142 -
Baze de date. Limbajul PL/SQL
declare
report_id Report_Object;
ReportServerJob VARCHAR2(100);
vc_rep_status VARCHAR2(100);
vjob_id VARCHAR2(100);
pl_id ParamList;
repsvr varchar2(21) := 'rwmaster';
BEGIN
report_id:= find_report_object('REPORT19');
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_COMM_MODE,S
YNCHRONOUS);
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_DESTYPE,fil
e);
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_DESNAME,'rp
out1.html');
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_DESFORMAT,'
HTMLCSS');
SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_SERVER,reps
vr);
--SE COMENTEAZĂ SAU SE ŞTERGE LINIA URMĂTOARE
--SET_REPORT_OBJECT_PROPERTY(report_id,REPORT_OTHER,
'paramform=no');
pl_id := get_parameter_list('TEMP');
if id_null(pl_id) then pl_id :=
create_parameter_list('TEMP');
else destroy_parameter_list('TEMP');
pl_id := create_parameter_list('TEMP');
end if;
Add_Parameter('TEMP','PID_DEP',TEXT_PARAMETER,:departam
ente.id_departament);
ReportServerJob:=run_report_object(report_id,'TEMP');
vjob_id := substr(reportserverjob,length(repsvr)+2);
vc_rep_status := REPORT_OBJECT_STATUS(ReportServerJob);
WHILE vc_rep_status in
('RUNNING','OPENING_REPORT','ENQUEUED', null)
LOOP
vc_rep_status:=REPORT_OBJECT_STATUS(ReportServerJob);
END LOOP;
- 143 -
Baze de date. Limbajul PL/SQL
IF vc_rep_status='FINISHED' THEN
WEB.SHOW_DOCUMENT('/reports/rwservlet/getjobid'||
vjob_id ||'?server='||repsvr,'_blank');
ELSE
message ('Report failed with error message '||
vc_rep_status);
END IF;
END;
Când apăsăm pe butonul Lansare Raport se va rula raportul doar pentru departamentul
angajatului curent din videoformat:
EXERCIŢII PROPUSE
- 144 -
Baze de date. Limbajul PL/SQL
CAPITOLUL XI.
EXERCIŢII RECAPITULATIVE
CERINŢE PROPUSE
1. Creaţi un bloc PL/SQL care să afişeze codul şi denumirea pentru departamentul din
localitatea al cărei cod (id_produs) este precizat (fie citit de la tastatură, fie dat drept
valoare iniţială unei variabile locale). Trataţi excepţiile predefinite care pot apărea.
3. Creaţi un bloc PL/SQL care să afişeze informaţii despre angajaţii care nu au încheiat
comenzi. Sortaţi descendent în funcţie de numele angajaţilor.
4. Creaţi un bloc PL/SQL prin care se dublează preţul produsului (pret_lista) al cărui cod
este 3169. În cazul în care acesta nu există (comanda UPDATE nu realizează nicio
modificare) se invocă o excepţie (pe care o definiţi în mod explicit).
5. Creaţi un bloc PL/SQL prin care să se dubleze preţul (pret_lista) pentru produsele din
categoriile hardware2 şi hardware3. În cazul în care actualizarea se realizează cu succes,
afişaţi numărul de înregistrări modificate, iar în caz contrar invocaţi o excepţie!
6. Creaţi un bloc PL/SQL care să afişeze informaţii despre clienţii care au încheiat comenzi
în anul 1999.
8. Într-un bloc anonim, citiţi de la tastatură id-ul unui produs şi pentru acesta afişaţi toate
comenzile corespunzătoare.
9. Utilizând un cursor explicit, afişaţi toţi salariaţii din tabela ANGAJAŢI care lucrează într-
un anumit departament (id_departament) primit ca parametru.
10. Să se afişeze printr-o funcţie informaţii despre clienţii de sex masculin. Funcţia să
returneze numărul acestora. Să se trateze şi o excepţie definită de utilizator. Să se apeleze
funcţia.
11. Afişati folosind un cursor toate comenzile care au fost date în ultimele 3 luni.
12. Construiţi un trigger care să nu permită existenţa unei comenzi cu o cantitate comandată
mai mare de 1000.
- 145 -
Baze de date. Limbajul PL/SQL
13. Într-un bloc PL/SQL ştergeţi toate produsele care au pret_lista mai mare de 1500.
Folosind o excepţie trataţi cazul în care nu exista nici un produs care îndeplineşte aceasta
condiţie, afişând un mesaj.
14. Să se afişeze, folosind un cursor, de câte ori a fost comandat fiecare produs care face parte
din categoria hardware2.
15. Construiţi un trigger care să nu permită adăugarea unui nou client cu o limită de credit mai
mică de 50.
16. Să se afişeze cele mai mari trei comenzi, ca valoare, date de clienţii de sex M, utilizând un
cursor.
18. Creaţi un bloc PL/SQL în care să utilizaţi un cursor explicit pentru a afişa valoarea
fiecărei comenzi şi data la care aceasta a fost încheiată.
20. Realizaţi o procedură care primeşte ca parametru id_functie şi, pentru fiecare angajat cu
această funcţie, realizează o mărire de salariu cu 10%. Procedura va afişa numărul de
angajaţi pentru care s-a actualizat salariul.
- 146 -
Baze de date. Limbajul PL/SQL
1. Limbajul PL/SQL:
a) este un limbaj de programare procedural
b) funcţionează stil compilator
c) funcţionează stil interpretor
d) este un limbaj de programare universal
e) este un limbaj de descriere a datelor.
*
Notă: Întrebările pot avea oricâte răspunsuri corecte!
- 147 -
Baze de date. Limbajul PL/SQL
- 148 -
Baze de date. Limbajul PL/SQL
- 149 -
Baze de date. Limbajul PL/SQL
BEGIN
UPDATE clienti SET limita_credit=2500 WHERE id_client=p_id;
END;
/
BEGIN
OPEN c1;
FOR i IN 1 .. 4 LOOP
FETCH c1 INTO v_id, v_nume;
INSERT INTO mesaje VALUES(v_id, v_nume);
END LOOP;
CLOSE c1;
END;
/
- 150 -
Baze de date. Limbajul PL/SQL
17. Un SGBD:
a) este un limbaj de regăsire
b) deţine întotdeauna SQL
c) este componenta software a unui sistem de baze de date
d) este o bază de date
e) asigură gestiunea tuturor fişierelor de pe un sistem de calcul.
- 151 -
Baze de date. Limbajul PL/SQL
28. Care din următoarele noţiuni nu sunt folosite în teoria relaţională, fiind specifice
SGBDR:
a) relaţie
b) tabelă
c) tuplu
d) coloană
e) atribut.
- 152 -
Baze de date. Limbajul PL/SQL
e) administrarea datelor.
- 154 -
Baze de date. Limbajul PL/SQL
SET SERVEROUTPUT ON
BEGIN
UPDATE comenzi SET data=TRUNC(SYSDATE);
DBMS_OUTPUT.PUT_LINE (SQL%ROWCOUNT);
ROLLBACK;
EXCEPTION
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Eroare');
END;
/
Presupunem că tabela Comenzi are 5 înregistrări. În aceste condiţii, blocul PL/SQL
afişează:
a) 5
b) 'Eroare'
c) nu afişează nimic
d) dă eroare la compilare
e) 0.
40. Pentru un cursor folosit într-un ciclu FOR se vor elimina operaţiile explicite de:
a) declarare a cursorului
b) deschidere a cursorului
c) încărcare de date din linia curentă din cursor în variabile
d) închidere a cursorului
e) toate operaţiile de mai sus.
- 155 -
Baze de date. Limbajul PL/SQL
- 156 -
Baze de date. Limbajul PL/SQL
a) funcţii
b) proceduri
c) tabele virtuale
d) pachete
e) declanşatori.
52. Care din următoarele concepte din teoria relaţională şi teoria bazelor de date sunt
similare:
a) relaţie şi colecţie de date
b) linie şi câmp
c) atribut şi linie
d) atribut şi caracteristică
e) tuplu şi familie de caracteristici.
- 157 -
Baze de date. Limbajul PL/SQL
d) %TYPE
e) %ROWCOUNT
54. Care din următoarele caracteristici constituie reguli ale lui Codd pentru a defini
SGBDR:
a) independenţa fizică a datelor
b) independenţa logică a datelor
c) reprezentarea logică a datelor
d) garantarea accesului la date
e) restricţionarea accesului la date
59. Un SGBD:
a) este un limbaj de regăsire
b) deţine întotdeauna SQL
c) este componenta software a unui sistem de baze de date
d) este o bază de date
e) asigură gestiunea tuturor fişierelor de pe un sistem de calcul.
- 158 -
Baze de date. Limbajul PL/SQL
BEGIN
RAISE_APPLICATION_ERROR(-20202,'nu aveţi voie să micşoraţi limita de
credit!');
END;
/
Care afirmaţii sunt eronate:
a) în secvenţă se specifică evenimentul care declanşează trigger-ul
b) în secvenţă se specifică tabela asociată trigger-ului
c) în secvenţă se specifică de câte ori se execută trigger-ul
d) calificatorii OLD şi NEW trebuiau prefixaţi prin „:”
e) în secvenţă se specifică momentul declanşării trigger-ului.
- 159 -
Baze de date. Limbajul PL/SQL
- 160 -
Baze de date. Limbajul PL/SQL
- 161 -
Baze de date. Limbajul PL/SQL
- 162 -
Baze de date. Limbajul PL/SQL
1. a, b; 2. d; 3. a, d; 4. a, b, c, d, e; 5. a, d; 6. a, d, e; 7. a, b; 8. a, d, e; 9. a, c; 10. a, d, e; 11. c,
d; 12. a, c, d, e; 13. a, d; 14. a, c, d; 15. d; 16. d; 17. c; 18. b, d; 19. b, d; 20. a, b; 21. b, e; 22. a,
b, e; 23. a, b, c; 24. b, d; 25. a, c, d, e; 26. a, b; 27. e; 28. b, d; 29. a, b, e; 30. b; 31. a, b, d; 32.
41. a, b, c, d; 42. c; 43. a, c, d; 44. b, c; 45. a; 46. a; 47. c; 48. a, b, d, e; 49. a, c, d, e; 50. d; 51.
a, b, e; 52. a, d, e; 53. a, b, c; 54. a, b, c, d; 55. e; 56. a; 57. b, c, e; 58. a, d; 59. c; 60. d; 61. a,
c; 62. a; 63. c; 64. a, c, e; 65. a, b, c, e; 66. a, c, e; 67. a; 68. a, d; 69. a, d, e; 70. e; 71. a, b, c, d,
e; 72. a; 73. e.
- 163 -
Baze de date. Limbajul PL/SQL
4. Specificaţi care dintre următorii operatori nu face parte din grupa operatorilor standard
din algebra relaţională:
a) selecţia
b) proiecţia
c) joncţiunea
d) produsul cartezian
e) cuantificatorul existenţial
5. Specificaţi care afirmaţie este una dintre funcţiile de bază ale unui sistem de gestiune a
bazelor de date (SGBD):
a) manipularea programelor
b) administrarea reţelei de calculatoare
c) proiectarea bazei de date
d) manipularea datelor
e) utilizarea limbajelor universale
†
Versiunea completă a setului de subiecte extras în sesiunea Septembrie 2008 se găseşte integral în
cartea: Admitere 2009 - Masterate de aprofundare editura ASE, 2009, ISBN: 978-606-505-138-6
Notă: Întrebările au un singur răspuns corect!
- 164 -
Baze de date. Limbajul PL/SQL
6. Specificaţi care dintre afirmaţiile următoare nu face parte din regulile lui Codd pentru
caracterizarea sistemelor de gestiune a bazelor de date relaţionale:
a) independenţa conceptuală a datelor
b) actualizarea tabelelor virtuale
c) independenţa fizică a datelor
d) distribuirea geografică a datelor
e) garantarea accesului la date
8. Care dintre următoarele afirmaţii ar putea fi considerată drept obiectiv al unui sistem
de gestiune a bazelor de date (SGBD):
a) asigurarea integrităţii datelor
b) maximizarea timpului de acces
c) independenţa conceptuală a datelor
d) creşterea complexităţii sistemului informatic
e) înlăturarea anomaliilor de ordin logic
- 165 -
Baze de date. Limbajul PL/SQL
şi
departamente ( id_departament number (3), denumire_dep varchar2(20) ),
Tabela angajati conţine informaţii şi despre angajaţi care nu aparţin unor
departamente, iar tabela departamente poate conţine şi departamente noi, care nu au angajaţi.
Se consideră următoarea comandă SQL:
SELECT a.nume_angajat, d.denumire_dep
FROM angajati a FULL OUTER JOIN departamente d
ON a.id_departament = d.id_departament;
Specificaţi care dintre comenzile următoare specifice SQL-Oracle afişează acelaşi
rezultat cu cea de mai sus:
a) SELECT a.nume_angajat, d.denumire_dep
FROM angajati a, departamente d
WHERE a.id_departament (+) = d.id_departament;
b) SELECT a.nume_angajat, d.denumire_dep
FROM angajati a, departamente d
WHERE a.id_departament = d.id_departament (+);
c) SELECT a.nume_angajat, d.denumire_dep
FROM angajati a, departamente d
WHERE a.id_departament (+) = d.id_departament (+);
d) SELECT a.nume_angajat, d.denumire_dep
FROM angajati a, departamente d
WHERE a.id_departament (+) = d.id_departament
UNION
SELECT a.nume_angajat, d.denumire_dep
FROM angajati a, departamente d
WHERE a.id_departament = d.id_departament (+);
e) SELECT a.nume_angajat, d.denumire_dep
FROM angajati a, departamente d;
- 166 -
Baze de date. Limbajul PL/SQL
- 167 -
Baze de date. Limbajul PL/SQL
RĂSPUNSURI ÎNTREBĂRI
- 168 -
Baze de date. Limbajul PL/SQL
BIBLIOGRAFIE
[ORAPL] Oracle Database PL/SQL User’s Guide and Reference, Oracle Corporation
[LUBA08] Ion Lungu, Adela Bâra, Mihai Andronie - Administrarea bazelor de date, editura
ASE, Bucureşti, 2008, ISBN: 978-606-505-071-6
[VELU07] Manole Velicanu - Baze de date prin exemple, editura ASE, Bucureşti, 2007,
ISBN: 978-973-594-918-1
[LUNG05] Ion Lungu - Baze de date Oracle. Limbajul SQL, editura ASE, Bucureşti, 2005,
ISBN: 973-594-684-X
[VELU03] Manole Velicanu, Ion Lungu, Mihaela Munteanu, Simona Ionescu – Sisteme de
baze de date – teorie şi practică, editura Petrion, Bucureşti, 2003, ISBN: 973-947-
070-X
[****] Suport electronic pentru seminarul de SGBD Oracle, CSIE-ASE, anul universitar
2008-2009: http://bd.ase.ro/
- 169 -
Baze de date. Limbajul PL/SQL
ANE X E
spool ru.log
prompt
prompt Creating table REGIUNI
prompt ======================
prompt
create table REGIUNI
(
ID_REGIUNE NUMBER,
DENUMIRE_REGIUNE VARCHAR2(25)
)
;
alter table REGIUNI
add constraint ID_REGIUNE_PK primary key (ID_REGIUNE);
alter table REGIUNI
add constraint ID_REGIUNE_NN
check ("ID_REGIUNE" IS NOT NULL);
prompt
prompt Creating table TARI
prompt ===================
prompt
create table TARI
(
ID_TARA CHAR(2),
DENUMIRE_TARA VARCHAR2(40),
ID_REGIUNE NUMBER,
constraint TARA_C_ID_PK primary key (ID_TARA)
)
organization index;
alter table TARI
add constraint TARA_REG_FK foreign key (ID_REGIUNE)
references REGIUNI (ID_REGIUNE);
alter table TARI
add constraint ID_TARA_NN
check ("ID_TARA" IS NOT NULL);
prompt
prompt Creating table LOCATII
prompt ======================
prompt
create table LOCATII
(
ID_LOCATIE NUMBER(4) not null,
ADRESA VARCHAR2(40),
COD_POSTAL VARCHAR2(12),
ORAS VARCHAR2(30),
ZONA VARCHAR2(25),
ID_TARA CHAR(2)
)
;
alter table LOCATII
add constraint LOC_ID_PK primary key (ID_LOCATIE);
- 170 -
Baze de date. Limbajul PL/SQL
prompt
prompt Creating table DEPARTAMENTE
prompt ===========================
prompt
create table DEPARTAMENTE
(
ID_DEPARTAMENT NUMBER(4) not null,
DENUMIRE_DEPARTAMENT VARCHAR2(30),
ID_MANAGER NUMBER(6),
ID_LOCATIE NUMBER(4)
)
;
alter table DEPARTAMENTE
add constraint DEPT_ID_PK primary key (ID_DEPARTAMENT);
alter table DEPARTAMENTE
add constraint DEPT_LOC_FK foreign key (ID_LOCATIE)
references LOCATII (ID_LOCATIE);
prompt
prompt Creating table FUNCTII
prompt ======================
prompt
create table FUNCTII
(
ID_FUNCTIE VARCHAR2(10) not null,
DENUMIRE_FUNCTIE VARCHAR2(35),
SALARIU_MIN NUMBER(6),
SALARIU_MAX NUMBER(6)
)
;
alter table FUNCTII
add constraint ID_FUNCTIE_PK primary key (ID_FUNCTIE);
alter table FUNCTII
add constraint DEN_FUNCTIE_NN
check ("DENUMIRE_FUNCTIE" IS NOT NULL);
prompt
prompt Creating table ANGAJATI
prompt =======================
prompt
create table ANGAJATI
(
ID_ANGAJAT NUMBER(6) not null,
PRENUME VARCHAR2(20),
NUME VARCHAR2(25),
EMAIL VARCHAR2(25),
TELEFON VARCHAR2(20),
DATA_ANGAJARE DATE,
- 171 -
Baze de date. Limbajul PL/SQL
ID_FUNCTIE VARCHAR2(10),
SALARIUL NUMBER(8,2),
COMISION NUMBER(2,2),
ID_MANAGER NUMBER(6),
ID_DEPARTAMENT NUMBER(4)
)
;
alter table ANGAJATI
add constraint ANG_ID_ANGAJAT_PK primary key (ID_ANGAJAT);
alter table ANGAJATI
add constraint ANG_EMAIL_UK unique (EMAIL);
alter table ANGAJATI
add constraint ANG_DEPT_FK foreign key (ID_DEPARTAMENT)
references DEPARTAMENTE (ID_DEPARTAMENT);
alter table ANGAJATI
add constraint ANG_FUNCTIE_FK foreign key (ID_FUNCTIE)
references FUNCTII (ID_FUNCTIE);
alter table ANGAJATI
add constraint ANG_MANAGER_FK foreign key (ID_MANAGER)
references ANGAJATI (ID_ANGAJAT);
alter table ANGAJATI
add constraint ANG_DATA_ANG_NN
check ("DATA_ANGAJARE" IS NOT NULL);
alter table ANGAJATI
add constraint ANG_EMAIL_NN
check ("EMAIL" IS NOT NULL);
alter table ANGAJATI
add constraint ANG_FUNCTIE_NN
check ("ID_FUNCTIE" IS NOT NULL);
alter table ANGAJATI
add constraint ANG_NUME_NN
check ("NUME" IS NOT NULL);
alter table ANGAJATI
add constraint ANG_SALARIUL_MIN
check (SALARIUL > 0);
create index ANG_DEPARTAMENT_IX on ANGAJATI (ID_DEPARTAMENT);
create index ANG_FUNCTIE_IX on ANGAJATI (ID_FUNCTIE);
create index ANG_MANAGER_IX on ANGAJATI (ID_MANAGER);
create index ANG_NUME_IX on ANGAJATI (NUME, PRENUME);
prompt
prompt Creating table CLIENTI
prompt ======================
prompt
create table CLIENTI
(
ID_CLIENT NUMBER(6) not null,
PRENUME_CLIENT VARCHAR2(20),
NUME_CLIENT VARCHAR2(20),
TELEFON VARCHAR2(20),
LIMITA_CREDIT NUMBER(9,2),
EMAIL_CLIENT VARCHAR2(30),
DATA_NASTERE DATE,
STAREA_CIVILA VARCHAR2(20),
SEX VARCHAR2(1),
NIVEL_VENITURI VARCHAR2(20)
)
- 172 -
Baze de date. Limbajul PL/SQL
;
alter table CLIENTI
add constraint CLIENTI_ID_CLIENT_PK primary key (ID_CLIENT);
alter table CLIENTI
add constraint CLIENTI_LIMITA_CREDIT_MAX
check (LIMITA_CREDIT <= 5000);
alter table CLIENTI
add constraint CL_NUME_NN
check ("NUME_CLIENT" IS NOT NULL);
alter table CLIENTI
add constraint CL_PRENUME_NN
check ("PRENUME_CLIENT" IS NOT NULL);
create index CLIENTI_NUME_IX on CLIENTI (UPPER(NUME_CLIENT),
UPPER(PRENUME_CLIENT));
prompt
prompt Creating table COMENZI
prompt ======================
prompt
create table COMENZI
(
NR_COMANDA NUMBER(12) not null,
DATA TIMESTAMP(6) WITH LOCAL TIME ZONE,
MODALITATE VARCHAR2(8),
ID_CLIENT NUMBER(6),
STARE_COMANDA NUMBER(2),
ID_ANGAJAT NUMBER(6)
)
;
alter table COMENZI
add constraint COMENZI_NR_COMANDA_PK primary key (NR_COMANDA);
alter table COMENZI
add constraint COMENZI_ID_ANGAJAT_FK foreign key (ID_ANGAJAT)
references ANGAJATI (ID_ANGAJAT) on delete set null;
alter table COMENZI
add constraint COMENZI_ID_CLIENT_FK foreign key (ID_CLIENT)
references CLIENTI (ID_CLIENT) on delete set null;
alter table COMENZI
add constraint COMENZI_DATA_NN
check ("DATA" IS NOT NULL);
alter table COMENZI
add constraint COMENZI_ID_CLIENT_NN
check ("ID_CLIENT" IS NOT NULL);
alter table COMENZI
add constraint COMENZI_MOD_CK
check (MODALITATE in ('direct','online'));
create index COMENZI_DATA_IX on COMENZI (DATA);
create index COMENZI_ID_ANGAJAT_IX on COMENZI (ID_ANGAJAT);
create index COMENZI_ID_CLIENT_IX on COMENZI (ID_CLIENT);
prompt
prompt Creating table ISTORIC_FUNCTII
prompt ==============================
prompt
create table ISTORIC_FUNCTII
(
ID_ANGAJAT NUMBER(6),
DATA_INCEPUT DATE,
DATA_SFARSIT DATE,
ID_FUNCTIE VARCHAR2(10),
ID_DEPARTAMENT NUMBER(4)
)
;
- 173 -
Baze de date. Limbajul PL/SQL
prompt
prompt Creating table PRODUSE
prompt ======================
prompt
create table PRODUSE
(
ID_PRODUS NUMBER(6) not null,
DENUMIRE_PRODUS VARCHAR2(50),
DESCRIERE VARCHAR2(2000),
CATEGORIE VARCHAR2(40),
PRET_LISTA NUMBER(8,2),
PRET_MIN NUMBER(8,2)
)
;
alter table PRODUSE
add constraint PRODUSE_ID_PRODUS_PK primary key (ID_PRODUS);
prompt
prompt Creating table RAND_COMENZI
prompt ===========================
prompt
create table RAND_COMENZI
(
NR_COMANDA NUMBER(12) not null,
ID_PRODUS NUMBER(6) not null,
PRET NUMBER(8,2),
CANTITATE NUMBER(8)
)
;
alter table RAND_COMENZI
add constraint PROD_COM_PK primary key (NR_COMANDA, ID_PRODUS);
alter table RAND_COMENZI
- 174 -
Baze de date. Limbajul PL/SQL
spool off
- 175 -
Baze de date. Limbajul PL/SQL
- 177 -
Baze de date. Limbajul PL/SQL
Un program încearcă să se
LOGIN_DENIED 01017 -1017 autentifice cu un utilizator
şi/sau parola greşită.
- 178 -
Baze de date. Limbajul PL/SQL
- 179 -