Sunteți pe pagina 1din 73

Laborator 1

Variante de creare ale unui tabel.

1. VARIANTA FARA DENUMIRI ALE CONSTRANGERILOR

Constrangerile pot fi mentionate atat la nivelul fiecarui camp cat si la sfarsit, dupa precizarea campurilor si a
tipurilor lor de date.
Se observa ca in aceasta situatie denumirile constrangerilor sunt alocate de catre Oracle.

CREATE TABLE clienti_ABC (


CODCL NUMBER (4) PRIMARY KEY CHECK (CODCL>1000),
DENCL VARCHAR2 (20),
LOCALIT VARCHAR2(30) NOT NULL
);

CREATE TABLE clienti_ABC1 (


CODCL NUMBER (4) PRIMARY KEY,
DENCL VARCHAR2 (20),
LOCALIT VARCHAR2(30) NOT NULL,
CHECK (CODCL>1000)
);

CREATE TABLE clienti_ABC2 (


CODCL NUMBER (4),
DENCL VARCHAR2 (20),
LOCALIT VARCHAR2(30) NOT NULL,
PRIMARY KEY (CODCL),
CHECK (CODCL>1000)
);

Varianta de mai sus prin care se precizeaza cheia primara la sfarsit, poate fi utilizata si la crearea unei chei primare
duble.

CREATE TABLE clienti_ABC3 (


CODCL NUMBER (4),
DENCL VARCHAR2 (20),
LOCALIT VARCHAR2(30) NOT NULL,
PRIMARY KEY (CODCL, DENCL),
CHECK (CODCL>1000)
);
2. VARIANTA CU DENUMIRI PERSONALIZATE ALE CONSTRANGERILOR

CREATE TABLE clienti_ABC4 (


CODCL NUMBER (4)
CONSTRAINT pk_codcl_abc4 PRIMARY KEY
CONSTRAINT ck_codcl_abc4 CHECK (CODCL>1000),
DENCL VARCHAR2 (20),
LOCALIT VARCHAR2(30)
CONSTRAINT nn_localit_abc4 NOT NULL
);

De asemenea aceste constrangeri pot fi mentionate si la sfarsit.

CREATE TABLE clienti_ABC5 (


CODCL NUMBER (4),
DENCL VARCHAR2 (20),
LOCALIT VARCHAR2(30) CONSTRAINT nn_localit_abc5 NOT NULL,
CONSTRAINT pk_codcl_abc5 PRIMARY KEY (CODCL),
CONSTRAINT ck_codcl_abc5 CHECK (CODCL>1000)
);

3. PRIMELE INSERARI DE DATE

INSERT INTO clienti_ABC VALUES (1200,'Popescu','Arad');

La al doilea rand de date, in mod intentionat, incalcam unicitatea cheii primare

INSERT INTO clienti_ABC VALUES (1200,'Georgescu','Timisoara');

INSERT INTO clienti_ABC VALUES (800,'Georgescu','Timisoara');

Aici este incalcata regula CHECK

Adaugand date asemanatoare in tabela clienti_ABC4, vom observa ca semnalarea erorilor se face prin denumirile
personalizate.
INSERT INTO clienti_ABC4 VALUES (1200,'Popescu','Arad');

INSERT INTO clienti_ABC4 VALUES (1200,'Georgescu','Timisoara');

INSERT INTO clienti_ABC4 VALUES (800,'Georgescu','Timisoara');


Laborator 2
Creare unei structuri de tabele RELATIONATE.
Inserare de date. Instructiuni DDL si DML.
Instructiunea SELECT.

1. TABELELE INITIALE

CREATE TABLE clienti_DEF (


codcl NUMBER (4) PRIMARY KEY,
dencl VARCHAR2 (20),
localit VARCHAR2(30)
);

CREATE TABLE clienti_DEF1 (


codcl NUMBER (4),
dencl VARCHAR2 (20),
localit VARCHAR2(30),
CONSTRAINT pk_cl_def1_codcl PRIMARY KEY (codcl)
);

2. CREAREA UNUI TABEL SUBORDONAT TABELULUI clienti_DEF

La fiecare din cele 2 tabele clienti este creat cate un tabel facturi, punand in evidenta modul de pozitionare si de
implementare al constrangerilor.
Constrangerile si legatura de subordonare se pot evidentia utilizand schema modelului de date.

CREATE TABLE facturi_DEF (


nrfact NUMBER (8) PRIMARY KEY,
datafact DATE,
codcl NUMBER (4) REFERENCES clienti_DEF (codcl),
valoare NUMBER(10)
);

CREATE TABLE facturi_DEF1 (


nrfact NUMBER (8),
datafact DATE,
codcl NUMBER (4),
valoare NUMBER(10),
CONSTRAINT pk_fact_def1_nrfact PRIMARY KEY (nrfact),
CONSTRAINT fk_fact_def1_codcl FOREIGN KEY (codcl) REFERENCES clienti_DEF1 (codcl)
);
3. INSERARE DE DATE

Popularea tabelelor cu date se face se realizeaza in ordinea „parinti”  „copii”, pentru a respecta integritatea
referentiala.

Trebuie respectata ordinea si tipul campurilor.

INSERT INTO clienti_DEF VALUES (10,'Popescu','Arad');


INSERT INTO clienti_DEF VALUES (20,'Georgescu','Timisoara');
INSERT INTO clienti_DEF VALUES (30,'Ionescu','Lugoj');
INSERT INTO clienti_DEF VALUES (40,'Radulescu','Timisoara');
INSERT INTO clienti_DEF VALUES (50,'Florescu','Arad');

Pentru a vizualiza structura tabelului, instructiunea este:

DESCRIBE clienti_DEF;

Pentru a vizualiza datele din tabel, instructiunea este:

SELECT * FROM clienti_DEF;

Daca sunt campuri ale caror valori se vor adauga ulterior, in instructiunea INSERT trebuie precizate campurile ale
caror valori se completeaza.
Folosim structura de tabele cu DEF1 pentru a nu afecta tabelele cu DEF pe care le vom utiliza si in instructiunile
ulterioare.

OBS: cheia primara NU poate ramane necompletata !!

INSERT INTO clienti_DEF1 VALUES (10,'Popescu','Arad');

INSERT INTO clienti_DEF1 VALUES (20,'Georgescu');

INSERT INTO clienti_DEF1(codcl,dencl) VALUES (20,'Georgescu');

INSERT INTO clienti_DEF1(codcl,localit) VALUES (30,'Lugoj');

Obs: pentru aceste adaugari partiale de date am utilizat tabela clienti_DEF1.


Revenind completam acum cu date tabelele facturi_DEF, facturi_DEF1.
Vom adauga cate trei facturi la fiecare clienti dar, in mod intentionat, vom lasa un client fara facturi in tabela
facturi_DEF.
De asemenea, avand un camp de tip data calendaristica, acesta se va completa utilizandu-se instructiunea
TO_DATE, in care trebuie precizat modelul dupa care se preia ziua, luna si anul.

INSERT INTO facturi_DEF VALUES (101,TO_DATE('10.02.2023','dd.mm.yyyy'),10,2500);


INSERT INTO facturi_DEF VALUES (102,TO_DATE('15/02/2023','dd/mm/yyyy'),20,2800);
INSERT INTO facturi_DEF VALUES (103,TO_DATE('03/22/2023','mm/dd/yyyy'),30,2000);
INSERT INTO facturi_DEF VALUES (104,TO_DATE('04/21/23','mm/dd/yy'),40,1800);
INSERT INTO facturi_DEF VALUES (105,TO_DATE('2023/17/05','yyyy/dd/mm'),30,2700);
INSERT INTO facturi_DEF VALUES (106,TO_DATE('2023/06/10','yyyy/mm/dd'),10,1800);
INSERT INTO facturi_DEF VALUES (107,TO_DATE('22-06-2023','dd-mm-yyyy'),20,3000);
INSERT INTO facturi_DEF VALUES (108,TO_DATE('07-12-23','mm-dd-yy'),40,2800);
INSERT INTO facturi_DEF VALUES (109,TO_DATE('15/08/2023','dd/mm/yyyy'),20,1500);
INSERT INTO facturi_DEF VALUES (110,TO_DATE('17/09/2023','dd/mm/yyyy'),10,1700);
INSERT INTO facturi_DEF VALUES (111,TO_DATE('23/10/2023','dd/mm/yyyy'),30,1000);
INSERT INTO facturi_DEF VALUES (112,TO_DATE('14/11/2023','dd/mm/yyyy'),40,2400);

INSERT INTO facturi_DEF VALUES (113,TO_DATE('17/12/2023','dd/mm/yyyy'),60,1300);

ATENTIE! Trebuie respectata integritatea referentiala, clientul cu codul 60 NU EXISTA in tabela parinte clienti_DEF
Adaugam aceleasi date si in tabela facturi_DEF1

INSERT INTO facturi_DEF1 VALUES (101,TO_DATE('10.02.2023','dd.mm.yyyy'),10,2500);


INSERT INTO facturi_DEF1 VALUES (102,TO_DATE('15/02/2023','dd/mm/yyyy'),20,2800);
INSERT INTO facturi_DEF1 VALUES (103,TO_DATE('03/22/2023','mm/dd/yyyy'),30,2000);
INSERT INTO facturi_DEF1 VALUES (105,TO_DATE('2023/17/05','yyyy/dd/mm'),30,2700);
INSERT INTO facturi_DEF1 VALUES (106,TO_DATE('2023/06/10','yyyy/mm/dd'),10,1800);
INSERT INTO facturi_DEF1 VALUES (107,TO_DATE('22-06-2023','dd-mm-yyyy'),20,3000);
INSERT INTO facturi_DEF1 VALUES (109,TO_DATE('15/08/2023','dd/mm/yyyy'),20,1500);
INSERT INTO facturi_DEF1 VALUES (110,TO_DATE('17/09/2023','dd/mm/yyyy'),10,1700);
INSERT INTO facturi_DEF1 VALUES (111,TO_DATE('23/10/2023','dd/mm/yyyy'),30,1000);

SELECT * FROM facturi_DEF;


SELECT * FROM facturi_DEF1;

Obs: in tabela facturi_DEF1 am adaugat doar facturile clientilor 10,20,30 intrucat doar acesti 3 clienti exista in
tabela parinte clienti_DEF1.
4. Instructiuni DDL

CREATE, ALTER, RENAME, DROP

Adaugarea unei constrangeri noi

ALTER TABLE clienti_DEF1


ADD
CONSTRAINT ck_codcl_def1 CHECK (codcl IN ('10','20'));

Obs: constrangerea NU ESTE VALIDATA SI ACTIVATA intrucat in tabela clienti_DEF1 a treia inregistrare nu
verifica aceasta noua constrangere.

Modificam constrangerea

ALTER TABLE clienti_DEF1


ADD
CONSTRAINT ck_codcl_def1 CHECK (codcl IN ('10','20', '30', '40', '50'));

Anularea unei constranageri existente.....constrangerea de tip CHECK a disparut

ALTER TABLE clienti_DEF1


DROP
CONSTRAINT ck_codcl_def1;

Adaugarea unui camp nou intr-o tabela

ALTER TABLE facturi_DEF1


ADD
luna NUMBER(2);

ALTER TABLE facturi_DEF1


ADD
an NUMBER(4);

Stergerea unui camp nou dintr-o tabela....atentie...COLUMN

ALTER TABLE facturi_DEF1


DROP
COLUMN an;
5. Instructiuni DML

INSERT, UPDATE, DELETE

Actualizarea campului luna din tabela facturi_DEF1

UPDATE facturi_DEF1
SET luna=EXTRACT(MONTH FROM datafact);

Actualizarea cu conditie

La a doua inregistrare trebuie sa actualizam cu orasul Timisoara iar la a treia inregistrare trebuie sa punem clientul
Ionescu.

UPDATE clienti_DEF1
SET localit='Timisoara'
WHERE codcl=20;

UPDATE clienti_DEF1
SET dencl='Ionescu'
WHERE codcl=30;

Stergerea cu conditie

Vom sterge o factura a carui numar este precizat

DELETE FROM facturi_DEF1


WHERE nrfact=109;

Stergerea tabelelor

DROP TABLE clienti_DEF1;

Aceasta se realizeaza de la tabelele subordonate spre tabelele primare pentru a nu afecta integritatea
referentiala.

DROP TABLE facturi_DEF1;


DROP TABLE clienti_DEF1;
6. Instructiunea SELECT

Complexitatea instructiunilor va creste progresiv.

- Afisarea intregului continut al unei tabele

SELECT * FROM clienti_DEF;

- Lista facturilor (nrfact, valoare, datafact) ..doar anumite campuri, intr-o alta ordine a coloanelor.

SELECT nrfact, valoare, datafact


FROM facturi_DEF;

- Aceeasi lista ordonata crescator dupa valorile campului valoare

SELECT nrfact, valoare, datafact


FROM facturi_DEF
ORDER BY valoare;

- Aceeasi lista ordonata descrescator dupa valorile campului valoare cu utilizarea indexului coloanei

SELECT nrfact, valoare, datafact


FROM facturi_DEF
ORDER BY 2 DESC;
- Aceeasi lista filtrata la valori peste 2000.

SELECT nrfact, valoare, datafact


FROM facturi_DEF
WHERE valoare>=2000
ORDER BY valoare;

- Lista cu facturile fiecarui client.

SELECT codcl, nrfact, valoare


FROM facturi_DEF
ORDER BY codcl;

SELECT codcl, nrfact, SUM(valoare)


FROM facturi_DEF
GROUP BY codcl, nrfact
ORDER BY codcl;

Campul nrfact fiind cu valori unice (cheie primara), impiedica posibilitatea gruparii. De aceea trebuie, fie
eliminat, fie „prins” intr-o formula de grupare.

SELECT codcl, SUM(valoare)


FROM facturi_DEF
GROUP BY codcl
ORDER BY codcl;

- Modificarea numelui coloanei calculate


SELECT codcl, SUM(valoare) AS Total
FROM facturi_DEF
GROUP BY codcl
ORDER BY codcl;

SELECT codcl, COUNT(nrfact) AS Nr_facturi, SUM(valoare) AS Total


FROM facturi_DEF
GROUP BY codcl
ORDER BY codcl;
- Ordonarea descrescatoare dupa campul calculat.
- Noul nume al campului calculat poate fi utilizat in clauza ORDER BY.

SELECT codcl, SUM(valoare)


FROM facturi_DEF
GROUP BY codcl
ORDER BY SUM(valoare) DESC;

SELECT codcl, SUM(valoare) AS Total


FROM facturi_DEF
GROUP BY codcl
ORDER BY Total DESC;

- Intocmirea unui clasament; aici nu se va mai putea utiliza noul nume al campului.
SELECT codcl, SUM(valoare) AS Total, RANK()OVER(ORDER BY SUM(valoare) DESC)
FROM facturi_DEF
GROUP BY codcl;

SELECT codcl, SUM(valoare) AS Total, RANK()OVER(ORDER BY Total DESC)


FROM facturi_DEF
GROUP BY codcl;

- Pozitia in clasament poate fi, la randul ei, redenumita


SELECT codcl, SUM(valoare) AS Total, RANK()OVER(ORDER BY SUM(valoare) DESC) AS Loc
FROM facturi_DEF
GROUP BY codcl;
Laborator 3

Instructiunea SELECT. Jonctiuni, filtrari, grupari, subinterogare.

1. TABELELE INITIALE
Crearea tabelelor se regaseste in script.
2. INSERARE DE DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
....
....
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
Instructiunile se regasesc in script.

END;
/
3. SELECT din 2 tabele

SELECT nrfact, valoare, datafact


FROM facturi_DEF
ORDER BY valoare;
SELECT codcl, valoare, datafact
FROM facturi_DEF
ORDER BY codcl;
- trecerea la informatii din 2 tabele..aparitia campului dencl
SELECT nrfact, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY valoare;
SELECT nrfact, dencl, valoare, datafact
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY 3;
SELECT codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY 1;
Obs: campul codcl apare in ambele tabele;
de aceea trebuie precizat tabelul
SELECT clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY 1;
- filtrare numerica si doua criterii de ordonare; al doilea criteriu intervine doar la egalitatea valorilor
referitoare la primul criteriu
SELECT clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND valoare>=2000
ORDER BY 1,3;

sau

SELECT clienti_DEF.codcl, dencl, valoare, datafact


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE valoare>=2000
ORDER BY 1,3;
4. Jonctiunea externa
Se doreste ca lista sa cuprinda si clientul care nu are facturi..clientul cu codul 50. Pentru a face acest lucru, este
necesar ca in conditia de legatura sa punem semnul (+) la tabela mai „saraca” din punct de vedere al valorilor posibile
ale cheii de legatura (codcl), adica la tabela copil.

SELECT clienti_DEF.codcl, dencl, valoare


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+)
ORDER BY 1;

Pentru a obtine ACELASI REZULTAT, in cazul utilizarii lui JOIN,


se va utiliza LEFT JOIN sau RIGHT JOIN care apar in clauza FROM
Aici insa trebuie avut grija la ordinea tabelelor !!!!!

Intrucat trebuie inclusi TOTI clientii, conexiunea este la stanga.

SELECT clienti_DEF.codcl, dencl, valoare


FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY 1; clienti facturi
left

Daca in clauza FROM inversam tabelele, conexiunea va fi la dreapta.


De asemenea am adaugat si NVL care inlocuieste null cu valoarea dorita
Am redenumit noul camp.

SELECT clienti_DEF.codcl, dencl, NVL(valoare,0) AS Val


FROM facturi_DEF RIGHT JOIN clienti_DEF ON facturi_DEF.codcl=clienti_DEF.codcl facturi clienti
ORDER BY 1; right
- Daca se doreste adaugarea si a unei conditii de filtrare, aceasta se pune tot in clauza WHERE alaturi de
conditia de legatura, sau ramane ca o clauza WHERE, in cazul in care conditia de legatura se realizeaza in
FROM prin una din variantele de JOIN.

SELECT clienti_DEF.codcl, dencl, valoare


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+) AND valoare>=2000
ORDER BY 1;
SAU
SELECT clienti_DEF.codcl, dencl, valoare
FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE valoare>=2000
ORDER BY 1;
Obs: clientul cu codul 50 nu mai apare intrucat exista conditia de filtrare aplicata valorii.
5. Grupari informatiilor din 2 tabele

In cazul interogarilor care contin grupari, campurile alese trebuie sa aiba valori cat mai omogene care sa permita
realizarea gruparilor. Daca se utilizeaza un astfel de camp, este necesara utilizarea unei functii de grupare pentru
acel camp.

Exemplu: sa se afiseze totalul valoric al facturilor fiecarui client.

SELECT nrfact, clienti_DEF.codcl, dencl, SUM(valoare)


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+)
GROUP BY nrfact, clienti_DEF.codcl, dencl
ORDER BY 2;

Campul nrfact fiind cheie primara, nu va permite gruparea

SELECT clienti_DEF.codcl, dencl, SUM(valoare)


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+)
GROUP BY clienti_DEF.codcl, dencl
ORDER BY 1;

Obs: disparitia campului cu valori eterogene nrfact, a dus la posibiltatea realizarii gruparilor pe fiecare client.
De asemenea s-a schimbat si indexul coloanei de grupare.
Daca se chiar doreste utilizarea si a campului nrfact, trebuie sa folosim o functie de grupare, de exemplu COUNT.

SELECT clienti_DEF.codcl, dencl, SUM(valoare), COUNT(nrfact)


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+)
GROUP BY clienti_DEF.codcl, dencl
ORDER BY 1;

O varianta imbunatatita este urmatoarea

SELECT clienti_DEF.codcl, dencl, SUM(NVL(valoare,0)) AS Totval, COUNT(nrfact) AS Nrfacturi


FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl
ORDER BY 1;

- Combinarea gruparii cu filtrarea


Pronind de la interogarea anterioara, sa se afiseze o situatie a clientilor al caror total valoric al facturilor este de
peste 10000, insa ordonati descrescator dupa totalul facturilor
SELECT clienti_DEF.codcl, dencl, SUM(valoare), COUNT(nrfact)
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+) AND SUM(valoare)>=10000
GROUP BY clienti_DEF.codcl, dencl
ORDER BY SUM(valoare) DESC;
OBS: criteriul de filtrare pentru grupuri se adauga folosind HAVING, clauza ce se plaseaza dupa GROUP BY
SELECT clienti_DEF.codcl, dencl, SUM(valoare), COUNT(nrfact)
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+)
GROUP BY clienti_DEF.codcl, dencl
HAVING SUM(valoare)>=10000
ORDER BY SUM(valoare) DESC;

SELECT clienti_DEF.codcl, dencl, SUM(NVL(valoare,0)) AS Totval, COUNT(nrfact) AS Nrfacturi


FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl
HAVING Totval>=10000
ORDER BY SUM(NVL(valoare,0)) DESC;

SELECT clienti_DEF.codcl, dencl, SUM(NVL(valoare,0)) AS Totval, COUNT(nrfact) AS Nrfacturi


FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl
HAVING SUM(NVL(valoare,0))>=10000
ORDER BY Totval DESC;
Obs: -la utilizarea variantei cu JOIN nu avem clauza WHERE intrucat filtrarea cu HAVING se face dupa grupare.
-noua denumire a campului Totval nu se poate utiliza in clauza HAVING, dar se poate utiliza in ORDER
Exista situatii in care filtrarea se poate face atat inaintea gruparii cat si dupa grupare, rezultatul fiind acelasi.
Sa se afiseze o situatie a mediei valorilor facturilor fiecarui client din Timisoara
De asemenea, am revenit si la jonctiunea simpla.

SELECT clienti_DEF.codcl, dencl, localit, AVG(valoare) AS medie_client


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl, localit
ORDER BY dencl;

Se adauga filtrarea doar a clientilor din Timisoara

SELECT clienti_DEF.codcl, dencl, localit, AVG(valoare) AS medie_client


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND localit='Timisoara'
GROUP BY clienti_DEF.codcl, dencl, localit
ORDER BY dencl;
SELECT clienti_DEF.codcl, dencl, localit, AVG(valoare) AS medie_client
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl, localit
HAVING localit='Timisoara'
ORDER BY dencl;
Aceleasi instructiuni scrise si in cazul legaturii
facuta cu INNER JOIN

SELECT clienti_DEF.codcl, dencl, localit, AVG(valoare) AS medie_client


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE localit='Timisoara'
GROUP BY clienti_DEF.codcl, dencl, localit
ORDER BY dencl;
SELECT clienti_DEF.codcl, dencl, localit, AVG(valoare) AS medie_client
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl, localit
HAVING localit='Timisoara'
ORDER BY dencl;

Observatii
- Filtrarea din clauza WHERE actioneaza inaintea gruparii datelor iar filtrarea din clauza HAVING actioneaza
dupa ce se fac gruparile si calculele pe grupuri;
- Prin utilizarea variantei JOIN, clauza WHERE ramane doar pentru filtrarea datelor inaintea gruparii;
- Exista situatii in care conditia de filtrare se poate aplica atat inante de grupare cat si dupa grupare, si anume
atunci cand acesta conditie se refera la un camp care nu este „afectat” de operatiunile de grupare si/sau de
calcul de grup
6. Subinterogare
Sa se calculeze media valorilor tuturor facturilor, indiferent de client.
Sa se afiseze apoi o situatie descrescatoare a facturilor care depasesc aceasta medie.
SELECT AVG(valoare)
FROM facturi_DEF;
- Rotunjirea valorii
SELECT ROUND(AVG(valoare),2) AS medie_facturi
FROM facturi_DEF;
OBS: Functiile de tip SUM, COUNT, AVG se pot folosi si fara GROUP BY, dar numai singure, fara campuri simple,
si, in aceasta situatie, calculul se face pentru toate valorile campului respectiv.

SELECT clienti_DEF.codcl, dencl, valoare


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY valoare DESC;

Cele doua interogari se pot combina, rezultatul primeia fiind folosit pentru filtrare in cea de-a doua.
SELECT clienti_DEF.codcl, dencl, valoare
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND valoare>(SELECT ROUND(AVG(valoare),2) AS medie_facturi
FROM facturi_DEF)
ORDER BY valoare DESC;
SELECT clienti_DEF.codcl, dencl, valoare
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND valoare>(SELECT AVG(valoare)
FROM facturi_DEF)
ORDER BY valoare DESC;
SELECT clienti_DEF.codcl, dencl, valoare
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE valoare>(SELECT AVG(valoare)
FROM facturi_DEF)
ORDER BY valoare DESC;

Observatii
- atunci cand rezultatul unei subinterogari este folosit ca si criteriu de filtrare (in WHERE sau in HAVING), acest
rezultat trebuie sa fie unic, nu un tabel cu una sau mai multe coloane; cand subinterogarea este folosita in
clauza FROM, atunci rezultatul subinterogarii trebuie sa fie un tabel
- in subinterogare nu este necesara redenumirea campului, rotunjirea valorii, etc intrucat rezultatul (AVG)
este folosit doar pentru comparare, nu este si afisat.
- cand este utilizat JOIN pentru legatura, in WHERE ramane doar filtrarea realizata pe baza subinterogarii.
Laborator 4
Instructiunea SELECT. Filtrari, campuri noi, grupari, duble grupari.
Utilizarea a 3 tabele.
1. TABELELE INITIALE
Crearea tabelelor se regaseste in script.
2. INSERARE DE DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
....
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
Instructiunile se regasesc in script.

END;
/
3. Instructiuni SELECT noi; filtrari utilizand mai multe valori numerice.
- filtrare pe camp numeric VALORI DISCRETE – facturile clientilor 10, 30 SAU 50
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+) AND (clienti_DEF.codcl=10 OR clienti_DEF.codcl=30 OR
clienti_DEF.codcl=50)
ORDER BY 2,4;
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE clienti_DEF.codcl=10 OR clienti_DEF.codcl=30 OR clienti_DEF.codcl=50
ORDER BY 2,4;
Pentru a reduce conditia de filtrare, se poate utiliza IN
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+) AND clienti_DEF.codcl IN (10,30,50)
ORDER BY 2,4;

SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact


FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE clienti_DEF.codcl IN (10,30,50)
ORDER BY 2,4;
- filtrare pe camp numeric de tip INTERVAL – facturile cu valorile intre 1500 si 2400
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND valoare>=1500 AND valoare<=2400
ORDER BY 2,4;
- cerinta de mai sus dar cu utilizarea lui BETWEEN
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND valoare BETWEEN 1500 AND 2400
ORDER BY 2,4;
- filtrare pe camp numeric INTERVAL – facturile cu valorile din EXTERIORUL intervalului 1500 .. 2400
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND (valoare<1500 OR valoare>2400)
ORDER BY 2,4;
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE valoare<1500 OR valoare>2400
ORDER BY 2,4;
- cerinta de mai sus dar cu utilizarea lui NOT BETWEEN
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND valoare NOT BETWEEN 1500 AND 2400
ORDER BY 2,4;
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, datafact
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE valoare NOT BETWEEN 1500 AND 2400
ORDER BY 2,4;
- filtrare pe valoare NULA – facturile care au sau nu au incasari – se utilizeaza IS NULL sau IS NOT NULL
SELECT facturi_DEF.nrfact, valoare, datafact, codinc, valinc
FROM facturi_DEF,incasari_DEF
WHERE facturi_DEF.nrfact=incasari_DEF.nrfact(+) AND codinc IS NULL
ORDER BY datafact;
SELECT facturi_DEF.nrfact, valoare, datafact, codinc, valinc
FROM facturi_DEF LEFT JOIN incasari_DEF ON facturi_DEF.nrfact=incasari_DEF.nrfact
WHERE codinc IS NOT NULL
ORDER BY 3;
- introducerea in interogarea de mai sus si a informatiilor despre client; informatiile provin din 3 tabele
SELECT clienti_DEF.codcl, dencl, facturi_DEF.nrfact, valoare, datafact, codinc, valinc
FROM clienti_DEF,facturi_DEF,incasari_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl AND facturi_DEF.nrfact=incasari_DEF.nrfact(+) AND codinc IS NULL
ORDER BY dencl;
SELECT clienti_DEF.codcl, dencl, facturi_DEF.nrfact, valoare, datafact, codinc, valinc
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl LEFT JOIN incasari_DEF ON
facturi_DEF.nrfact=incasari_DEF.nrfact
WHERE codinc IS NULL
ORDER BY dencl;
4. Camp calculat
- sa se adauge in tabelul facturi_DEF un camp nou, cota_tva; se completeaza cu valoarea 0.05 pentru anumite
facturi, 0.1 pentru alte cateva facturi si cu 0.19 pentru restul facturilor. Se foloseste instructiunea CASE; sa
se calculeze apoi, intr-o coloana separata, valoarea cu TVA a facturilor, inclusiv pentru clientul care nu are
facturi; sa se redenumeasca Valoare_cu_TVA

ALTER TABLE facturi_DEF ADD


cota_tva NUMBER(3,2);

UPDATE facturi_DEF
SET cota_tva=
CASE
WHEN nrfact IN (102, 105, 203, 206) THEN 0.05
WHEN nrfact IN (104, 107, 207, 210) THEN 0.10
ELSE 0.19
END;
SELECT * From facturi_DEF;
SELECT nrfact, clienti_DEF.codcl, dencl, valoare, cota_tva, valoare*(1+cota_tva)
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+)
ORDER BY 2,4;
SELECT nrfact, clienti_DEF.codcl, dencl, NVL(valoare,0) AS valnoua, NVL(cota_tva,0) AS cota_noua,
valnoua*(1+cota_noua) AS Valoare_cu_TVA
FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY 2,4;
SELECT nrfact, clienti_DEF.codcl, dencl, NVL(valoare,0) AS valnoua, NVL(cota_tva,0) AS cota_noua,
valnoua*(1+NVL(cota_tva,0)) AS Valoare_cu_TVA
FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY 2,4;
SELECT nrfact, clienti_DEF.codcl, dencl, NVL(valoare,0) AS valnoua, NVL(cota_tva,0) AS cota_noua,
NVL(valoare,0)*(1+NVL(cota_tva,0)) AS Valoare_cu_TVA
FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
ORDER BY 2,4;

OBS: seminarul trecut am vazut ca noul nume al unui camp calculat nu poate fi folosit in conditiile de filtrare.
Conform exemplelor de mai sus, noul nume nu poate fi utilizat nici in calculul unui alt camp.
5. Grupari pe camp calculat
- totalul general – NU contine nicio grupare, deci NU are nevoie de optiunea GROUP BY
SELECT SUM(valoare*(1+cota_tva)) AS Total_brut_general
FROM facturi_DEF;
- subtotaluri – presupun obtinerea de valori partiale, pe subgrupuri obtinute prin gruparea in functie de
valorile unuia sau mai multor campuri; de exemplu subtotaluri lunare; in aceasta situatie se poate pune in
discutie si un anumit criteriu de ordonare

SELECT SUM(valoare*(1+cota_tva)) AS Total_brut_lunar


FROM facturi_DEF
GROUP BY EXTRACT(MONTH FROM datafact)
ORDER BY EXTRACT(MONTH FROM datafact);

SELECT EXTRACT(MONTH FROM datafact) AS Luna, SUM(valoare*(1+cota_tva)) AS Total_brut_lunar


FROM facturi_DEF
GROUP BY EXTRACT(MONTH FROM datafact)
ORDER BY Luna;
OBS: NU este obligatoriu sa apara in SELECT campul (campurile) utilizat (utilizate) in GROUP BY; reciproca insa NU
este adevarata, daca un camp apare in SELECT si nu este camp calculat in urma gruparii (SUM, AVG, MIN, MAX)
atunci el trebuie sa apara OBLIGATORIU si in GROUP BY; in acest ultim caz, un eventual nou nume al campului poate
fi folosit doar in clauza ORDER BY
- alt exemplu de subtotaluri – in functie de client si date din 2 tabele
SELECT clienti_DEF.codcl, dencl, SUM(valoare*(1+cota_tva)) AS Total_brut, COUNT(nrfact) AS Nr_facturi
FROM clienti_DEF LEFT JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl
ORDER BY Nr_facturi DESC, Total_brut DESC;

OBS: daca se doreste ordonare descrescatore la fiecare criteriu, optiunea DESC trebuie precizata la fiecare camp.
- Combinatia grupare – filtrare pe camp calculat– clientii cu total facturi peste 10000

SELECT clienti_DEF.codcl, dencl, SUM(valoare*(1+cota_tva)) AS Total_brut, COUNT(nrfact) AS Nr_facturi


FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl
HAVING SUM(valoare*(1+cota_tva))>=10000
ORDER BY Total_brut DESC;

SELECT clienti_DEF.codcl, dencl, SUM(valoare*(1+cota_tva)) AS Total_brut, COUNT(nrfact) AS Nr_facturi


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl
HAVING SUM(valoare*(1+cota_tva))>=10000
ORDER BY Total_brut DESC;
- Filtrare combinata....clientii cu totalul valoric al facturilor mai mare de 10000..insa doar pentru facturile
emise DUPA data de 01/05/2023.
SELECT clienti_DEF.codcl, dencl, SUM(valoare*(1+cota_tva)) AS Total_brut, COUNT(nrfact) AS Nr_facturi
FROM clienti_DEF,facturi_DEF
WHERE clienti_DEF.codcl=facturi_DEF.codcl(+) AND datafact>=TO_DATE('01/05/2023','dd/mm/yyyy')
GROUP BY clienti_DEF.codcl, dencl
HAVING SUM(valoare*(1+cota_tva))>=10000
ORDER BY Total_brut DESC;

SELECT clienti_DEF.codcl, dencl, SUM(valoare*(1+cota_tva)) AS Total_brut, COUNT(nrfact) AS Nr_facturi


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE datafact>=TO_DATE('01/05/2023','dd/mm/yyyy')
GROUP BY clienti_DEF.codcl, dencl
HAVING SUM(valoare*(1+cota_tva))>=10000
ORDER BY Total_brut DESC;

OBSERVATII
- Filtrarea din clauza WHERE actioneaza inaintea gruparii datelor iar filtrarea din clauza HAVING actioneaza
dupa ce se fac gruparile;
- Se pot utiliza la filtrarea datelor inaintea gruparii, adica in WHERE, atat campuri care apar in SELECT, cat si
campuri care nu apar in SELECT intrucat, din cauza eterogenitatii valorilor lor, ar face ca gruparea sa nu se
realizeze. In exemplul de mai sus se observa ca datafact nu apare intre campurile SELECT, ea fiind utilizata
doar ca si criteriu de filtrare.
6. Grupari, calcule, subinterogari
SELECT MAX(valoare) AS Maxim_facturi, ROUND(AVG(valoare),2) AS Medie_facturi
FROM facturi_DEF;

OBS: NU avem GROUP BY intrucat sunt doar valori calculate pentru intreaga coloana de valori.
- Utilizarea lui MAX

Sa se determine factura cu valoarea cea mai mare si sa se afiseze clientul / clientii care are / au facturi cu
aceasta valoare.

SELECT clienti_DEF.codcl, dencl, MAX(valoare) AS Maxim_facturi


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY clienti_DEF.codcl, dencl;

OBS: maximul general este maximul maximelor fiecarui client. Maximul calculat mai sus se va utiliza in
subinterogare

SELECT clienti_DEF.codcl, dencl, nrfact, valoare AS Maxim


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
WHERE valoare=(SELECT MAX(valoare)
FROM facturi_DEF)
;
- Combinarea a doua functii de grupare...utilizarea lui SUM combinat cu MAX
Sa se determine si sa se afiseze localitatea / localitatile cu cea mai mare valoare a totalului de vanzari.

SELECT localit, SUM(valoare) AS Total_loc


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY localit;

- Extragerea maximului din coloana TOTAL_LOC;

SELECT localit, MAX(SUM(valoare)) AS Maxim_totaluri_loc


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY localit;

- intrucat e o dubla grupare, campul localit trebuie sa dispara din SELECT dar trebuie sa ramana in GROUP
BY intrucat gruparea dupa localitate este cea care da totalurile partiale.

SELECT MAX(SUM(valoare)) AS Maxim_totaluri_loc


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY localit;

Obs: utilizarea tabelei clienti_DEF este necesara doar pentru ca se face gruparea dupa campul localit

- utilizarea celei de-a doua interogari ca subinterogare in prima interogare; intrucat filtrarea se face pe
campul SUM, conditia de filtrare se pune in clauza HAVING

SELECT localit, SUM(valoare) AS Max_t_l


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY localit
HAVING SUM(valoare)= (SELECT MAX(SUM(valoare))
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY localit)
;

OBS: subinterogarea de mai sus poate fi valorificata mai bine daca se doreste afisarea localitatilor care au un volum
de vanzari mai mare de 50% din cel mai mare volum de vanzari, ordonate descrescator in functie de aceste valori.

SELECT localit, SUM(valoare) AS Top_valori


FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY localit
HAVING SUM(valoare)>=0.5*(SELECT MAX(SUM(valoare))
FROM clienti_DEF INNER JOIN facturi_DEF ON clienti_DEF.codcl=facturi_DEF.codcl
GROUP BY localit)
ORDER BY 2 DESC;
- Combinarea a doua functii de grupare...utilizarea lui SUM combinat cu AVG

Sa se determine si sa se afiseze lunile care au totalul facturilor mai mare decat media acestor totaluri.

- totalul fiecarei luni


SELECT EXTRACT(MONTH FROM datafact) AS Luna, SUM(valoare) AS Total_lunar
FROM facturi_DEF
GROUP BY EXTRACT(MONTH FROM datafact);

- Media totalurilor lunare..dubla grupare..

SELECT AVG(SUM(valoare)) AS Media_totaluri_lunare


FROM facturi_DEF
GROUP BY EXTRACT(MONTH FROM datafact);
o lunile care au totalul facturilor mai mare decat media acestor totaluri

SELECT EXTRACT(MONTH FROM datafact) AS Luna, SUM(valoare) AS Total_lunar


FROM facturi_DEF
GROUP BY EXTRACT(MONTH FROM datafact)
HAVING SUM(valoare)>=(SELECT AVG(SUM(valoare))
FROM facturi_DEF
GROUP BY EXTRACT(MONTH FROM datafact))
ORDER BY 2 DESC;

Obs: intrucat media s-a utilizat doar pentru comparare, nu fost necesara rotunjirea acesteia la doua zecimale.

TEMA:

PORNIND DE LA TOATE EXEMPLELE ILUSTRATE IN SEMINARIILE 2,3,4, PROPUNETI

- ADAUGAREA IN TABELA incasari_DEF A UNUI CAMP NUMIT TRIMESTRU AL CARUI CONTINUT SA FIE
COMPLETAT AUTOMAT CU CUVINTELE primul, al doilea, al treilea, al patrulea IN FUNCTIE DE LUNA DATEI
INCASARII
- 5 INSTRUCTIUNI SELECT CAT MAI INEDITE, FOLOSIND GRUPARI, DUBLE GRUPARI, FILTRARI PENTRU
DIVERSE CAMPURI EXISTENTE SAU CALCULATE, DIN TOATE CELE 3 TABELE.

Astept raspunsurile intr-un document WORD atat instructiunile cat si capturi ale executiei acestora.
Documentul se va incarca pe platforma, in sectiunea dedicata.

Termen: 03.04.2023
Laborator 5
FILTRARI, GRUPARI, SUBINTEROGARI, ALIAS, PROCEDURI

1. TABELELE INITIALE
Crearea tabelelor se regaseste in script.
2. INSERAREA DATELOR

Popularea tabelelor cu date se face se realizeaza


in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
....
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
Instructiunile se regasesc in script.
END;
/
3. EXEMPLE DE INTEROGARI PE DIVERSE TIPURI DE DATE
- Filtrarea pe camp de tip VARCHAR – este CASE SENSITIVE
SELECT empno, ename, deptno
FROM emp
WHERE ename = 'blake';
SELECT empno, ename, deptno
FROM emp
WHERE UPPER(ename) = 'BLAKE';
SELECT empno, ename, deptno
FROM emp
WHERE LOWER(ename) = 'blake';
SELECT *
FROM emp
WHERE ename LIKE 'A%' OR ename LIKE 'M%'
ORDER BY 2;
SELECT *
FROM emp
WHERE deptno IN (10,20)
ORDER BY 8;
SELECT *
FROM emp
WHERE deptno NOT IN (10,20)
ORDER BY 8;
SELECT *
FROM emp
WHERE hiredate BETWEEN TO_DATE ('01/01/1981', 'dd/mm/yyyy') AND TO_DATE('28/02/1982','dd/mm/yyyy')
ORDER BY 5;
- Implicarea in calcule a campurilor NULL
SELECT ename, job, sal, comm, sal+comm
FROM emp;
SELECT ename, job, sal, comm, sal+NVL(comm,0) AS sal_tot
FROM emp;
- Exemplu de conditie de filtrare care poate fi utilizata atat in WHERE cat si in HAVING
SELECT emp.deptno, loc,dname, COUNT(empno) AS nr_ang
FROM emp, dept
WHERE emp.deptno=dept.deptno AND emp.deptno<>10
GROUP BY emp.deptno,loc,dname
ORDER BY nr_ang DESC;
SELECT emp.deptno, loc,dname, COUNT(empno) AS nr_ang
FROM emp, dept
WHERE emp.deptno=dept.deptno
GROUP BY emp.deptno,loc,dname
HAVING emp.deptno<>10
ORDER BY 4 DESC;
SELECT emp.deptno, loc,dname, COUNT(empno) AS nr_ang
FROM emp, dept
WHERE emp.deptno=dept.deptno
GROUP BY emp.deptno,loc,dname
HAVING COUNT(empno)>5
ORDER BY nr_ang DESC;
- Utilizarea campului de legatura din tabela mai „bogata”,adica acolo unde campul acesta este cheie primara
SELECT emp.deptno, loc,dname, COUNT(empno) AS nr_ang
FROM emp, dept
WHERE emp.deptno(+)=dept.deptno
GROUP BY emp.deptno,loc,dname
ORDER BY 1;

SELECT dept.deptno, loc,dname, COUNT(empno) AS nr_ang


FROM emp, dept
WHERE emp.deptno(+)=dept.deptno
GROUP BY dept.deptno,loc,dname
ORDER BY 1;
- Rotujirea mediei atunci cand ea este afisata
SELECT loc, MIN(sal+NVL(comm,0)) AS minim, AVG(sal+NVL(comm,0)) AS medie
FROM emp,dept
WHERE emp.deptno=dept.deptno
GROUP BY loc
ORDER BY 3;
SELECT loc, MIN(sal+NVL(comm,0)) AS minim, ROUND(AVG(sal+NVL(comm,0)),2) AS medie
FROM emp,dept
WHERE emp.deptno=dept.deptno
GROUP BY loc
ORDER BY 3;
4. EXEMPLE DE SUBINTEROGARI - IN CLAUZA HAVING SAU IN CLAUZA WHERE
Selectarea departamentelor care au cel mai mic numar de angajati
SELECT dept.deptno, dname, COUNT(empno) AS Total
FROM emp INNER JOIN dept ON emp.deptno=dept.deptno
GROUP BY dept.deptno, dname
HAVING COUNT(empno)=(SELECT MIN(COUNT(empno))
FROM emp
GROUP BY deptno)
;
Calculul salariului mediu si apoi persoanele care au salariul mai mare salariul mediu, ORDONATE DESCRESCATOR
SELECT ROUND(AVG(sal+NVL(comm,0)),2) AS salariu_mediu
FROM emp;
SELECT dept.deptno,empno,ename,job,dname,sal+NVL(comm,0) AS salariu
FROM emp INNER JOIN dept ON emp.deptno=dept.deptno
WHERE sal+NVL(comm,0)>=(SELECT AVG(sal+NVL(comm,0))
FROM emp)
ORDER BY 6 DESC;
Folosirea lui RANK
SELECT dept.deptno,empno,ename,job,dname,sal+NVL(comm,0) AS salariu,
RANK() OVER(ORDER BY sal+NVL(comm,0) DESC) AS Top
FROM emp INNER JOIN dept ON emp.deptno=dept.deptno
WHERE sal+NVL(comm,0)>=(SELECT AVG(sal+NVL(comm,0))
FROM emp)
;
SELECT dept.deptno,empno,ename,job,dname,sal+NVL(comm,0) AS salariu,
RANK() OVER(ORDER BY sal+NVL(comm,0) DESC, empno) AS Top
FROM emp INNER JOIN dept ON emp.deptno=dept.deptno
WHERE sal+NVL(comm,0)>=(SELECT AVG(sal+NVL(comm,0))
FROM emp)
;
SELECT dept.deptno,empno,ename,job,dname,sal+NVL(comm,0) AS salariu,
RANK() OVER(ORDER BY sal+NVL(comm,0) DESC, empno) AS Top
FROM emp INNER JOIN dept ON emp.deptno=dept.deptno
WHERE sal+NVL(comm,0)>=(SELECT AVG(sal+NVL(comm,0))
FROM emp)
AND ROWNUM<=4
;

5. ALIASURILE („poreclele”) tabelelor

Se pot utiliza pentru a simplifica scrierea unor interogari complexe in care sunt implicate mai multe tabele
Legatura intre aliasuri si denumirea tabelelor se realizeaza O SINGURA DATA, in clauza FROM, aliasurile putand fi
utilizate oriunde in SELECT, atat inainte cat si dupa FROM
SELECT empno, ename, a.deptno, dname
FROM emp a, dept b
WHERE a.deptno=b.deptno;
Acelasi lucru cu INNER JOIN

SELECT empno, ename, a.deptno, dname


FROM emp a INNER JOIN dept b ON a.deptno=b.deptno;

Folosind aliasuri nu mai trebuie acordata atentie situatiei in care campul apare in mai multe tabele, putandu-se folosi
denumirea tabelului la fiecare camp ales in SELECT.
Acest aspect ajuta in special in instructiunele SELECT mai complexe, in care sunt implicate date din mai multe tabele.
Instructiunea de mai sus se poate scrie si astfel.
SELECT a.empno, a.ename, a.deptno, b.dname
FROM emp a INNER JOIN dept b ON a.deptno=b.deptno;

Un alt mare avantaj al utilizarii aliasurilor este posibilitatea scrierii de interogari de tip SELF JOIN adica de stabilire a
unei legaturi dintre un tabel si el insusi.

PORNIND DE LA SEMNIFICATIA INFORMATIONALA IDENTICA A COLOANELOR empno si mgr, ambele referindu-se la


codul unui persoane, vazute insa din ipostaza de angajat (codul de pe coloana empno), respectiv al managerului
caruia angajatul este subordonat (codul de pe coloana mgr), putem construi o lista a angajatilor la care putem „alipi”
datele referitoare la managerii carora le sunt subordonati.

SELECT e.empno AS ID_Angajat, e.ename AS Nume_Angajat, e.job AS Job_Angajat, e.mgr AS ID_Sef,


m.ename AS Nume_sef, m.job AS Functie_sef
FROM emp e INNER JOIN emp m ON e.mgr = m.empno;
--pentru a vedea si pe KING
SELECT e.empno AS ID_Angajat, e.ename AS Nume_Angajat, e.job AS Job_Angajat, e.mgr AS ID_Sef,
m.ename AS Nume_sef, m.job AS Functie_sef
FROM emp e LEFT JOIN emp m ON e.mgr = m.empno;
6. PROCEDURI NESTOCATE (ANONIME). PROCEDURI STOCATE.
Punctul de plecare sunt interogarile !!!!.
In exemplele din acest seminar interogarea returneaza doar o valoare sau un singur rand de valori.
SELECT e.empno AS ID_angajat, e.ename AS Nume_Angajat, e.job AS Job_Angajat,
e.mgr AS Id_Sef, m.ename AS Nume_sef, m.job AS Functie_sef
FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=7698;
- Numarul de subordonati
SELECT COUNT(e.empno) AS nrsubordonati, e.mgr AS Id_Sef, m.ename AS Nume_sef, m.job AS Functie_sef
FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=7698
GROUP BY e.mgr, m.ename, m.job;

SELECT COUNT(e.empno) AS nrsubordonati


FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=7698
GROUP BY e.mgr, m.ename, m.job;
In cazul procedurilor, afisarea trebuie activata. Instructiunea este:
SET SERVEROUTPUT ON;
OBS: Procedurile vor creste in complexitate
- Procedura nestocata, cu afisarea unei singure informatii.
DECLARE
v_nr_sbd NUMBER;
BEGIN
SELECT COUNT(e.empno) INTO v_nr_sbd
FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=7698
GROUP BY e.mgr, m.ename, m.job;
DBMS_OUTPUT.PUT_LINE ('Numarul de subordonati este '||v_nr_sbd);
END;
/
- Procedura nestocata, cu afisarea mai multor informatii.
Este necesara declararea unei variabile pentru fiecare informatie afisata.
DECLARE
v_nr_sbd NUMBER;
v_cod_mgr emp.mgr%TYPE;
v_nume_mgr emp.ename%TYPE;
v_fct_mgr emp.job%TYPE;

BEGIN
SELECT COUNT(e.empno),e.mgr,m.ename,m.job INTO v_nr_sbd,v_cod_mgr,v_nume_mgr,v_fct_mgr
FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=7698
GROUP BY e.mgr, m.ename, m.job;
DBMS_OUTPUT.PUT_LINE ('Cod manager '||v_cod_mgr);
DBMS_OUTPUT.PUT_LINE ('Nume manager '||v_nume_mgr);
DBMS_OUTPUT.PUT_LINE ('Functie manager '||v_fct_mgr);
DBMS_OUTPUT.PUT_LINE ('Numar subordonati '||v_nr_sbd);
END;
/
- Transformarea procedurii anterioare in procedura stocata.
CREATE OR REPLACE PROCEDURE afis_sbd IS
v_nr_sbd NUMBER;
v_cod_mgr emp.mgr%TYPE;
v_nume_mgr emp.ename%TYPE;
v_fct_mgr emp.job%TYPE;
BEGIN
SELECT COUNT(e.empno),e.mgr,m.ename,m.job INTO v_nr_sbd,v_cod_mgr,v_nume_mgr,v_fct_mgr
FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=7698
GROUP BY e.mgr, m.ename, m.job;
DBMS_OUTPUT.PUT_LINE ('Cod manager '||v_cod_mgr);
DBMS_OUTPUT.PUT_LINE ('Nume manager '||v_nume_mgr);
DBMS_OUTPUT.PUT_LINE ('Functie manager '||v_fct_mgr);
DBMS_OUTPUT.PUT_LINE ('Numar subordonati '||v_nr_sbd);
END;
/
Procedura se compileaza si se stocheaza iar pentru executarea ei este nevoie de instructiunea
EXECUTE afis_sbd;
- Transformarea procedurii anterioare in procedura stocata cu parametru
CREATE OR REPLACE PROCEDURE afis_sbd_p (p_cod NUMBER) IS
v_nr_sbd NUMBER;
v_cod_mgr emp.mgr%TYPE;
v_nume_mgr emp.ename%TYPE;
v_fct_mgr emp.job%TYPE;
BEGIN
SELECT COUNT(e.empno),e.mgr,m.ename,m.job INTO v_nr_sbd,v_cod_mgr,v_nume_mgr,v_fct_mgr
FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=p_cod
GROUP BY e.mgr, m.ename, m.job;
DBMS_OUTPUT.PUT_LINE ('Cod manager '||v_cod_mgr);
DBMS_OUTPUT.PUT_LINE ('Nume manager '||v_nume_mgr);
DBMS_OUTPUT.PUT_LINE ('Functie manager '||v_fct_mgr);
DBMS_OUTPUT.PUT_LINE ('Numar subordonati '||v_nr_sbd);
END;
/
Parametrul se precizeaza in denumirea procedurii iar in corpul procedurii el se poate utiliza in filtrari, calcule.

In instructiunea de executare e necesara precizarea valorii parametrului;


aceasta permite executarea procedurii pentru orice manager.

EXECUTE afis_sbd_p(7698);
EXECUTE afis_sbd_p(7566);
EXECUTE afis_sbd_p(7839);

- Transformarea procedurii anterioare pentru tratarea erorii de negasire a datelor

La comunicarea unui cod gresit, fie ca nu exista (7200), fie ca nu are functie de manager (7499) apare eroarea de
mai jos.

EXECUTE afis_sbd_p(7200);

EXECUTE afis_sbd_p(7499);

Aceasta eroare se gestioneaza in partea de EXCEPTION a procedurii.


CREATE OR REPLACE PROCEDURE afis_sbd_p_err (p_cod NUMBER) IS
v_nr_sbd NUMBER;
v_cod_mgr emp.mgr%TYPE;
v_nume_mgr emp.ename%TYPE;
v_fct_mgr emp.job%TYPE;

BEGIN
SELECT COUNT(e.empno),e.mgr,m.ename,m.job INTO v_nr_sbd,v_cod_mgr,v_nume_mgr,v_fct_mgr
FROM emp e INNER JOIN emp m ON e.mgr = m.empno
WHERE e.mgr=p_cod
GROUP BY e.mgr, m.ename, m.job;

DBMS_OUTPUT.PUT_LINE ('Cod manager '||v_cod_mgr);


DBMS_OUTPUT.PUT_LINE ('Nume manager '||v_nume_mgr);
DBMS_OUTPUT.PUT_LINE ('Functie manager '||v_fct_mgr);
DBMS_OUTPUT.PUT_LINE ('Numar subordonati '||v_nr_sbd);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Angajat inexistent sau nu are functie de manager !!');

END;
/

EXECUTE afis_sbd_p_err(7839);

EXECUTE afis_sbd_p_err(7200);

EXECUTE afis_sbd_p_err(7499);

Apelarea procedurii stocate dintr-o alta procedura anonima


Nu mai apare EXECUTE

DECLARE
codpersoana NUMBER;
BEGIN
codpersoana := &dati_codul_persoanei;

afis_sbd_p_err(codpersoana);
END;
/
Laborator 6
Alte exemple de interogari si proceduri bazate pe ele.
Se va crea o noua structura tabelara si se va completa cu date
1. CREAREA A TREI TABELE
Crearea tabelelor se regaseste in script.
2. COMPLETAREA CU DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
....
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/
Obs:
- din date se observa ca exista studenti care nu au sustinut toate examenele intr-o anumita sesiune;
- studentul cu matricola 60 nu a sustinut niciun examen; la disciplina 330 nu s-au sustinut exemene.
- am pus si examene sustinute dar la care a fost nota 4; tabela de examinari are cheie primara distincta ceea
ce permite posibilitatea reexaminarii.
- prima cifra din codul disciplinei (1, 2 sau 3) se refera la anul in care se studiaza materia; am pus cate 2 materii
in fiecare an de studiu.
3. CERINTE DE LUCRU

SA SE AFISEZE O SITUATIE ALFABETICA A STUDENTILOR


SI DESCRESCATOARE A NOTELOR OBTINUTE DE ACESTIA;

SELECT stud_XY.matr_st, nume_st, den_disc, nota


FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st INNER JOIN disc_XY ON
ex_XY.cod_disc=disc_XY.cod_disc
ORDER BY 2,4 DESC;

SA SE IMBUNATATEASCA ACEASTA SITUATIE ASTFEL

SELECT stud_XY.matr_st, nume_st, den_disc, nota


FROM stud_XY LEFT JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st LEFT JOIN disc_XY ON
ex_XY.cod_disc=disc_XY.cod_disc
ORDER BY 2,4 DESC;
SELECT stud_XY.matr_st, nume_st, den_disc, nota
FROM stud_XY LEFT JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st RIGHT JOIN disc_XY ON
ex_XY.cod_disc=disc_XY.cod_disc
ORDER BY 2,4 DESC;
PORNIND DE LA PRIMA LISTA, OBTINETI URMATOAREA SITUATIE
SELECT stud_XY.matr_st, nume_st, MAX(nota) AS nota_maxima
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
GROUP BY stud_XY.matr_st, nume_st
ORDER BY 3;
RESTRANGETI LISTA DOAR LA UN STUDENT SI PRELUATI INTEROGAREA INTR-O PROCEDURA NESTOCATA.
SELECT stud_XY.matr_st, nume_st, MAX(nota) AS nota_maxima
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE stud_XY.matr_st=20
GROUP BY stud_XY.matr_st, nume_st;
SET SERVEROUTPUT ON;
DECLARE
v_matr_st stud_XY.matr_st%TYPE;
v_nume_st stud_XY.nume_st%TYPE;
v_max NUMBER;
BEGIN
SELECT stud_XY.matr_st, nume_st, MAX(nota) INTO v_matr_st,v_nume_st,v_max
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE stud_XY.matr_st=20
GROUP BY stud_XY.matr_st, nume_st;
DBMS_OUTPUT.PUT_LINE ('Matricola student: '||v_matr_st);
DBMS_OUTPUT.PUT_LINE ('Nume student: '||v_nume_st);
DBMS_OUTPUT.PUT_LINE ('Nota maxima: '||v_max);
END;
/
TRANSFORMATI APOI PROCEDURA IN UNA STOCATA.
CREATE OR REPLACE PROCEDURE afis_max IS
v_matr_st stud_XY.matr_st%TYPE;
v_nume_st stud_XY.nume_st%TYPE;
v_max NUMBER;
BEGIN
SELECT stud_XY.matr_st, nume_st, MAX(nota) INTO v_matr_st,v_nume_st,v_max
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE stud_XY.matr_st=20
GROUP BY stud_XY.matr_st, nume_st;
DBMS_OUTPUT.PUT_LINE ('Matricola student: '||v_matr_st);
DBMS_OUTPUT.PUT_LINE ('Nume student: '||v_nume_st);
DBMS_OUTPUT.PUT_LINE ('Nota maxima: '||v_max);
END;
/
EXECUTE afis_max;
MODIFICATI APOI PROCEDURA ASTFEL INCAT SA AFISEZE NOTA CEA MAI MARE A ORICARUI STUDENT.
CREATE OR REPLACE PROCEDURE afis_max_p(p_matr stud_XY.matr_st%TYPE) IS
v_matr_st stud_XY.matr_st%TYPE;
v_nume_st stud_XY.nume_st%TYPE;
v_max NUMBER;
BEGIN
SELECT stud_XY.matr_st, nume_st, MAX(nota) INTO v_matr_st,v_nume_st,v_max
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE stud_XY.matr_st=p_matr
GROUP BY stud_XY.matr_st, nume_st;
DBMS_OUTPUT.PUT_LINE ('Matricola student: '||v_matr_st);
DBMS_OUTPUT.PUT_LINE ('Nume student: '||v_nume_st);
DBMS_OUTPUT.PUT_LINE ('Nota maxima: '||v_max);
END;
/

EXECUTE afis_max_p(20);
EXECUTE afis_max_p(40);

4. INTEROGARE DE START..LlSTA TUTUROR STUDENTILOR

SELECT stud_XY.matr_st, nume_st,spec,


ROUND(AVG(nota),2) AS media,
COUNT(ex_XY.matr_st) AS numar_examene
FROM stud_XY LEFT JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
GROUP BY stud_XY.matr_st, nume_st, spec
ORDER BY 1;

-IMBUNATATIREA INTEROGARII ANTERIOARE PRIN LUAREA IN CONSIDERARE DOAR A EXAMENELOR PROMOVATE

SELECT stud_XY.matr_st, nume_st, spec,


ROUND(AVG(nota),2) AS media,
COUNT(ex_XY.matr_st) AS numar_examene
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE nota>4
GROUP BY stud_XY.matr_st, nume_st, spec
ORDER BY 1;

-AFISAREA DOAR A STUDENTILOR CU CEL MAI MARE NUMAR DE EXAMENE PROMOVATE

SELECT MAX(COUNT(matr_st))
FROM ex_XY
WHERE nota>4
GROUP BY matr_st;

UTILIZAREA ACESTEI SUBINTEROGARI IN INTEROGAREA DE MAI SUS; VOR FI DOUA CONDITII


SELECT stud_XY.matr_st, nume_st, spec,
ROUND(AVG(nota),2) AS media,
COUNT(ex_XY.matr_st) AS numar_examene
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE nota>4
GROUP BY stud_XY.matr_st, nume_st,spec
HAVING COUNT(ex_XY.matr_st)=(SELECT MAX(COUNT(matr_st))
FROM ex_XY
WHERE nota>4
GROUP BY matr_st)
;

PORNIND DE LA A 2-A INTEROGARE, SA SE CREEZE O PROCEDURA STOCATA PENTRU A AFISA SITUATIA UNUI
STUDENT PENTRU CARE SE COMUNICA NUMARUL MATRICOL.

MODIFICAREA INTEROGARII PENTRU A AFISA DOAR UN RAND DE DATE

SELECT stud_XY.matr_st, nume_st, spec,


ROUND(AVG(nota),2) AS media,
COUNT(ex_XY.matr_st) AS numar_examene
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE nota>4 AND stud_XY.matr_st=30
GROUP BY stud_XY.matr_st, nume_st, spec;

TRANSFERUL INTEROGARII INTR-O PROCEDURA STOCATA

CREATE OR REPLACE PROCEDURE afis_sstd IS


v_matr_st stud_XY.matr_st%TYPE;
v_nume_st stud_XY.nume_st%TYPE;
v_spec_st stud_XY.spec%TYPE;
v_medie NUMBER;
v_nr_ex NUMBER;

BEGIN

SELECT stud_XY.matr_st, nume_st, spec, ROUND(AVG(nota),2), COUNT(ex_XY.matr_st)


INTO v_matr_st, v_nume_st, v_spec_st, v_medie, v_nr_ex
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE nota>4 AND stud_XY.matr_st=30
GROUP BY stud_XY.matr_st, nume_st, spec;

DBMS_OUTPUT.PUT_LINE ('Matricola student: '||v_matr_st);


DBMS_OUTPUT.PUT_LINE ('Nume student: '||v_nume_st);
DBMS_OUTPUT.PUT_LINE ('Specializare: '||v_spec_st);
DBMS_OUTPUT.PUT_LINE ('Media: '||v_medie);
DBMS_OUTPUT.PUT_LINE ('Numar examene: '||v_nr_ex);
END;
/

EXECUTE afis_sstd;
TRANSFORMAREA PROCEDURII IN PROCEDURA CU PARAMENTRU DE INTRARE

CREATE OR REPLACE PROCEDURE afis_sstd_p (p_matr stud_XY.matr_st%TYPE) IS


v_matr_st stud_XY.matr_st%TYPE;
v_nume_st stud_XY.nume_st%TYPE;
v_spec_st stud_XY.spec%TYPE;
v_medie NUMBER;
v_nr_ex NUMBER;

BEGIN
SELECT stud_XY.matr_st, nume_st, spec,
ROUND(AVG(nota),2),
COUNT(ex_XY.matr_st)
INTO v_matr_st, v_nume_st, v_spec_st, v_medie, v_nr_ex
FROM stud_XY INNER JOIN ex_XY ON stud_XY.matr_st=ex_XY.matr_st
WHERE nota>4 AND stud_XY.matr_st=p_matr
GROUP BY stud_XY.matr_st, nume_st, spec;
DBMS_OUTPUT.PUT_LINE ('Matricola student: '||v_matr_st);
DBMS_OUTPUT.PUT_LINE ('Nume student: '||v_nume_st);
DBMS_OUTPUT.PUT_LINE ('Specializare: '||v_spec_st);
DBMS_OUTPUT.PUT_LINE ('Media: '||v_medie);
DBMS_OUTPUT.PUT_LINE ('Numar examene: '||v_nr_ex);
END;
/
EXECUTE afis_sstd_p(30);
EXECUTE afis_sstd_p(&dati_matricola);
Laborator 7
Proceduri, exceptie si functie. Utilizare parametru de iesire. Definire si utilizare cursor.
1. CREAREA A TREI TABELE
Crearea tabelelor se regaseste in script.
2. COMPLETAREA CU DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
....
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/
3. Determinarea si afisarea sumei creditelor obtinute la o anumita disciplina de catre toti studentii
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) AS totalcredite
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
-LIMITAREA LA O INREGISTRARE
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) AS totalcredite
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4 AND disc_XY.cod_disc=120
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
-ACTIVAREA AFISARII REZULTATELOR PROCEDURILOR
SET SERVEROUTPUT ON;
-PORNIND DE LA ACEASTA INTEROGARE, VOM CREA PROCEDURI A CAROR COMPLEXIATAE VA CRESTE GRADUAL
-procedura nestocata
DECLARE
v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) INTO v_cod_disc, v_den_disc, v_total
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4 AND disc_XY.cod_disc=120
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
DBMS_OUTPUT.PUT_LINE ('La disciplina cu codul '||v_cod_disc||' denumita '||v_den_disc||' s-au obtinut
'||v_total||' credite');
END;
/
-transformarea in procedura stocata

CREATE OR REPLACE PROCEDURE calc_crdt1 IS


v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) INTO v_cod_disc, v_den_disc, v_total
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4 AND disc_XY.cod_disc=120
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
DBMS_OUTPUT.PUT_LINE ('La disciplina cu codul '||v_cod_disc||' denumita '||v_den_disc||' s-au obtinut
'||v_total||' credite');
END;
/

EXECUTE calc_crdt1;
-transformarea in procedura stocata cu parametru
CREATE OR REPLACE PROCEDURE calc_crdt1_p (p_cod disc_XY.cod_disc%TYPE) IS
v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) INTO v_cod_disc, v_den_disc, v_total
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4 AND disc_XY.cod_disc=p_cod
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
DBMS_OUTPUT.PUT_LINE ('La disciplina cu codul '||v_cod_disc||' denumita '||v_den_disc||' s-au obtinut
'||v_total||' credite');
END;
/

-APELAREA PROCEDURII CU PARAMETRU IN DIVERSE VARIANTE

-apelare directa
EXECUTE calc_crdt1_p(110);
EXECUTE calc_crdt1_p(&dati_codul);

-apelare dintr-o procedura nestocata – nu se mai utilizeaza EXECUTE


BEGIN
calc_crdt1_p(110);
END;
/
BEGIN
calc_crdt1_p(&dati_codul);
END;
/

-apelare dintr-o procedura nestocata, cu memorarea si utilizarea raspunsului de la tastatura

DECLARE
v_slct_cod disc_XY.cod_disc%TYPE;
BEGIN
v_slct_cod:=&dati_codul;
DBMS_OUTPUT.PUT_LINE ('Situatia disciplinei cu codul '||v_slct_cod);
calc_crdt1_p(v_slct_cod);
END;
/

-OBS - numele variabilelor, parametrilor din procedura apelanta nu sunt legate de denumirile variabilelor si
parametrilor din procedura apelata
-sa se modifice procedura de mai sus, astfel incat sa cuprinda si situatia in care este comunicat un cod gresit.

CREATE OR REPLACE PROCEDURE calc_crdt1_p_ex (p_cod disc_XY.cod_disc%TYPE) IS


v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) INTO v_cod_disc, v_den_disc, v_total
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4 AND disc_XY.cod_disc=p_cod
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
DBMS_OUTPUT.PUT_LINE ('La disciplina cu codul '||v_cod_disc||' denumita '||v_den_disc||' s-au obtinut
'||v_total||' credite');
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('Nu exista disciplina ');
END;
/
-apelare prodecurii este identica, doar ca acum poate acoperi si situatia unui cod eronat

DECLARE
v_slct_cod disc_XY.cod_disc%TYPE;
BEGIN
v_slct_cod:=&dati_codul;
DBMS_OUTPUT.PUT_LINE ('Situatia disciplinei cu codul '||v_slct_cod);
calc_crdt1_p_ex(v_slct_cod);
END;
/
-transformarea procedurii calc_crdt1_p in functie si utilizarea informatiei pe care o returneaza functia
CREATE OR REPLACE FUNCTION f_calc_crdt1_p (p_cod disc_XY.cod_disc%TYPE) RETURN NUMBER IS
v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) INTO v_cod_disc, v_den_disc, v_total
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4 AND disc_XY.cod_disc=p_cod
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
DBMS_OUTPUT.PUT_LINE ('La disciplina cu codul '||v_cod_disc||' denumita '||v_den_disc||' s-au obtinut
'||v_total||' credite');
RETURN v_total;
END;
/
-apelarea functiei se poate face doar dintr-o procedura sau alta functie si presupune definirea unei variabile care
sa preia rezultatul returnat de functie, in situatia aceasta totalul de credite.

DECLARE
v_slct_cod disc_XY.cod_disc%TYPE;
v_prel_tot NUMBER;
BEGIN
v_slct_cod:=&dati_codul;
DBMS_OUTPUT.PUT_LINE ('Situatia disciplinei cu codul '||v_slct_cod);
v_prel_tot:=f_calc_crdt1_p(v_slct_cod);
DBMS_OUTPUT.PUT_LINE ('Aici se afiseaza totalul de '||v_prel_tot||' credite returnat de functie');
END;
/
-daca se doreste ca rezultatul totalului de credite sa fie extras din procedura, este necesar ca procedura sa aiba
parametru de iesire; punctul de pornire va fi tot procedura calc_crdt1_p

CREATE OR REPLACE PROCEDURE calc_crdt1_p_io (p_cod disc_XY.cod_disc%TYPE, p_ies_tot OUT NUMBER) IS


v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) INTO v_cod_disc, v_den_disc, v_total
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4 AND disc_XY.cod_disc=p_cod
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
DBMS_OUTPUT.PUT_LINE ('La disciplina cu codul '||v_cod_disc||' denumita '||v_den_disc||' s-au obtinut
'||v_total||' credite');
p_ies_tot:=v_total;
END;
/
-apelarea procedurii cu parametru de iesire seamana cu apelarea unei functii, in sensul ca presupune definirea unei
variabile care sa preia rezultatul (totalul de credite in acest caz) care "paraseste" procedura prin intermediul
parametrului de iesire

DECLARE
v_slct_cod disc_XY.cod_disc%TYPE;
v_prel_tot NUMBER;
BEGIN
v_slct_cod:=&dati_codul;
DBMS_OUTPUT.PUT_LINE ('Situatia disciplinei cu codul '||v_slct_cod);
calc_crdt1_p_io(v_slct_cod,v_prel_tot);
DBMS_OUTPUT.PUT_LINE ('Aici se afiseaza totalul de '||v_prel_tot||' credite returnat de procedura cu parametru
de iesire');
END;
/

4. Afisarea situatiei totalurilor de credite la toate disciplinele. Utilizarea cursorului

Pornind de la interogarea ititiala, se construieste procedura care utilizeza cursorul.


CREATE OR REPLACE PROCEDURE calc_crdt1_cs IS
CURSOR c IS
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) AS totalcredite
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Cod disciplina Denumire disciplina Numar credite');
FOR i IN c LOOP
DBMS_OUTPUT.PUT_LINE (' '||i.cod_disc||' '||i.den_disc||' '||i.totalcredite);
END LOOP;
END;
/

EXECUTE calc_crdt1_cs;
TEMA
SA SE SCRIE O INTEROGARE CARE SA AFISEZE

coddisc, dendisc, cea_mai_mare_nota, nr_studenti_participanti

SA SE AFISEZE ACEASTA SITUATIE UTILIZAND O PROCEDURA STOCATA CU CURSOR, CONFORM MODELULUI DIN
SEMINAR.

PORNIND TOT DE LA INTEROGAREA INITIALA, SA SE TRANSFORME IN INTEROGARE PENTRU O DISCIPLINA IAR APOI
SA SE CONSTRUIASCA PROGRESIV PROCEDURILE DE AFISARE CU PARAMETRU, CU FUNCTIE, CONFORM
SEMINARULUI, PRECUM SI PROCEDURILE ANONIME DE APELARE ALE ACESTORA.

SUPLIMENTAR, LA VARIANTA CU PARAMETRU DE IESIRE SAU LA VARIANTA CU FUNCTIE, IN UNA DIN PROCEDURILE
APELANTE SA SE AFISEZE, DUPA CAZ, MESAJUL

La disciplina cu codul ... au / nu au participat toti studentii.

RASPUNSURILE SE POSTEAZA PE PLATFORMA IN SECTIUNEA DEDICATA.

TERMEN 30.04.2023!!
Laborator 8
Alte modalitati de utilizare a cursorului in proceduri si functii.

1. CREAREA A TREI TABELE


Crearea tabelelor se regaseste in script.
2. COMPLETAREA CU DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
....
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/

Obs:
- prima cifra din codul disciplinei (1, 2 sau 3) se refera la anul in care se studiaza materia; am pus cate 2 materii
in fiecare an de studiu.
SET SERVEROUTPUT ON;
3. Alte variante de implementare a cursorului
- procedura stocata cu cursor – varianta 2
CREATE OR REPLACE PROCEDURE calc_crdt1_cs2 IS
CURSOR c IS
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite)
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Cod disciplina Denumire disciplina Numar credite');
OPEN c;
LOOP
FETCH c INTO v_cod_disc, v_den_disc, v_total;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (' '||v_cod_disc||' '||v_den_disc||' '||v_total);
END LOOP;
CLOSE c;

END;
/

EXECUTE calc_crdt1_cs2;
- procedura stocata cu cursor – varianta 3

CREATE OR REPLACE PROCEDURE calc_crdt1_cs3 IS


CURSOR c IS
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite)
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
v_cod_disc disc_XY.cod_disc%TYPE;
v_den_disc disc_XY.den_disc%TYPE;
v_total NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Cod disciplina Denumire disciplina Numar credite');
OPEN c;
FETCH c INTO v_cod_disc, v_den_disc, v_total;
WHILE c%FOUND LOOP
DBMS_OUTPUT.PUT_LINE (' '||v_cod_disc||' '||v_den_disc||' '||v_total);
FETCH c INTO v_cod_disc, v_den_disc, v_total;
END LOOP;
CLOSE c;
END;
/
EXECUTE calc_crdt1_cs3;
OBS: in practica pentru parcurgerea liniilor cursorului se utilizeaza cel mai mult varianta 1 (cea din seminarul
anterior) si varianta 2.

4. Uilizarea unei functii in care se adauga un camp denumit an_st, precum si calcularea unui total al creditelor la
sfarsitul tabelului; de asemenea se vor adauga si linii grafice de separare a informatiilor. Totalul creditelor se
va utiliza ulterior in procedura apelanta, pentru a calcula o valoare medie a creditelor pe fiecare disciplina.
Se va utiliza functia SUBSTR a carei sintaxa este SUBSTR(string, start_position [, length ])

CREATE OR REPLACE FUNCTION f_calc_crdt1_cs_tot RETURN NUMBER IS


CURSOR c IS
SELECT disc_XY.cod_disc, den_disc, SUM(nr_credite) AS totalcredite, SUBSTR(disc_XY.cod_disc,1,1) AS an_st
FROM disc_XY INNER JOIN ex_XY ON disc_XY.cod_disc=ex_XY.cod_disc
WHERE nota>4
GROUP BY disc_XY.cod_disc, den_disc
ORDER BY 1;
v_tot_cr NUMBER :=0;
BEGIN

DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------------------------');
DBMS_OUTPUT.PUT_LINE ('Cod disciplina Denumire disciplina Numar credite An studiu');
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------------------------');
FOR i IN c LOOP

DBMS_OUTPUT.PUT_LINE (' '||i.cod_disc||' '||i.den_disc||' '||i.totalcredite||' '||i.an_st);


v_tot_cr:=v_tot_cr+i.totalcredite;
END LOOP;
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------------------------');
RETURN v_tot_cr;
END;
/

Apelarea si utilizarea rezultatului functiei

DECLARE
v_prel_tot_cr NUMBER;
v_nrdisc NUMBER;
v_med NUMBER;
BEGIN
v_prel_tot_cr:=f_calc_crdt1_cs_tot;
DBMS_OUTPUT.PUT_LINE ('Total credite '||v_prel_tot_cr);
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------------------------');
SELECT COUNT(DISTINCT cod_disc) INTO v_nrdisc
FROM ex_XY;
v_med:=ROUND(v_prel_tot_cr/v_nrdisc,2);
DBMS_OUTPUT.PUT_LINE ('Medie '||v_med);
END;
/
Laborator 9
Alte exemple de proceduri si functii.
Apelarea repetitiva dintr-un cursor a unei functii stocate anterior. Utilizarea a doua cursoare.
1. CREAREA A TREI TABELE
Crearea tabelelor se regaseste in script.
2. COMPLETAREA CU DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/

Vom realiza diverse proceduri, deci avem nevoie de activarea afisarii.


SET SERVEROUTPUT ON;
3. Procedura stocata - totalul incasarilor realizate de soferii unei categorii precizata ca parametru
SELECT categ, SUM(incasari) AS total
FROM curse_XY INNER JOIN soferi_XY ON curse_XY.codpers=soferi_XY.codpers
GROUP BY categ
HAVING UPPER(categ)='B';
Transferarea instructiunii SELECT intr-o procedura stocata cu parametru
CREATE OR REPLACE PROCEDURE calc_tot_categ (p_categ soferi_XY.categ%TYPE) AS
v_categ soferi_XY.categ%TYPE;
v_tot NUMBER;
BEGIN
SELECT categ, SUM(incasari) INTO v_categ, v_tot
FROM curse_XY INNER JOIN soferi_XY ON curse_XY.codpers=soferi_XY.codpers
GROUP BY categ
HAVING UPPER(categ)=UPPER(p_categ);
DBMS_OUTPUT.PUT_LINE ('Totalul categoriei '||v_categ||' este '||v_tot);
END;
/
EXECUTE calc_tot_categ('&dati_categoria');
Transferarea instructiunii SELECT intr-o functie cu parametru
CREATE OR REPLACE FUNCTION total_categ (pf_categ soferi_XY.categ%TYPE) RETURN NUMBER AS
vf_total NUMBER;
BEGIN
SELECT SUM(incasari) INTO vf_total
FROM curse_XY INNER JOIN soferi_XY ON curse_XY.codpers=soferi_XY.codpers
GROUP BY categ
HAVING UPPER(categ)=UPPER(pf_categ);
RETURN vf_total;
END;
/
Apelarea functiei dintr-o procedura nestocata.

DECLARE
v_prel_categ soferi_XY.categ%TYPE;
v_tot NUMBER;
BEGIN
v_prel_categ:=UPPER('&dati_categoria');
v_tot:=total_categ(v_prel_categ);
DBMS_OUTPUT.PUT_LINE ('Totalul categoriei '||v_prel_categ||' este '||v_tot);
END;
/

Trecerea la cursor pentru a afisa totalul incasarilor fiecarei categorii; totalul fiecarei categorii se va calcula
repetitiv cu ajutorul functiei stocate total_categ care are ca argument o valoare din cursor; de asemenea, se va
realiza si un total general la sfarsitul tabelului calculat in doua moduri.
-pregatire SELECT
SELECT categ
FROM soferi_XY
GROUP BY categ;
sau
SELECT DISTINCT categ
FROM soferi_XY;
-procedura de calcul al totalului la fiecare categorie si obtinerea, in acelasi timp, si al totalului general.
CREATE OR REPLACE PROCEDURE calc_tot_categ_c1 AS
CURSOR c IS
SELECT DISTINCT categ
FROM soferi_XY;
v_calc NUMBER:=0;
v_tg NUMBER:=0;
BEGIN
FOR i IN c LOOP
v_calc:=total_categ(i.categ);
v_tg:=v_tg+v_calc;
DBMS_OUTPUT.PUT_LINE ('Totalul categoriei '||i.categ||' este '||v_calc);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('--------------------------------------');
DBMS_OUTPUT.PUT_LINE ('Total general '||v_tg);
END;
/
EXECUTE calc_tot_categ_c1;

-Varianta a doua cu cap de tabel si calculul unui total general calculat intr-un SELECT separat;
CREATE OR REPLACE PROCEDURE calc_tot_categ_c2 AS
CURSOR c IS
SELECT DISTINCT categ
FROM soferi_XY;
v_calc NUMBER:=0;
v_tg NUMBER:=0;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Cod categorie Total incasari');
FOR i IN c LOOP
v_calc:=total_categ(i.categ);
DBMS_OUTPUT.PUT_LINE (' '||i.categ||' '||v_calc);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('--------------------------------------');
SELECT SUM(incasari) INTO v_tg
FROM curse_XY;
DBMS_OUTPUT.PUT_LINE ('Total general '||v_tg);
END;
/

EXECUTE calc_tot_categ_c2;

4. Intrucat oricare dintre soferi poate efectua curse cu oricare dintre vehiculele din parc, sa se afiseze totalul
incasarilor pe fiecare din combinatiile POSIBILE!! dintre soferi si vehicule, ordonate alfabetic dupa numele
soferilor si apoi dupa id_ul auto; daca acest total este zero, sa se afiseze un mesaj in care se precizeaza ca
acel sofer nu a efectuat curse cu acel vehicul.

Construirea functiei de calcul a totalului incasarilor pe fiecare sofer si auto.

CREATE OR REPLACE FUNCTION total_sf_auto (p_cp soferi_XY.codpers%TYPE, p_id parc_XY.id_auto%TYPE)


RETURN NUMBER AS
v_tot_f NUMBER;
BEGIN
SELECT SUM(incasari) INTO v_tot_f
FROM curse_XY
GROUP BY codpers,id_auto
HAVING codpers=p_cp AND id_auto=p_id;

RETURN v_tot_f;

EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 0;

END;
/
Afisarea tuturor combinatiilor posibile--produs cartezian, relatie fara WHERE

SELECT codpers,id_auto,nume,nrauto
FROM soferi_XY,parc_XY
ORDER BY nume,id_auto;
Procedura de apelare a functiei de mai sus

CREATE OR REPLACE PROCEDURE list_inc AS


CURSOR c IS
SELECT codpers,id_auto,nume,nrauto
FROM soferi_XY,parc_XY
ORDER BY nume,id_auto;
v_total NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE (' Nume sofer Nr auto Suma');
FOR i IN c LOOP
v_total:=total_sf_auto(i.codpers,i.id_auto);
IF v_total > 0 THEN
DBMS_OUTPUT.PUT_LINE (' '||i.nume||' '||i.nrauto||' '||v_total);
ELSE
DBMS_OUTPUT.PUT_LINE (' '||i.nume||' '||i.nrauto||' nu a efectuat curse');
END IF;
END LOOP;
END;
/

EXECUTE list_inc;

5. Procedura de mai sus poate fi imbunatatita


prin utilizarea a doua cursoare legate intre ele,
astfel incat numele soferului sa apara o singura
data; prin aceasta metoda se poate obtine o
situatie mai amanuntita, dupa cum urmeaza.

CREATE OR REPLACE PROCEDURE list_inc_supl AS

CURSOR c1 IS
SELECT codpers,nume
FROM soferi_XY
ORDER BY nume;

v_trs_cod soferi_XY.codpers%TYPE;

CURSOR c2 IS
SELECT codpers,id_auto,nrauto
FROM soferi_XY,parc_XY
WHERE codpers=v_trs_cod
ORDER BY nume,id_auto;

v_total NUMBER;
contor NUMBER :=0;
v_tot_sof NUMBER :=0;
BEGIN
DBMS_OUTPUT.PUT_LINE (' Nume sofer Nr auto Suma');
DBMS_OUTPUT.PUT_LINE ('-------------------------------------------------------------');
FOR i IN c1 LOOP
DBMS_OUTPUT.PUT_LINE (' '||i.nume);
v_tot_sof:=0;
v_trs_cod:=i.codpers;
FOR j IN c2 LOOP
v_total:=total_sf_auto(j.codpers,j.id_auto);
IF v_total > 0 THEN
DBMS_OUTPUT.PUT_LINE (' '||j.nrauto||' '||v_total);
contor:=contor+1;
ELSE
DBMS_OUTPUT.PUT_LINE (' '||j.nrauto||' nu a efectuat curse');
END IF;
v_tot_sof:=v_tot_sof+v_total;
END LOOP;
DBMS_OUTPUT.PUT_LINE ('-------------------------------------------------------------');
DBMS_OUTPUT.PUT_LINE (' Total sofer '||v_tot_sof);
DBMS_OUTPUT.PUT_LINE ('-------------------------------------------------------------');
END LOOP;
DBMS_OUTPUT.PUT_LINE (' Combinatii realizate '||contor);
END;
/

EXECUTE list_inc_supl;

Obs:
Cand se utilizeaza doua cursoare interconectate, este necesara declararea unei variabile intre declararile celor
doua cursoare, variabila ce este utilizata pentru transferul valorii ce provine de la primul cursor si intervine in cel
de-al doilea cursor.
Laborator 10
Alte exemple de blocuri de proceduri si functii. Utilizarea unor variabile de tip obiect predefinit.
Vom realiza diverse proceduri si functii, deci avem nevoie de activarea afisarii.
SET SERVEROUTPUT ON;
1. CREAREA A TREI TABELE
Crearea tabelelor se regaseste in script.
2. COMPLETAREA CU DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
Trebuie respectata ordinea si tipul campurilor.
BEGIN
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/
3. Crearea si utilizarea comparativa a unei proceduri cu doi parametrii de iesire versus o functie cu un
parametru de iesire.

Interogari pregatitoare
SELECT EXTRACT(MONTH FROM data_cursa) AS luna, SUM(incasari), COUNT(id_cursa)
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa);
SELECT EXTRACT(MONTH FROM data_cursa) AS luna, SUM(incasari), COUNT(id_cursa)
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa)
HAVING EXTRACT(MONTH FROM data_cursa)=1;
SELECT SUM(incasari), COUNT(id_cursa)
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa)
HAVING EXTRACT(MONTH FROM data_cursa)=1;
Preluarea interogarii in functie de unde incercam sa returnam doua valori
CREATE OR REPLACE FUNCTION sit_lun_f1 (p_luna NUMBER) RETURN NUMBER AS
v_tot_inc NUMBER;
v_nrc NUMBER;
BEGIN
SELECT SUM(incasari), COUNT(id_cursa) INTO v_tot_inc,v_nrc
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa)
HAVING EXTRACT(MONTH FROM data_cursa)=p_luna;
RETURN v_tot_inc;
RETURN v_nrc;
END;
/
Utilizarea functiei
DECLARE
v_prel_luna NUMBER;
v1 NUMBER;
v2 NUMBER;
BEGIN
v_prel_luna:=&dati_luna;
v1:=sit_lun_f1(v_prel_luna);
v2:=sit_lun_f1(v_prel_luna);
DBMS_OUTPUT.PUT_LINE ('Totalul lunii '||v_prel_luna||' este '||v1||' iar numarul de curse este '||v2);
END;
/

Se observa ca nu se poate extrage decat


o informatie.
Pentru a extrage doua sau mai multe informatii,
o prima varianta este de a tranforma functia intr-o
functie cu parametru de iesire.

CREATE OR REPLACE FUNCTION sit_lun_f2 (p_luna NUMBER, p_nrc OUT NUMBER) RETURN NUMBER AS
v_tot_inc NUMBER;
BEGIN
SELECT SUM(incasari), COUNT(id_cursa) INTO v_tot_inc,p_nrc
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa)
HAVING EXTRACT(MONTH FROM data_cursa)=p_luna;
RETURN v_tot_inc;
END;
/
Utilizarea functiei
DECLARE
v_prel_luna NUMBER;
v1 NUMBER;
v2 NUMBER;
BEGIN
v_prel_luna:=&dati_luna;
v1:=sit_lun_f2(v_prel_luna, v2);
DBMS_OUTPUT.PUT_LINE ('Totalul lunii '||v_prel_luna||' este '||v1||' iar numarul de curse este '||v2);
END;
/
O alta metoda prin care se pot obtine doua sau mai multe valori din apelarea unei functii, este prin definirea unui
nou tip de variabila obiect care sa incapsuleze in campuri distincte aceste valori.

CREATE OR REPLACE TYPE OB_INF AS OBJECT (v_ob1 NUMBER, v_ob2 NUMBER);


/
Functia va fi asemanatoare cu prima varinata cu exceptia instructiunii de „incapsulare” a informatiei.
CREATE OR REPLACE FUNCTION sit_lun_f3 (p_luna NUMBER) RETURN OB_INF AS
v_tot_inc NUMBER;
v_nrc NUMBER;
BEGIN
SELECT SUM(incasari), COUNT(id_cursa) INTO v_tot_inc,v_nrc
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa)
HAVING EXTRACT(MONTH FROM data_cursa)=p_luna;
RETURN OB_INF(v_tot_inc,v_nrc);
END;
/
Utilizarea functiei
DECLARE
v_prel_luna NUMBER;
v1_2 OB_INF;
v1 NUMBER;
v2 NUMBER;
BEGIN
v_prel_luna:=&dati_luna;
v1_2:=sit_lun_f3(v_prel_luna);
v1:=v1_2.v_ob1;
v2:=v1_2.v_ob2;
DBMS_OUTPUT.PUT_LINE ('Totalul lunii '||v_prel_luna||' este '||v1||' iar numarul de curse este '||v2);
END;
/

OBS: functia returneaza „capsula” cu cele doua informatii in interiorul ei.


Apelarea functiei se poate face doar prin utilizarea unei variabile intermediare de tipul OB_INF, care va prelua in
ea cele doua informatii, pe care apoi le va putea afisa separat.

Aceasta functie poate fi utilizata suucesiv, prin apelare dintr-un cursor, pentru a calcula aceste informatii pentru
fiecare luna.
DECLARE
CURSOR c_luna IS
SELECT EXTRACT(MONTH FROM data_cursa) AS luna
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa)
ORDER BY 1;

v1_2 OB_INF;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Luna Suma Nr_curse');
DBMS_OUTPUT.PUT_LINE ('------------------------------------');
FOR i IN c_luna LOOP
v1_2:=sit_lun_f3(i.luna);
DBMS_OUTPUT.PUT_LINE (' '||i.luna||' '||v1_2.v_ob1||' '||v1_2.v_ob2);
END LOOP;
END;
/

O utilizare inedita a instructiunilor TO_CHAR, TO_DATE

DECLARE
CURSOR c_luna IS
SELECT EXTRACT(MONTH FROM data_cursa) AS luna
FROM curse_XY
GROUP BY EXTRACT(MONTH FROM data_cursa)
ORDER BY 1;
v1_2 OB_INF;
v_nluna VARCHAR2(15);
BEGIN
DBMS_OUTPUT.PUT_LINE (' Luna Suma Nr_curse');
DBMS_OUTPUT.PUT_LINE ('--------------------------------------------');
FOR i IN c_luna LOOP
v1_2:=sit_lun_f3(i.luna);
v_nluna:=TO_CHAR(TO_DATE(i.luna, 'MM'), 'MONTH','NLS_DATE_LANGUAGE = romanian');
DBMS_OUTPUT.PUT_LINE (' '||v_nluna||' '||v1_2.v_ob1||' '||v1_2.v_ob2);
END LOOP;
END;
/

OBS: stergera unui tip predefinit de variabila se realizeaza astfel

DROP TYPE OB_INF;


Laborator 11
Alte exemple de proceduri si functii. Afisare ordonata. Triggere
Vom realiza diverse proceduri si functii, deci avem nevoie de activarea afisarii.
SET SERVEROUTPUT ON;
1. CREAREA A TREI TABELE
Crearea tabelelor se regaseste in script.
2. COMPLETAREA CU DATE
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
BEGIN
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/
3. Crearea si utilizarea unei proceduri cu doi parametrii de iesire/functie cu un parametru de iesire.
Consideratii preliminare; pregatirea interogarilor.
SELECT parc_XY.id_auto, AVG(incasari) AS mdpa, MAX(incasari) AS mxpa
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto
ORDER BY 1;
SELECT AVG(AVG(incasari)), MAX(MAX(incasari))
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto;
SELECT AVG(incasari), MAX(incasari)
FROM curse_XY;
Rotunjiri
SELECT parc_XY.id_auto, ROUND(AVG(incasari),2) AS mdpa, MAX(incasari) AS mxpa
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto
ORDER BY 1;
SELECT ROUND(AVG(AVG(incasari)),2), MAX(MAX(incasari))
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto;
SELECT ROUND(AVG(incasari),2), MAX(incasari)
FROM curse_XY;
Procedura de calcul al mediei si maximului incasarilor fiecarui vehicul
CREATE OR REPLACE PROCEDURE sit_auto_proc (p_idauto IN parc_XY.id_auto%TYPE, p_mdpa OUT NUMBER, p_mxpa OUT
NUMBER) AS
BEGIN
SELECT ROUND(AVG(incasari),2),MAX(incasari) INTO p_mdpa,p_mxpa
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto
HAVING parc_XY.id_auto=p_idauto
ORDER BY 1;
END;
/
Functia care face acelasi lucru
CREATE OR REPLACE FUNCTION sit_auto_func (p_idauto IN parc_XY.id_auto%TYPE, p_mxpa OUT NUMBER) RETURN NUMBER AS
v_mdpa NUMBER;
BEGIN
SELECT ROUND(AVG(incasari),2),MAX(incasari) INTO v_mdpa,p_mxpa
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto
HAVING parc_XY.id_auto=p_idauto
ORDER BY 1;
RETURN v_mdpa;
END;
/
Utilizarea procedurii de mai sus
CREATE OR REPLACE PROCEDURE sit_auto1 AS
CURSOR c_auto IS
SELECT id_auto,nrauto
FROM parc_XY;
v_med NUMBER;
v_max NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Id_auto Nrauto Media Maxim');
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
FOR i IN c_auto LOOP
sit_auto_proc(i.id_auto,v_med, v_max);
DBMS_OUTPUT.PUT_LINE (' '||i.id_auto||' '||i.nrauto||' '||v_med||' '||v_max);
END LOOP;
END;
/
EXECUTE sit_auto1;
Utilizarea functiei de mai sus
CREATE OR REPLACE PROCEDURE sit_auto2 AS
CURSOR c_auto IS
SELECT id_auto,nrauto
FROM parc_XY;
v_med NUMBER;
v_max NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Id_auto Nrauto Media Maxim');
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
FOR i IN c_auto LOOP
v_med:=sit_auto_func(i.id_auto,v_max);
DBMS_OUTPUT.PUT_LINE (' '||i.id_auto||' '||i.nrauto||' '||v_med||' '||v_max);
END LOOP;
END;
/
EXECUTE sit_auto2;
Vom realiza o imbunatatire a procedurii de mai sus prin utilizarea si a celorlalte informatii.
CREATE OR REPLACE PROCEDURE sit_auto3 AS
CURSOR c_auto IS
SELECT id_auto,nrauto
FROM parc_XY;
v_med NUMBER;
v_max NUMBER;
v_mmed NUMBER;
v_mmax NUMBER;
v_medc NUMBER;
v_maxc NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE ('Id_auto Nrauto Media Maxim');
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
FOR i IN c_auto LOOP
v_med:=sit_auto_func(i.id_auto,v_max);
DBMS_OUTPUT.PUT_LINE (' '||i.id_auto||' '||i.nrauto||' '||v_med||' '||v_max);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
SELECT ROUND(AVG(AVG(incasari)),2), MAX(MAX(incasari)) INTO v_mmed,v_mmax
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto;
SELECT ROUND(AVG(incasari),2), MAX(incasari) INTO v_medc,v_maxc
FROM curse_XY;
DBMS_OUTPUT.PUT_LINE (' Media mediilor '||v_mmed);
DBMS_OUTPUT.PUT_LINE (' Maximul maximelor '||v_mmax);
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
DBMS_OUTPUT.PUT_LINE (' Media curselor '||v_medc);
DBMS_OUTPUT.PUT_LINE (' Maximul curselor '||v_maxc);
END;
/
EXECUTE sit_auto3;
Vom imbunatatire procedura de mai sus prin utilizarea si a afisarii ordonate, realizata prin transferul informatiilor
in variabile de lungime fixa.
CREATE OR REPLACE PROCEDURE sit_auto4 AS
CURSOR c_auto IS
SELECT id_auto,nrauto
FROM parc_XY;
v_med NUMBER;
v_max NUMBER;
v_mmed NUMBER;
v_mmax NUMBER;
v_medc NUMBER;
v_maxc NUMBER;
c1 CHAR(15);
c2 CHAR(15);
c3 CHAR(15);
c4 CHAR(15);
BEGIN
DBMS_OUTPUT.PUT_LINE ('Id_auto Nrauto Media Maxim');
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
FOR i IN c_auto LOOP
v_med:=sit_auto_func(i.id_auto,v_max);
c1:=TO_CHAR(i.id_auto);
c2:=TO_CHAR(i.nrauto);
c3:=TO_CHAR(v_med);
c4:=TO_CHAR(v_max);
DBMS_OUTPUT.PUT_LINE (' '||c1||c2||c3||c4);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
SELECT ROUND(AVG(AVG(incasari)),2), MAX(MAX(incasari)) INTO v_mmed,v_mmax
FROM parc_XY INNER JOIN curse_XY ON parc_XY.id_auto=curse_XY.id_auto
GROUP BY parc_XY.id_auto;
SELECT ROUND(AVG(incasari),2), MAX(incasari) INTO v_medc,v_maxc
FROM curse_XY;
DBMS_OUTPUT.PUT_LINE (' Media mediilor '||v_mmed);
DBMS_OUTPUT.PUT_LINE (' Maximul maximelor '||v_mmax);
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
DBMS_OUTPUT.PUT_LINE (' Media curselor '||v_medc);
DBMS_OUTPUT.PUT_LINE (' Maximul curselor '||v_maxc);
END;
/
EXECUTE sit_auto4;
4. Crearea unei clone a tabelului parc, adaugarea a doua coloane noi in acest tabel si completarea lor cu
informatiile referitoare la medie si maxim.
CREATE TABLE parc_XY_C AS
SELECT * FROM parc_XY;
ALTER TABLE parc_XY_C ADD PRIMARY KEY(id_auto);
ALTER TABLE parc_XY_C ADD (med NUMBER, max NUMBER);
CREATE OR REPLACE PROCEDURE sit_auto5 AS
CURSOR c_auto IS
SELECT id_auto,nrauto
FROM parc_XY;
v_med NUMBER;
v_max NUMBER;
BEGIN
FOR i IN c_auto LOOP
v_med:=sit_auto_func(i.id_auto,v_max);
UPDATE parc_XY_C SET
med=v_med,
max=v_max
WHERE id_auto=i.id_auto;
END LOOP;
END;
/
EXECUTE sit_auto5;
SELECT * FROM parc_XY_C;
5. Actualizarea automata a mediei si maximului din tabelul parc_XY_C, la fiecare adaugare a unei curse, realizata
cu ajutorul unui trigger care va apela procedura stocata sit_auto5.

CREATE OR REPLACE TRIGGER modif_sit AFTER INSERT OR DELETE OR UPDATE ON curse_XY


BEGIN
DBMS_OUTPUT.PUT_LINE ('Atentie !!! S-au operat modificari in tabela curse');
sit_auto5;
END;
/

Punrea in evidenta a functionarii trigger-ului, prin realizarea unor operatiuni de adaugare/stergere, respectiv
modificare.

SELECT * FROM parc_XY_C;


INSERT INTO curse_XY VALUES(1015, 30, 300, TO_DATE('05.05.2023','dd.mm.yyyy'),1400 );
SELECT * FROM parc_XY_C;

DELETE FROM curse_XY WHERE id_cursa=1015;


SELECT * FROM parc_XY_C;

SELECT * FROM parc_XY_C;


UPDATE curse_XY SET INCASARI=1600 WHERE id_cursa=1001;
SELECT * FROM parc_XY_C;

UPDATE curse_XY SET INCASARI=1233 WHERE id_cursa=1001;


SELECT * FROM parc_XY_C;
Laborator 12
Recapitulare finala 1. Instructiuni SELECT. O alta utilizare a aliasurilor. Primele proceduri si functii
1. Definirea tabelelor
Crearea tabelelor se regaseste in script.
2. Adaugarea datelor
Popularea tabelelor cu date se face se realizeaza
in ordinea „parinti”  „copii”.
BEGIN
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/
OBS:
- produsul cu codul 32 exista doar in tabela catalog iar produsul cu codul 31 doar s-a achizitionat
- adaugare camp nou - 'categ' in tabelul ctl_XY
ALTER TABLE ctl_XY ADD categ VARCHAR2(20);
- inlocuirea cu categoria 'alimente' pentru produsele al caror cod de produs incepe cu 1
- inlocuirea cu categoria 'electronice' pentru produsele al caror cod de produs incepe cu 2
- inlocuirea cu categoria 'jucarii' pentru produsele al caror cod de produs incepe cu 3
UPDATE ctl_XY
SET categ='alimente'
WHERE SUBSTR(cod_p,1,1) ='1';
SELECT * FROM ctl_XY;
- realizarea simultana a actualizarilor utilizarea lui CASE
UPDATE ctl_XY
SET categ=
CASE
WHEN SUBSTR(cod_p,1,1) ='1' THEN 'alimente'
WHEN SUBSTR(cod_p,1,1) ='2' THEN 'electronice'
WHEN SUBSTR(cod_p,1,1) ='3' THEN 'jucarii'
END;
3. Interogari progresive
- 3a) interogare simpla – campuri din 2 sau 3 tabele
SELECT ctl_XY.cod_p, den_prod, tip_op,cant
FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p
ORDER BY ctl_XY.cod_p,tip_op;
SELECT ctl_XY.cod_p, den_prod, tip_op,cant
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
ORDER BY 1,3;
SELECT ctl_XY.cod_p, misc_XY.cod_misc AS Nrfactura, den_prod, tip_op,cant
FROM ctl_XY, misc_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p AND misc_XY.cod_misc=detalii_misc_XY.cod_misc
ORDER BY ctl_XY.cod_p, misc_XY.cod_misc ;
SELECT ctl_XY.cod_p, misc_XY.cod_misc AS Nrfactura, den_prod, tip_op,cant
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
INNER JOIN misc_XY ON detalii_misc_XY.cod_misc=misc_XY.cod_misc
ORDER BY 1,2;
- 3b) semnul (+), LEFT JOIN, RIGHT JOIN
SELECT ctl_XY.cod_p, den_prod, tip_op,cant
FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p(+)
ORDER BY 1,3;
SELECT ctl_XY.cod_p, den_prod, tip_op,cant
FROM ctl_XY LEFT JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
ORDER BY 1,3;
SELECT ctl_XY.cod_p, den_prod, tip_op,cant
FROM detalii_misc_XY RIGHT JOIN ctl_XY ON detalii_misc_XY.cod_p=ctl_XY.cod_p
ORDER BY 1,3;
- 3c) filtrari DIVERSE
- filtrarea cantitativa
SELECT ctl_XY.cod_p, den_prod, tip_op,cant
FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p AND cant>15
ORDER BY ctl_XY.cod_p;
SELECT ctl_XY.cod_p, den_prod, tip_op,cant
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
WHERE cant>15
ORDER BY 1;
– filtrare calitativa produsele din catalog la care NU s-au realizat aprovizionari si vanzari
SELECT ctl_XY.cod_p, den_prod
FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p(+) AND tip_op IS NULL
ORDER BY ctl_XY.cod_p;
SELECT ctl_XY.cod_p, den_prod
FROM ctl_XY LEFT JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
WHERE tip_op IS NULL
ORDER BY 1;
SELECT cod_p, den_prod
FROM ctl_XY
WHERE cod_p NOT IN (SELECT DISTINCT cod_p FROM detalii_misc_XY)
ORDER BY cod_p;

- 3d) grupare si calcul agregat


SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_cant
FROM ctl_XY LEFT JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
ORDER BY 1,3;
- totalul cantitativ al aprovizionarilor
- filtrare anterioara gruparii
SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_cant
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
WHERE UPPER (tip_op)='A'
GROUP BY ctl_XY.cod_p, den_prod, tip_op
ORDER BY 1;
- filtrare ulterioara gruparii (filtrarea grupurilor)
SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_apr
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='A'
ORDER BY 1;
Obs: Daca filtrarea se refera la un camp calculat in urma gruparii, evident ca poate fi realizata doar pe clauza HAVING
- totalul cantitativ al vanzarilor
SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_vanz
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='V'
ORDER BY 1;

- 3e) UNIFICAREA celor doua tabele in FROM - utilizare ALIASURI


- legatura realizata in WHERE
SELECT ta.cod_p, ta.den_prod, Total_apr,Total_vanz
FROM
(SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_apr
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='A'
ORDER BY 1) ta
,
(SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_vanz
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='V'
ORDER BY 1) tv
WHERE ta.cod_p=tv.cod_p(+);

- legatura realizata in LEFT JOIN


SELECT ta.cod_p, ta.den_prod, Total_apr,Total_vanz
FROM
(SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_apr
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='A'
ORDER BY 1) ta

LEFT JOIN
(SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_vanz
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='V'
ORDER BY 1) tv
ON ta.cod_p=tv.cod_p;

- 3f) utilizarea selectiei de mai sus in calcularea STOCULUI – utilizarea NVL


SELECT ta.cod_p, ta.den_prod, Total_apr,NVL(Total_vanz,0) AS Total_vanz, Total_apr-NVL(Total_vanz,0) AS Stoc
FROM
(SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_apr
FROM ctl_XY LEFT JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='A'
ORDER BY ctl_XY.cod_p) ta
LEFT JOIN
(SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant) AS Total_vanz
FROM ctl_XY LEFT JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY ctl_XY.cod_p, den_prod, tip_op
HAVING UPPER (tip_op)='V'
ORDER BY ctl_XY.cod_p) tv
ON ta.cod_p=tv.cod_p
ORDER BY Stoc DESC;
4. Calcule agregate, parametrii, subinterogari
- 4a) filtrari cu parametrii comunicati de la tastatura
- parametru de tip text – totalul valoric al aprovizionarilor sau vanzarilor
SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(cant*pret) AS Total_valoric
FROM ctl_XY LEFT JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
WHERE UPPER (tip_op)=UPPER('&tipul_operatiei')
GROUP BY ctl_XY.cod_p, den_prod,tip_op
ORDER BY ctl_XY.cod_p;
- 4b) calcul agregat si filtrare – total valoric miscari in luna curenta
SELECT EXTRACT(MONTH from data_misc) AS luna, tip_op, SUM(cant*pret) AS Total_val
FROM misc_XY INNER JOIN detalii_misc_XY ON misc_XY.cod_misc=detalii_misc_XY.cod_misc
GROUP BY EXTRACT (MONTH from data_misc),tip_op
HAVING EXTRACT (MONTH from data_misc) = EXTRACT (MONTH from SYSDATE)
ORDER BY Total_val;

- 4c) subinterogari
- toate aprovizionarile cu pretul sub media preturilor de aprovizionare
- subinterogarea trebuie sa returneze doar o valoare
- testarea subinterogarii
SELECT AVG(pret)
FROM detalii_misc_XY
WHERE UPPER (tip_op)='A';
- utilizarea subinterogarii intr-o selectie simpla

SELECT ctl_XY.cod_p, den_prod, tip_op, pret, cant, pret*cant AS Valoare


FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p
AND UPPER (tip_op)='A'
AND pret<=(SELECT AVG(pret)
FROM detalii_misc_XY
WHERE UPPER (tip_op)='A')
ORDER BY 1;
- utilizarea subinterogarii intr-o selectie agregata – se elimina campurile pret si cant care poate avea valori
diferite de la o aprovizionare la alta; pretul insa ramane ca si criteriu de filtrare

SELECT ctl_XY.cod_p, den_prod, tip_op, SUM(pret*cant) AS Total_val


FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p
AND UPPER (tip_op)='A'
AND pret<=(SELECT AVG(pret)
FROM detalii_misc_XY
WHERE UPPER (tip_op)='A')
GROUP BY ctl_XY.cod_p, den_prod, tip_op
ORDER BY 1;

5. Combinarea a doua grupari

-stabilirea categoriei pentru care totalul valoric al aprovizionarilor este cel mai mare
- testare subinterogarii
SELECT SUBSTR(cod_p,1,1), tip_op, SUM(cant*pret)
FROM detalii_misc_XY
WHERE UPPER(tip_op)='A'
GROUP BY SUBSTR(cod_p,1,1),tip_op
ORDER BY SUBSTR(cod_p,1,1);
- filtrarea doar a unei valori si anume a maximului
SELECT SUBSTR(cod_p,1,1), tip_op, MAX(SUM(cant*pret))
FROM detalii_misc_XY
WHERE UPPER(tip_op)='A'
GROUP BY SUBSTR(cod_p,1,1),tip_op;

- renuntarea la campurile suplimentare


SELECT MAX(SUM(cant*pret))
FROM detalii_misc_XY
WHERE UPPER(tip_op)='A'
GROUP BY SUBSTR(cod_p,1,1),tip_op;
- utilizarea acestei subinterogari
SELECT SUBSTR(cod_p,1,1), tip_op, SUM(cant*pret)
FROM detalii_misc_XY
WHERE UPPER(tip_op)='A'
GROUP BY SUBSTR(cod_p,1,1),tip_op
HAVING SUM(cant*pret)=(SELECT MAX(SUM(cant*pret))
FROM detalii_misc_XY
WHERE UPPER(tip_op)='A'
GROUP BY SUBSTR(cod_p,1,1),tip_op)
;

- varianta imbunatatita cu informatii din 2 tabele


SELECT categ, tip_op, SUM(cant*pret) AS Valoare
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
WHERE UPPER(tip_op)='A'
GROUP BY categ, tip_op
HAVING SUM(cant*pret)=(SELECT MAX(SUM(cant*pret))
FROM detalii_misc_XY
WHERE UPPER(tip_op)='A'
GROUP BY SUBSTR(cod_p,1,1),tip_op)
;
6. Utilizarea interogarii de mai sus intr-o procedura simpla, nestocata, de afisare a calculului de mai sus.
SET SERVEROUTPUT ON;
DECLARE
v_categ ctl_XY.categ%TYPE;
v_tip_op detalii_misc_XY.tip_op%TYPE;
v_suma NUMBER;

BEGIN
SELECT categ, tip_op, SUM(cant*pret) INTO v_categ,v_tip_op,v_suma
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p=detalii_misc_XY.cod_p
WHERE UPPER(tip_op)='A'
GROUP BY categ, tip_op
HAVING SUM(cant*pret)=(SELECT MAX(SUM(cant*pret))
FROM detalii_misc_XY
WHERE UPPER(tip_op)='A'
GROUP BY SUBSTR(cod_p,1,1),tip_op)
;

DBMS_OUTPUT.PUT_LINE('Categoria '||v_categ||' are la operatiunea '||v_tip_op||' cea mai mare valoare


'||v_suma);
END;
/
Tema
1) Utilizand interogarea de la punctul 3f) scrieti o functie care sa afiseze urmatorea situatie pentru un
produs. Functia se va apela dintr-o procedura nestocata.

Suplimentar incercati sa obtineti si raspunsul (tratarea erorii de negasire a unui cod).

2) Pornind de la prima si a treia interogare a punctului 4c), scrieti o procedura fara parametru, cu cursor,
care sa afiseze urmatoarea situatie.
Laborator 13
Recapitulare finala partea 2. Functii, proceduri, parametrii. Apelarea acestora.

1. Definirea tabelelor
Crearea tabelelor se regaseste in script.
2. Adaugarea datelor
Popularea tabelelor cu date se face se
realizeazain ordinea „parinti”  „copii”.
BEGIN
Pentru a adauga datele mai rapid, instructiunile au fost
incadrate intr-o procedura.
END;
/
OBS:
- produsul cu codul 32 exista doar in tabela catalog iar produsul cu codul 31 doar s-a achizitionat

- adaugare camp nou - 'categ' in tabelul ctl_XY


ALTER TABLE ctl_XY ADD categ VARCHAR2(20);
- inlocuirea cu categoria 'alimente' pentru produsele al caror cod de produs incepe cu 1
- inlocuirea cu categoria 'electronice' pentru produsele al caror cod de produs incepe cu 2
- inlocuirea cu categoria 'jucarii' pentru produsele al caror cod de produs incepe cu 3
- realizarea simultana a actualizarilor utilizarea lui CASE

UPDATE ctl_XY
SET categ=
CASE
WHEN SUBSTR(cod_p,1,1) ='1' THEN 'alimente'
WHEN SUBSTR(cod_p,1,1) ='2' THEN 'electronice'
WHEN SUBSTR(cod_p,1,1) ='3' THEN 'jucarii'
END;
Activarea afisarii.
SET SERVEROUTPUT ON;
3. Functia si apelarea ei
- 3a) functii cu parametri
Testare SELECT

SELECT SUM(cant*pret) AS valoare


FROM misc_XY INNER JOIN detalii_misc_XY ON misc_XY.cod_misc=detalii_misc_XY.cod_misc
WHERE UPPER(explicatii) = 'FACT1';

SELECT SUM(cant*pret) AS valoare, COUNT(detalii_misc_XY.cod_misc) AS NrLinii


FROM misc_XY INNER JOIN detalii_misc_XY ON misc_XY.cod_misc=detalii_misc_XY.cod_misc
WHERE UPPER(explicatii) = 'FACT1';
Transformare in functii cu parametri.
OBS. Tinand seama ca al doilea SELECT returneaza 2 valori,va fi necesara si definirea si unui parametru de iesire.

CREATE OR REPLACE FUNCTION calc_fact_XY1 (p_expl misc_XY.explicatii%TYPE) RETURN NUMBER AS


v_total NUMBER :=0;
BEGIN
SELECT SUM(cant*pret) INTO v_total
FROM misc_XY INNER JOIN detalii_misc_XY ON misc_XY.cod_misc=detalii_misc_XY.cod_misc
WHERE UPPER(explicatii) = UPPER(p_expl);
RETURN v_total;
END;
/
CREATE OR REPLACE FUNCTION calc_fact_XY2 (p_expl misc_XY.explicatii%TYPE, p_linii OUT NUMBER) RETURN
NUMBER AS
v_total NUMBER :=0;
BEGIN
SELECT SUM(cant*pret), COUNT(detalii_misc_XY.cod_misc) INTO v_total, p_linii
FROM misc_XY INNER JOIN detalii_misc_XY ON misc_XY.cod_misc=detalii_misc_XY.cod_misc
WHERE UPPER(explicatii) = UPPER(p_expl);
RETURN v_total;
END;
/
- 3b) apelarea simultana a functiilor cu memorarea raspunsului de a tastatura

DECLARE
v_expl misc_XY.explicatii%TYPE;
v_prel_tot1 NUMBER :=0;
v_prel_tot2 NUMBER :=0;
v_prel_nrl NUMBER :=0;
BEGIN
v_expl:='&Dati_factura';
v_prel_tot1:=calc_fact_XY1(v_expl);
v_prel_tot2:=calc_fact_XY2(v_expl,v_prel_nrl);
IF v_prel_tot1>0 THEN
DBMS_OUTPUT.PUT_LINE('Factura '||v_expl||' are totalul '||v_prel_tot1);
DBMS_OUTPUT.PUT_LINE('Factura '||v_expl||' are totalul '||v_prel_tot2||' si numarul de linii '||v_prel_nrl);
ELSE
DBMS_OUTPUT.PUT_LINE('Factura inexistenta!!');
END IF;
END;
/
- 3c) imbunatatirea procedurii prin afisarea liniilor facturii si utilizarea celei de-a doua functii pentru afisarea
informatiilor despre fiecare factura
Testare SELECT
SELECT ctl_XY.cod_p AS cod, den_prod AS den, cant, pret, cant*pret AS val
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p = detalii_misc_XY.cod_p INNER JOIN misc_XY ON
detalii_misc_XY.cod_misc = misc_XY.cod_misc
WHERE UPPER(explicatii) = 'FACT1';
Transpunere SELECT in cursor
CREATE OR REPLACE PROCEDURE sit_fact_XY1(p_proc_expl misc_XY.explicatii%TYPE) AS
v_prel_tot NUMBER :=0;
v_prel_nrl NUMBER :=0;
CURSOR c_linie IS
SELECT ctl_XY.cod_p AS cod, den_prod AS den , cant, pret, cant*pret AS val
FROM ctl_XY INNER JOIN detalii_misc_XY ON ctl_XY.cod_p = detalii_misc_XY.cod_p INNER JOIN misc_XY ON
detalii_misc_XY.cod_misc = misc_XY.cod_misc
WHERE UPPER(explicatii) = UPPER(p_proc_expl);
BEGIN
v_prel_tot:=calc_fact_XY2(p_proc_expl,v_prel_nrl);
IF v_prel_tot>0 THEN
DBMS_OUTPUT.PUT_LINE('Factura '||p_proc_expl||' are totalul '||v_prel_tot||' si numarul de linii '||v_prel_nrl);
DBMS_OUTPUT.PUT_LINE('---------------------------------------------------------------------');
DBMS_OUTPUT.PUT_LINE('Cod produs Denumire Cantitate Pret Valoare');
DBMS_OUTPUT.PUT_LINE('---------------------------------------------------------------------');
FOR i IN c_linie LOOP
DBMS_OUTPUT.PUT_LINE(' '||i.cod||' '||i.den||' '||i.cant||' '||i.pret||' '||i.val);
END LOOP;
DBMS_OUTPUT.PUT_LINE('================================================================');
ELSE
DBMS_OUTPUT.PUT_LINE('Factura inexistenta!!');
END IF;
END;
/
Apelarea procedurii-varianta 1

EXECUTE sit_fact_XY1('&Dati_factura');
Apelarea procedurii-varianta 2 - se comunica de la tastatura doar numarul facturii
EXECUTE sit_fact_XY1('fact'||&dati_nr_facturii);

4. Proceduri diverse si apelarea lor


Testarea interogarilor SELECT necesare in proceduri

SELECT SUBSTR(ctl_XY.cod_p,1,1) AS cod, tip_op, SUM(cant*pret) AS total


FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p
GROUP BY SUBSTR(ctl_XY.cod_p,1,1),tip_op
ORDER BY cod;
SELECT tip_op, SUM(cant*pret) AS tot_gen
FROM detalii_misc_XY
GROUP BY tip_op;
Procedura stocata cu parametru de intrare, de afisare a totalului aprovizionarilor sau vanzarilor precum si cu
adaugarea clauzei de exceptie
CREATE OR REPLACE PROCEDURE sit_actXY1(p_tipop detalii_misc_XY.tip_op%TYPE) AS
v_totgen NUMBER;
CURSOR c1 IS
SELECT SUBSTR(ctl_XY.cod_p,1,1) AS cod,tip_op, SUM(cant*pret) AS total
FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p AND UPPER(tip_op)=UPPER(p_tipop)
GROUP BY SUBSTR(ctl_XY.cod_p,1,1),tip_op
ORDER BY cod;
v_pondere NUMBER;
v_pp VARCHAR2(10);
BEGIN
SELECT SUM(cant*pret) INTO v_totgen
FROM detalii_misc_XY
GROUP BY tip_op
HAVING UPPER(tip_op)=UPPER(p_tipop);
DBMS_OUTPUT.PUT_LINE ('Categorie Tip operatiune Suma Pondere Pondere procentuala');
FOR i IN c1 LOOP
v_pondere:=ROUND(i.total/v_totgen,4);
v_pp:=TO_CHAR(v_pondere*100)||'%';
DBMS_OUTPUT.PUT_LINE (' '||i.cod||' '||i.tip_op||' '||i.total||' '||v_pondere||' '||v_pp);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
DBMS_OUTPUT.PUT_LINE ('Totalul '||UPPER(p_tipop)||' este '||v_totgen);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Nu exista date pentru aceasta operatiune');
END;
/
Apelarea directa a procedurii de mai sus
EXECUTE sit_actXY1(UPPER('&A_sau_V'));
Apelarea procedurii de mai sus cu memorarea si utilizarea raspunsului de la tastatura.
In aceasta situatie este nevoie de utilzarea unor variabile, deci este necesara cel putin o procedura nestocata

DECLARE
v_tipop detalii_misc_XY.tip_op%TYPE;
v_operatie VARCHAR2(30);
BEGIN
v_tipop:=UPPER('&A_sau_V');
CASE v_tipop
WHEN 'A' THEN v_operatie:='APROVIZIONARI';
WHEN 'V' THEN v_operatie:='VANZARI';
ELSE v_operatie:='OPERATIUNE NECUNOSCUTA';
END CASE;
DBMS_OUTPUT.PUT_LINE ('Situatia de '||v_operatie);
DBMS_OUTPUT.PUT_LINE('-----------------------------------------------');
sit_actXY1(v_tipop);
END;
/

Aceeasi procedura dar cu utilizarea unui parametrului de iesire.

CREATE OR REPLACE PROCEDURE sit_actXY2(p_tipop IN detalii_misc_XY.tip_op%TYPE, p_totgen OUT NUMBER) AS


v_totgen NUMBER;
CURSOR c1 IS
SELECT SUBSTR(ctl_XY.cod_p,1,1) AS cod,tip_op, SUM(cant*pret) AS total
FROM ctl_XY, detalii_misc_XY
WHERE ctl_XY.cod_p=detalii_misc_XY.cod_p AND UPPER(tip_op)=UPPER(p_tipop)
GROUP BY SUBSTR(ctl_XY.cod_p,1,1),tip_op
ORDER BY cod;
v_pondere NUMBER;
v_pp VARCHAR2(10);
BEGIN
SELECT SUM(cant*pret) INTO v_totgen
FROM detalii_misc_XY
GROUP BY tip_op
HAVING UPPER(tip_op)=UPPER(p_tipop);
DBMS_OUTPUT.PUT_LINE ('Categorie Tip operatiune Suma Pondere Pondere procentuala');
FOR i IN c1 LOOP
v_pondere:=ROUND(i.total/v_totgen,4);
v_pp:=TO_CHAR(v_pondere*100)||'%';
DBMS_OUTPUT.PUT_LINE (' '||i.cod||' '||i.tip_op||' '||i.total||' '||v_pondere||' '||v_pp);
END LOOP;
DBMS_OUTPUT.PUT_LINE ('-----------------------------------------------------');
p_totgen:=v_totgen;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Nu exista date pentru aceasta operatiune');
p_totgen:=0;
END;
/

Apelarea unei proceduri ce are si parametrii de iesire nu se mai poate face direct, cu EXECUTE, intrucat este nevoie
cel putin de o variabila care sa preia informatia din parametrul de iesire. De aceea este nevoie cel putin de o
procedura nestocata, in care realizam atat utilzarea raspunsului de la tastatura cat si preluarea informatiei aduse
de parametru de iesire.

DECLARE
v_tipop detalii_misc_XY.tip_op%TYPE;
v_operatie VARCHAR2(30);
v_prel_totgen NUMBER;
BEGIN
v_tipop:=UPPER('&A_sau_V');

CASE v_tipop
WHEN 'A' THEN v_operatie:='APROVIZIONARI';
WHEN 'V' THEN v_operatie:='VANZARI';
ELSE v_operatie:=v_tipop||' OPERATIUNE NECUNOSCUTA';
END CASE;
DBMS_OUTPUT.PUT_LINE ('Situatia de '||v_operatie);
DBMS_OUTPUT.PUT_LINE('-----------------------------------------------');
sit_actXY2(v_tipop, v_prel_totgen);
IF v_prel_totgen>0 THEN
DBMS_OUTPUT.PUT_LINE ('Totalul '||v_operatie||' este '||v_prel_totgen);
END IF;
END;
/
Transformarea procedurii de mai sus intr-o procedura cu cursor care parcurge toate tipurile de operatiuni.
SELECT DISTINCT tip_op
FROM detalii_misc_XY;

DECLARE
CURSOR c_prel IS
SELECT DISTINCT tip_op
FROM detalii_misc_XY;
v_operatie VARCHAR2(30);
v_prel_totgen NUMBER;
BEGIN
FOR j IN c_prel LOOP
CASE UPPER(j.tip_op)
WHEN 'A' THEN v_operatie:='APROVIZIONARI';
WHEN 'V' THEN v_operatie:='VANZARI';
END CASE;
DBMS_OUTPUT.PUT_LINE ('Situatia de '||v_operatie);
DBMS_OUTPUT.PUT_LINE('-----------------------------------------------');
sit_actXY2(UPPER(j.tip_op), v_prel_totgen);
DBMS_OUTPUT.PUT_LINE ('Totalul '||v_operatie||' este '||v_prel_totgen);
DBMS_OUTPUT.PUT_LINE('=============================================');
END LOOP;
END;
/
OBS: instructiunile ELSE..IF...nu mai sunt necesare intrucat nu pot exista alte operatiuni decat A sau V, care sunt
preluate din tabel prin cursor si nu comunicate de la tastatura.

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