|
APLICATIE INFORMATICA. UTILIZAREA BAZELOR DE DATE ORACLE IN VANZARI.
1. CREAREA BAZEI DE DATE.
Se creaza tabelele CLIENT, FURNIZOR, TARA, APELURI_TEL, COMANDA, ARTICOLE_COMENZI, STOC_PRODUSE, CATALOG.
DROP TABLE Client;
CREATE TABLE Client
(
cod_client integer not null,
nume char(15) not null
constraint ck_client_nume check (nume=LTRIM(initcap(nume))),
prenume char(15)
constraint ck_client_prenume check (prenume=LTRIM(initcap(prenume))),
firma char(20) not null,
adresa1 char(50) not null,
oras char(15) default 'Dr.Tr. Severin',
cod_tarachar(2) default 'RO',
cod_postal char(5) default '1500',
telefon char(18),
primary key (cod_client),
foreign key(cod_tara) references Tara (cod_tara)
);
COMMIT
DROP TABLE Furnizor;
CREATE TABLE Furnizor
(
cod_furnizor integer not null,
nume_furnizor char(15)
constraint nn_furnizor_nume_furnizor not null,
timp_livrare integer default 14
constraint ck_furnizor_timp_livrare check (timp_livrare between 1 and 21),
cod_tara char(2),
primary key (cod_furnizor),
foreign key (cod_tara) references Tara(cod_tara)
);
COMMIT
DROP TABLE Tara;
CREATE TABLE Tara
(
cod_tara char(2) not null,
nume_tarachar(15) default 'Romania'
constraint ck_Tara_nume_tara check (nume_tara=LTRIM(initcap(nume_tara))),
primary key (cod_tara)
);
COMMIT
DROP TABLE Apeluri_Tel;
CREATE TABLE Apeluri_Tel
cod_clientinteger not null,
ora_apel varchar2(35) default systimestamp,
cod_apel number default 1
constraint ck_Apeluri_tel_cod_apel check (cod_apel in(1,2,3)),
descriere_apel varchar2(100),
primary key (cod_client,ora_apel),
foreign key (cod_client) references Client(cod_client)
COMMIT
DROP TABLE Comanda;
CREATE TABLE Comanda
(
nr_comanda INTEGER not null,
data_comanda DATE DEFAULT SYSDATE,
cod_client INTEGER NOT NULL,
data_livrare DATE DEFAULT SYSDATE+14,
data_platii DATE,
primary key (nr_comanda),
foreign key (cod_client) references Client(cod_client)
);
COMMIT
DROP TABLE Articole_Comenzi;
CREATE TABLE Articole_Comenzi
(
nr_comanda integer not null,
indice_comanda integer not null,
cod_produs integer not null,
cod_furnizorinteger not null,
cantitate number(6,2),
primary key (Nr_comanda, Indice_comanda),
foreign key (Nr_comanda) references Comanda(Nr_comanda) on delete cascade,
foreign key (Cod_produs,Cod_furnizor) references Stoc_produse(Cod_produs,Cod_furnizor)
);
COMMIT
DROP TABLE Stoc_produse;
CREATE TABLE Stoc_produse
(
cod_produsINTEGER NOT NULL,
cod_furnizor INTEGER NOT NULL,
denumire CHAR(50),
pret_unitar NUMBER(6,2)
CONSTRAINT ck_Stoc_produse_pret_unitar check (pret_unitar BETWEEN 1 and
500), cantitate_disponibila number(7,2)
CHECK (cantitate_disponibila BETWEEN 0 and 15000), u_m char(10)
DEFAULT 'BUC.'
CONSTRAINT ck_Stoc_produse_u_m CHECK (UPPER(u_m) IN ('LITRI',
'PACHET', 'BUC.')),
PRIMARY KEY (Cod_produs,Cod_furnizor),
FOREIGN KEY (Cod_furnizor) REFERENCES Furnizor(Cod_furnizor)
);
COMMIT
DROP TABLE Catalog;
CREATE TABLE Catalog
(
serie_catalog INTEGER NOT NULL,
cod_produs INTEGER NOT NULL,
cod_furnizorINTEGER NOT NULL,
descriere NVARCHAR2(100),
foto BLOB,
reclama BLOB,
PRIMARY KEY (Serie_catalog),
FOREIGN KEY (Cod_produs,Cod_furnizor) REFERENCES Stoc_produse
(Cod_produs, Cod_furnizor) ON DELETE CASCADE );
COMMIT
Schema bazei de date astfel create arata ca in figura urmatoare:
SCHEMA BAZEI DE DATE
2. DEFINIREA UNUI GENERATOR DE NUMERE DE SECVENTA SI A UNOR TRIGGERI SERIALI.
Inainte de a trece la inserarea efectiva a datelor in tabele vom crea pentru tabelele Client, Furnizor, Stoc_produse, Catalog cate un declansator (trigger) la nivel de linie pentru generarea valorilor implicite, prin care inaintea inserarii unei noi inregistrari valoarea atributului cod_client, cod_furnizor, cod_produs, serie_catalog respectiv, sa fie obtinuta in mod automat. Se va folosi functia NVL care ne permite inlocuirea unei valori NULL, atunci cand ea este intalnita, cu o valoare predefinita: NVL(parametru a carui valoare se testeaza, valoare care ii ia locul in caz de NULL).
Pentru tabelul comanda vom defini o secventa (sequence) care, la fel ca si triggerii de mai sus genereaza automat numere unice pentru campul nr_comanda. Daca asimilam comanda unei facturi, avem astfel definit intervalul (seriile) facturilor.
CREATE OR REPLACE TRIGGER serial_cl
BEFORE INSERT ON Client
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
SELECT NVL(MAX(cod_client),100)+1 INTO :NEW.cod_client
FROM Client
END;
CREATE OR REPLACE TRIGGER serial_fu
BEFORE INSERT ON Furnizor
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
SELECT NVL(MAX(cod_furnizor),1000)+1 INTO:NEW.cod_furnizor
FROM Furnizor
END;
CREATE OR REPLACE TRIGGER serial_st
BEFORE INSERT ON Stoc_produse
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
SELECT NVL(MAX(cod_produs),100000)+1 INTO:NEW.cod_produs
FROM Stoc_produse
END;
CREATE OR REPLACE TRIGGER serial_cat
BEFORE INSERT ON Catalog
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
SELECT NVL(MAX(serie_catalog),5000)+1 INTO:NEW.serie_catalog
FROM Catalog
END;
CREATE SEQUENCE secv_com // nume secventa
INCREMENT BY 1 // pasul de incrementare
START WITH 10501 // valoarea de pornire a secventei
MAXVALUE 10999 // valoarea maxima a secventei
NOCACHE NOCYCLE; // secventa finita
3. INSERAREA DATELOR IN TABELE.
Valorile pentru anumite atribute din tabela Comanda, Articole_comenzi, etc. nu vor direct introduse, deoarece aceste campuri sunt unele calculate, iar valorile lor se vor introduce ulterior.
Furnizor
COD_FURNIZOR
NUME_FURNIZOR
TIMP_LIVRARE
COD_TARA
1001
Luk oil
14
RO
1002
Petrom S.A.
14
RO
1003
OMV
14
A
1004
Mol Hungary
14
HU
1005
Shell Oil
14
GB
1006
General Motors
14
US
Stoc produse
COD
PROD.
COD
FUR.
DENUMIRE
PRET
UNITAR
CANTITATE
DISPONIBILA
U_M
100001
1002
Motorina Alpine Diesel Euro4
3.62
6540
Litri
100002
1004
Benzina fara plumb CO98
3.95
7200
Litri
100003
1003
Cauciucuri iarna 185/65R1588T
125
62
BUC.
100004
1005
Benzina V-Power Euro4
4.05
9900
Litri
100005
1001
Benzina fara plumb CO95
3.51
7500
Litri
100006
1001
Cauciucuri iarna 175R14C99/98N
120
75
BUC.
100007
1002
Ulei motor M20-W50,3+1gratis
42.5
340
Pachet
100008
1005
Lichid antigel North Star -45
10.5
990
Litri
100009
1001
Cauciucuri iarna 175R15C99/98N
105.6
250
BUC.
100010
1003
Spray pornire Alpine Diesel 150ml
12.5
140
BUC.
100011
1003
Motorina Euro4
3.59
8800
Litri
100012
1004
Ulei motor M20-W50
11.6
550
Litri
100013
1005
Ulei motor M20-W40 Shell Oil
22
440
Litri
100014
1002
Odorizant auto Car fresh
2.5
1300
BUC.
100015
1005
Odorizant auto lichid Ambi-Pur
18.5
90
BUC.
100016
1003
Benzina fara plumb OMV Carerra 100
4.15
10250
Litri
100017
1001
Ulei transmisie T90 butoi-250l
332.5
120
BUC.
100018
1001
Ulei transmisie T90
5.6
540
Litri
100019
1002
Filtru aer E930 -Renault
12.5
85
BUC.
100020
1005
Filtru aer E700 -BMW
42.5
90
BUC.
100021
1003
Solutie parbriz Winter Glass -30
8.5
500
Litri
100022
1004
Benzina fara plumb Mol Dynamic Euro4
4.2
5400
Litri
100023
1002
Filtru benzina E85
22.1
110
BUC.
100024
1006
Cauciucuri iarna 175R15C99/98N
195
180
BUC.
100025
1006
Cauciucuri iarna 185/65R1588T
175
54
BUC.
100026
1002
Pivot superior K80 -Renault
32.5
210
BUC.
100027
1002
Pivot superior K90 -Peugeot
35.15
108
BUC.
Client
COD CLIENT
NUME
PRENUME
FIRMA
ADRESA1
ORAS
COD TARA
101
Popescu
Ovidiu
Jhonson wax
str. Lujerului nr 25
Craiova
RO
102
Otto
Klauss
Diesel&
October strasse 25
Berlin
D
103
Lazarovici
Lucian
Henkel Romania
str. Linistii 24
Bucuresti
RO
104
Sfrija
Liviu
Faur S.A.
str. Iancului 52
Bucuresti
RO
105
Tomescu
Valentin
Cronos S.A.
str. Tomis nr 125
Constanta
RO
106
Jolt
Nagy
Royal Hungary
str. Paris nr 34
Budapesta
HU
107
Dassin
Joe
Air France
Champs Elyses nr 58
Paris
F
108
Millwake
Jhon
Start Time inc.
Bridge avenue no 256
Londra
GB
109
Coman
Ion
Dacia S.A.
str. Uzinei nr 69
Pitesti
RO
110
Lozba
Alexandru
Concret Comert SRL.
str. Cernetului nr 8
Dr.Tr. Severin
RO
111
Bush
George
Bush investment inc.
President avenue 1
Washington D.C.
US
Comanda
NR COMANDA
DATA COMANDA
COD CLIENT
DATA LIVRARE
DATA PLATII
10501
14-Mar-08
108
28-Mar-08
10502
14-Mar-08
101
28-Mar-08
10503
14-Mar-08
107
28-Mar-08
10504
14-Mar-08
104
28-Mar-08
10505
14-Mar-08
103
28-Mar-08
10506
14-Mar-08
107
28-Mar-08
10507
14-Mar-08
108
28-Mar-08
10508
14-Mar-08
102
28-Mar-08
10509
14-Mar-08
106
28-Mar-08
10510
14-Mar-08
105
28-Mar-08
10511
14-Mar-08
107
28-Mar-08
10512
14-Mar-08
108
28-Mar-08
10513
14-Mar-08
104
28-Mar-08
10514
14-Mar-08
102
28-Mar-08
10515
14-Mar-08
110
28-Mar-08
10516
14-Mar-08
111
28-Mar-08
10517
14-Mar-08
109
28-Mar-08
10518
14-Mar-08
111
28-Mar-08
Tara
COD TARA
NUME_TARA
RO
Romania
HU
Ungaria
GB
Marea Britanie
A
Austria
D
Germania
F
Franta
US
Statele Unite
E
Spania
Articole_comenzi
NR COMANDA
INDICE COMANDA
COD PRODUS
COD FURNIZOR
CANTITATE
10001
1
100005
1001
250
10002
1
100014
1002
30
10002
2
100021
1003
15
10002
3
100024
1006
12
10003
1
100001
1002
410
10003
2
100027
1002
9
10004
1
100004
1005
340
10005
1
100008
1005
50
10005
2
100017
1001
5
10005
3
100019
1002
6
10005
4
100026
1002
10
10006
1
100022
1004
480
10006
2
100011
1003
350
10007
1
100010
1003
15
10008
1
100003
1003
12
10008
2
100009
1001
24
10009
1
100002
1004
250
10009
2
100006
1001
4
10010
1
100007
1002
25
10010
2
100011
1003
430
10011
1
100013
1005
80
10012
1
100016
1003
400
10012
2
100017
1001
5
10012
3
100019
1002
15
10012
4
100021
1003
50
10013
1
100020
1005
12
10013
2
100023
1002
25
10014
1
100025
1006
8
10015
1
100022
1004
250
10015
2
100024
1006
6
10015
3
100005
1001
350
10016
1
100002
1004
330
10016
2
100008
1005
210
10017
1
100012
1004
80
10017
2
100015
1005
25
10017
3
100018
1001
110
10017
4
100022
1004
840
10017
5
100001
1002
300
10017
6
100005
1001
25
10017
7
100027
1002
18
10018
1
100005
1001
840
10018
2
100010
1003
6
10018
3
100018
1001
30
10018
4
100011
1003
630
10018
5
100024
1006
15
4. MODIFICAREA STRUCTURII TABELELOR BAZEI DE DATE
1) Sa se modifice dimensiunea atributului desciere_apel in tabela Apeluri_tel, de la 100 la 50 de caractere.
ALTER TABLE Apeluri_tel MODIFY (descriere_apel varchar2(50));
DESCRIBE Apeluri_tel;
Name
Null?
Type
COD_CLIENT
NOT NULL
NUMBER(38)
ORA_APEL
NOT NULL
VARCHAR2(35)
COD_APEL
CHAR(1)
DESCRIERE_APEL
VARCHAR2(50)
2) Adaugati atributul TVA si val fara TVA (number(6,2)), la tabela Articole_comenzi si Comanda. In plus in tabelul Comanda vom introduce si atributul val_totala.
ALTER TABLE Articole_comenzi ADD (TVA number(6,2));
ALTER TABLE Articole_comenzi ADD (val_fara_TVA number(6,2));
ALTER TABLE Comanda ADD (total_TVA number(6,2));
ALTER TABLE Comanda ADD (total_val_fara_TVA number(6,2));
ALTER TABLE Comanda ADD (val_totala number(6,2));
DESCRIBE Articole_comenzi;
DESCRIBE Comanda;
Name
Null?
Type
NR_COMANDA
NOT NULL
NUMBER(38)
INDICE_COMANDA
NOT NULL
NUMBER(38)
COD_PRODUS
NOT NULL
NUMBER(38)
COD_FURNIZOR
NOT NULL
NUMBER(38)
CANTITATE
NUMBER(6,2)
TVA
NUMBER(6,2)
VAL_FARA_TVA
NUMBER(6,2)
Name
Null?
Type
NR_COMANDA
NOT NULL
NUMBER(38)
DATA_COMANDA
DATE
COD_CLIENT
NOT NULL
NUMBER(38)
DATA_LIVRARE
DATE
DATA_PLATII
DATE
TOTAL_TVA
NUMBER(6,2)
TOTAL_VAL_FARA_TVA
NUMBER(6,2)
VAL_TOTALA
NUMBER(6,2)
3) Adaugati atributul adresa2, char(20), la tabela Client, pentru cazul in care un client are mai mult de o adresa..
ALTER TABLE Client ADD (adresa2 char(20));
DESCRIBE Client;
Name
Null?
Type
COD_CLIENT
NOT NULL
NUMBER(38)
NUME
NOT NULL
CHAR(15)
PRENUME
CHAR(15)
FIRMA
NOT NULL
CHAR(20)
ADRESA1
NOT NULL
CHAR(50)
ADRESA2
CHAR(20)
ORAS
CHAR(15)
COD_TARA
CHAR(2)
COD_POSTAL
CHAR(5)
TELEFON
CHAR(18)
5. ACTUALIZARI, INTEROGARI, FUNCTII MATEMATICE, FUNCTII PENTRU SIRURI DE CARACTERE ETC.
1) Sa se afiseze ultimul numar utilizat din secventa secv_com:
SELECT secv_com.CURRVAL
FROM DUAL;
CURRVAL
10518
2) Sa se mareasca pretul la motorina cu 0.15 RON iar la benzina cu 8%.
UPDATE stoc_produse
SET pret_unitar= pret_unitar+0.15
WHERE denumire LIKE 'Motorina%';
UPDATE stoc_produse
SET pret_unitar= pret_unitar+pret_unitar*0.08
WHERE denumire LIKE 'Benzina%';
SELECT * FROM stoc_produse
WHERE (denumire LIKE 'Motorina%') OR (denumire LIKE 'Benzina%')
ORDER BY pret_unitar;
COD PRODUS
COD FURNIZOR
DENUMIRE
PRET UNITAR
CANTITATE
DISPONIBILA
U_M
100011
1003
Motorina Euro4
3.74
8800
Litri
100001
1002
Motorina Alpine Diesel Euro4
3.77
6540
Litri
100005
1001
Benzina fara plumb CO95
3.79
7500
Litri
100002
1004
Benzina fara plumb CO98
4.27
7200
Litri
100004
1005
Benzina V-Power Euro4
4.37
9900
Litri
100016
1003
Benzina fara plumb OMV Carerra 100
4.48
10250
Litri
100022
1004
Benzina fara plumb Mol Dynamic Euro4
4.54
5400
Litri
3) Sa se calculeze si sa se introduca in tabelul Articole_comenzi valoarea atributului val_fara_TVA si TVA pentru fiecare tuplu.
UPDATE Articole_comenzi ac SET
val_fara_tva = cantitate*(SELECT pret_unitar FROM stoc_produse sp
WHERE ac.cod_produs=sp.cod_produs)/1.19;
UPDATE Articole_comenzi ac SET
tva = val_fara_tva*0.19;
SELECT * FROM Articole_comenzi (din motive de spatiu prezentam doar cateva linii).
NR COMANDA
INDICE COMANDA
COD PRODUS
COD FURNIZOR
CANTITATE
TVA
VAL FARA_TVA
10501
1
100005
1001
250
151.28
7922
10502
1
100014
1002
30
11.98
63.03
10502
2
100021
1003
15
20.36
107.14
10502
3
100024
1006
12
373.61
19639
10503
1
100001
1002
410
2479
1298.91
10503
2
100027
1002
9
50.51
265.84
4) Sa se calculeze valoarea, numarul de unitati si de SKU-uri de pe fiecare comanda
SELECT nr_comanda, SUM(cantitate*pret_unitar) AS valoare_comanda, SUM(cantitate) AS unitati, COUNT(*) AS SKU
FROM articole_comenzi a, stoc_produse sp
WHERE a.cod_produs=sp.cod_produs
GROUP BY nr_comanda
ORDER BY nr_comanda;
(din motive de spatiu prezentam doar cateva linii):
NR_COMANDA
VALOARE_COMANDA
UNITATI
SKU
10501
947.5
250
1
10502
2542.5
57
3
10503
1862.05
419
2
10504
1485.8
340
1
10505
2587.5
71
4
10506
3488.2
830
2
10507
187.5
15
1
10508
4034.4
36
2
5) Sa se calculeze si sa se introduca in tabelul Comanda valoarea atributelor total_TVA, total_val_fara_TVA si val_totala pentru fiecare tuplu.
UPDATE Comanda cc SET
val_totala=(SELECT SUM(cantitate*pret_unitar)
FROM articole_comenzi a, stoc_produse sp
WHERE (a.cod_produs=sp.cod_produs) AND
nr_comanda=cc.nr_comanda);
UPDATE Comanda cc SET
total_TVA=0.19*val_totala/1.19;
UPDATE Comanda cc SET
total_val_fara_TVA=val_totala/1.19;
SELECT * FROM comanda
ORDER BY nr_comanda (din motive de spatiu prezentam doar cateva linii):
NR COMANDA
DATA COMANDA
COD CLIENT
DATA LIVRARE
TOTAL TVA
TOTAL_VAL FARA_TVA
VAL TOTALA
10501
15-Mar-08
108
29-Mar-08
151.28
7922
947.55
10502
15-Mar-08
101
29-Mar-08
405.95
21355
2542.5
10503
15-Mar-08
107
29-Mar-08
297.3
1564.75
1862.05
10504
15-Mar-08
104
29-Mar-08
237.23
1248.57
1485.8
10505
15-Mar-08
103
29-Mar-08
413.13
2174.37
2587.5
10506
15-Mar-08
107
29-Mar-08
5594
2931.26
3488.2
10507
15-Mar-08
108
29-Mar-08
29.94
157.56
187.5
10508
15-Mar-08
102
29-Mar-08
644.15
3390.25
4034.4
10509
15-Mar-08
106
29-Mar-08
247.08
1300.42
1547.5
10510
15-Mar-08
105
29-Mar-08
4241
2244.29
2670.7
6) Sa se afiseze codul produsului, denumirea si pretul marit cu 5% pentru produsele livrate de furnizorul OMV. (dim motive de spatiu se vor afisa doar 12 tupluri):
SELECT cod_produs,denumire,cod_furnizor, pret_unitar AS PRET_INITIAL,
DECODE (cod_furnizor, 1003, pret_unitar+pret_unitar*0.05, pret_unitar) AS PRET_MARIT
FROM Stoc_produse
WHERE cod_produs<=100012;
COD
PRODUS
DENUMIRE
COD
FURNIZOR
PRET
INITIAL
PRET
MARIT
100001
Motorina Alpine Diesel Euro4
1002
3.77
3.77
100002
Benzina fara plumb CO98
1004
4.27
4.27
100003
Cauciucuri iarna 185/65R1588T
1003
125
131.25
100004
Benzina V-Power Euro4
1005
4.37
4.37
100005
Benzina fara plumb CO95
1001
3.79
3.79
100006
Cauciucuri iarna 175R14C99/98N
1001
120
120
100007
Ulei motor M20-W50,3+1gratis
1002
42.5
42.5
100008
Lichid antigel North Star -45
1005
10.5
10.5
100009
Cauciucuri iarna 175R15C99/98N
1001
105.6
105.6
100010
Spray pornire Alpine Diesel 150ml
1003
12.5
13.125
100011
Motorina Euro4
1003
3.74
3.927
100012
Ulei motor M20-W50
1004
11.6
11.6
Ca algoritm de analiza al functiei DECODE se poate observa ca, pentru coloana cod_furnizor (primul argument) are loc cautarea valorii "1003", iar in cazul in care aceasta este regasita, valoarea atributului pret din acel tuplu este actualizata cu 5%.
Pentru restul produselor care nu fac obiectul cautarii, se trece implicit, ultimul argument, in cazul de fata coloana pret_unitar.
7) Sa se afiseze Cantitatea Medie cumparata din fiecare produs (evident doar daca apare pe mai multe comenzi) si sa se ordoneze tuplurile dupa cantitate descrescator:
SELECT p.denumire, AVG (cantitate) cant_medie, COUNT(*) frecv_aparitie
FROM Stoc_produse P, Articole_comenzi a
WHERE p.cod_produs= a.cod_produs
GROUP BY p.denumire HAVING COUNT(*)>1
ORDER BY AVG (cantitate) DESC;
DENUMIRE
CANT_MEDIE
FRECV_APARITIE
Benzina fara plumb Mol Dynamic Euro4
523.333333
3
Motorina Euro4
470
3
Benzina fara plumb CO95
3625
4
Motorina Alpine Diesel Euro4
355
2
Benzina fara plumb CO98
290
2
Lichid antigel North Star -45
130
2
Ulei transmisie T90
70
2
Solutie parbriz Winter Glass -30
32.5
2
Cauciucuri iarna 175R15C99/98N
14.25
4
Pivot superior K90 -Peugeot
13.5
2
Spray pornire Alpine Diesel 150ml
10.5
2
Filtru aer E930 -Renault
10.5
2
Cauciucuri iarna 185/65R1588T
10
2
Ulei transmisie T90 butoi-250l
5
2
8) Sa se extraga o lista, ordonata alfabetic, ce va contine firmele (clientii) din Romania si adresa lor completa.
SELECT firma, CONCAT (adresa1, oras) as ADRESA_COMPLETA, nume_tara
FROM client c, tara t
WHERE (c.cod_tara=t.cod_tara) AND (nume_tara LIKE 'Ro%')
ORDER BY firma
FIRMA
ADRESA_COMPLETA
NUME_TARA
Concret Comert SRL.
str. Cernetului nr 8 Dr.Tr. Severin
Romania
Cronos S.A.
str. Tomis nr 125 Constanta
Romania
Dacia S.A.
str. Uzinei nr 69 Pitesti
Romania
Faur S.A.
str. Iancului 52 Bucuresti
Romania
Henkel Romania
str. Linistii 24 Bucuresti
Romania
Jhonson wax
str. Lujerului nr 25 Craiova
Romania
9) Aceiasi cerinta ca la punctual 8) insa ordonarea se va face descrescator in functie de achizitiile fiecarei societati comerciale (topul firmelor din Romania).
DROP VIEW top_firme;
CREATE VIEW top_firme AS
SELECT cod_client, SUM(val_totala) AS total_achizitii, COUNT (*) AS nr_comenzi
FROM Comanda
GROUP BY cod_client
ORDER BY cod_client;
SELECT firma, total_achizitii, nr_comenzi
FROM top_firme t, client c
WHERE (t.cod_client=c.cod_client) AND (cod_tara IN('RO'))
ORDER BY total_achizitii DESC;
FIRMA
TOTAL_ACHIZITII
NR_COMENZI
Dacia S.A.
7678.55
1
Concret Comert SRL.
3631.5
1
Cronos S.A.
2670.7
1
Henkel Romania
2587.5
1
Faur S.A.
2548.3
2
Jhonson wax
2542.5
1
10) Sa se afiseze produsele care sunt oferite de furnizorul "OMV" - cantitate si pret. (Oferta de pret)
SELECT nume_furnizor, denumire, u_m, cantitate_disponibila, pret_unitar
FROM furnizor f ,stoc_produse p
WHERE (f.cod_furnizor=p.cod_furnizor) AND (nume_furnizor IN ('OMV'))
ORDER BY denumire;
NUME
FURNIZOR
DENUMIRE
U_M
CANTITATE
DISPONIBILA
PRET
UNITAR
OMV
Benzina fara plumb OMV Carerra 100
Litri
10250
4.48
OMV
Cauciucuri iarna 185/65R1588T
BUC
62
125
OMV
Motorina Euro4
Litri
8800
3.74
OMV
Solutie parbriz Winter Glass -30
Litri
500
8.5
OMV
Spray pornire Alpine Diesel 150ml
BUC
140
12.5
11) Sa se afiseze comenzile cu valoare mai mica decat cea mai mare valoare a unei comenzi facute de firma 'Air France'. (1862.05 , 3488.20 , 1760)
SELECT nr_comanda, val_totala, cod_client
FROM comanda
WHERE val_totala < ANY( SELECT val_totala
FROM comanda c, client cl
WHERE c.cod_client=cl.cod_client
AND firma='Air France')
ORDER BY val_totala DESC;
NR_COMANDA
VAL_TOTALA
COD_CLIENT
10510
2670.7
105
10505
2587.5
103
10502
2542.5
101
10503
1862.05
107
10511
1760
107
10509
1547.5
106
10504
1485.8
104
10514
1400
102
10513
1062.5
104
10501
947.5
108
10507
187.5
108
Pentru interogarea de mai sus s-a folosit operatorul ANY cu semnificatia:
> ANYmai mare decat minim din lista
< ANYmai putin decat maxim din lista
= ANYin
> ALL mai mare decat maxim din lista
< ALL mai mic decat minim din lista
Cand se foloseste SOME/ANY, operatorul DISTINCT este frecvent utilizat pentru a impiedica sa se selecteze liniile de mai multe ori.
12) Afisati produsele care au fost comandate in cantitatea mai mare decat cea mai mica cantitate a produsului 100011 - 'Motorina Euro 4' (min(cantitate)100011=350).
SELECT a.cod_produs, s.denumire, a.cantitate
FROM Articole_comenzi a, Stoc_produse s
WHERE a.cod_produs=s.cod_produs AND
a.cod_produs!= 100011 AND cantitate > SOME
(SELECT DISTINCT cantitate
FROM Articole_comenzi
WHERE cod_produs=100011)
ORDER BY cantitate ASC;
COD_PRODUS
DENUMIRE
CANTITATE
100016
Benzina fara plumb OMV Carerra 100
400
100001
Motorina Alpine Diesel Euro4
410
100022
Benzina fara plumb Mol Dynamic Euro4
480
100005
Benzina fara plumb CO95
840
100022
Benzina fara plumb Mol Dynamic Euro4
840
13) Cate comenzi a facut fiecare client (clientii la care numele incepe cu ., clientii cu un anumit nr. de comenzi etc.)
SELECT nume, prenume, COUNT(*) AS nr_comenzi
FROM comanda co, client c
WHERE (co.cod_client=c.cod_client) AND (nume like '%')
GROUP BY nume , prenume
HAVING count(*)>0
ORDER BY nr_comenzi ASC;
NUME
PRENUME
NR_COMENZI
Coman
Ion
1
Lozba
Alexandru
1
Lazarovici
Lucian
1
Popescu
Ovidiu
1
Jolt
Nagy
1
Tomescu
Valentin
1
Bush
George
2
Sfrija
Liviu
2
Otto
Klauss
2
Millwake
Jhon
3
Dassin
Joe
3
14) Sa se afiseze, pentru fiecare comanda in parte, ce procent cantitativ si valoric reprezinta fiecare produs in parte din totalul produselor de pe comanda.
SELECT a.nr_comanda, a.cod_produs, a.procc/b.total*100 procent_cantitativ, a.procv/b.tval*100 procent_valoric
FROM
(SELECT nr_comanda, cod_produs, cantitate procc, val_fara_tva procv
FROM Articole_comenzi
GROUP BY nr_comanda, cod_produs,cantitate,val_fara_tva) a,
(SELECT nr_comanda, SUM (cantitate) total, SUM(val_fara_tva) tval
FROM Articole_comenzi
GROUP BY nr_comanda) b
WHERE a.nr_comanda=b.nr_comanda
ORDER BY nr_comanda
(din motive de spatiu afisam doar cateva comenzi)
NR_COMANDA
COD_PRODUS
PROCENT_CANTITATIV
PROCENT_VALORIC
10501
100005
100
100
10502
100014
52.6315789
2.95006927
10502
100021
23157895
5.01460291
10502
100024
21.0526316
92.0353278
10503
100027
2.14797136
19892954
10503
100001
97.8520286
83.0107046
10504
100004
100
100
10505
100019
8.45070423
2.89875735
10505
100008
70.4225352
20.2899217
10505
100026
14.084507
12.560362
10505
100017
7.04225352
64.2509589
10506
100022
57.8313253
62.4734756
10506
100011
42.1686747
37.5265244
10507
100010
100
100
10508
100003
33.3333333
37.180149
10508
100009
66666667
62.819851
10509
100002
98.4251969
68.9823288
10509
100006
1.57480315
31.0176712
10510
100011
94.5054945
60.2163713
10510
100007
5.49450549
39.7836287
10511
100013
100
100
15. Clientul care a facut cea mai mare comanda valoric/ unitati/ nr. SKU:
SELECT nume, prenume, firma ,nr_comanda, val_totala
FROM client c, comanda co
WHERE (c.cod_client=co.cod_client) AND (val_totala = (SELECT MAX(val_totala) FROM comanda));
NUME
PRENUME
FIRMA
NR_COMANDA
VAL_TOTALA
Bush
George
Bush investment inc.
10518
8707.8
CREATE OR REPLACE VIEW vc AS
SELECT nr_comanda, SUM(cantitate) AS unitati, COUNT(*) AS SKU
FROM articole_comenzi
GROUP BY nr_comanda;
SELECT nume, prenume, firma, co.nr_comanda, val_totala, unitati, SKU
FROM vc, Client c, comanda co
WHERE (vc.nr_comanda=co.nr_comanda) AND (co.cod_client=c.cod_client) AND (SKU/unitati=(SELECT MAX(SKU/unitati) FROM vc));
NUME
PRENUME
FIRMA
NR_COMANDA
VAL_TOTALA
UNITATI
SKU
Coman
Ion
Dacia S.A.
10517
7678.55
1398
7
NUME
PRENUME
FIRMA
NR_COMANDA
VAL_TOTALA
UNITATI
SKU
Bush
George
Bush investment inc.
10518
8707.8
1521
5
1 Sa se gasesca toti clientii care au comandat produse fabricate in Austria si pe cate comenzi:
SELECT nume, prenume, firma, COUNT(*) nr_comenzi
FROM Client c, Comanda co
WHERE c.cod_client=co.cod_client
AND nr_comanda IN
(SELECT c1.nr_comanda
FROM Comanda c1, Articole_comenzi a
WHERE c1.nr_comanda=a.nr_comanda
AND cod_produs IN
(SELECT a1.cod_produs
FROM Articole_comenzi a1, Stoc_produse p
WHERE a1.cod_produs = p.cod_produs
AND p.cod_furnizor IN
(SELECT p1.cod_furnizor
FROM Stoc_produse p1, Furnizor f
WHERE p1.cod_furnizor=f.cod_furnizor
AND cod_tara IN
(SELECT t1.cod_tara
FROM Furnizor t1, Tara t
WHERE t1.cod_tara=t.cod_tara
AND nume_tara = 'Austria')
) ) )
GROUP BY nume, prenume, firma;
NUME
PRENUME
FIRMA
NR_COMENZI
Dassin
Joe
Air France
1
Popescu
Ovidiu
Jhonson wax
1
Tomescu
Valentin
Cronos S.A.
1
Millwake
Jhon
Start Time inc.
2
Bush
George
Bush investment inc.
1
Otto
Klauss
Diesel&
1
17) Sa se determine toti clientii din orasul celui care a facut comanda 10505:
SELECT nume, prenume, firma, oras
FROM Client
WHERE oras IN (SELECT oras
FROM Client c, Comanda co
WHERE c.cod_client=co.cod_client
AND nr_comanda=10505
NUME
PRENUME
FIRMA
ORAS
Sfrija
Liviu
Faur S.A.
Bucuresti
Lazarovici
Lucian
Henkel Romania
Bucuresti
BLOCURI PL/SQL, CURSOARE, FUNCTII DE DATA, FUNCTII DE CONVERSIE, FUNCTII OLAP ETC.
1) Sa se seteze in tabelul Comanda data comenzii descrescator (de la data curenta - a fost setata ca fiind 05 mai) incepand cu ultima comanda, data de livrare ca fiind in prima vineri daca comanda este facuta in zilele luni si marti, sambata daca e facuta miercuri, sau peste o saptamana, tot vineri, daca comanda este facuta joi si vineri. Comenzile facute sambata si duminica vor avea ziua de livrare lunea, peste o saptamana
Data platii va fi ultima zi din luna (sau in prima zi lucratoare a urmatoarei luni daca ultima zi este sambata sau duminica) daca comanda este facuta pana pe 15 inclusiv, sau pana pe data de 15 a urmatoarei luni (sau in prima zi lucratoare dupa data de 15 daca aceasta este sambata sau duminica) daca comanda este facuta in intervalul 16 - sfarsitul lunii curente.
Se vor crea trei tabele in care se vor grupa comenzile in functie de ziua_livrarii (vineri, sambata si luni);
Pentru a putea realiza aceasta cerere multipla vom realiza un bloc PL/SQL anonim. Inainte vom introduce in schema de relatie a tabelului Comanda doua atribute suplimentare - ziua_comenzii si ziua livrarii:
ALTER TABLE Comanda
ADD ziua_comenzii CHAR(10);
ALTER TABLE Comanda
ADD ziua_livrarii CHAR(10);
Name
Null?
Type
NR_COMANDA
NOT NULL
NUMBER(38)
DATA_COMANDA
DATE
COD_CLIENT
NOT NULL
NUMBER(38)
DATA_LIVRARE
DATE
TOTAL_TVA
NUMBER(6,2)
TOTAL_VAL_FARA_TVA
NUMBER(6,2)
VAL_TOTALA
NUMBER(6,2)
DATA_PLATII
DATE
ZIUA_COMENZII
CHAR(10)
ZIUA_LIVRARII
CHAR(10)
SET SERVEROUTPUT ON
DECLARE
i NUMBER;
a NUMBER;
z NUMBER;
m NUMBER;
ziua CHAR(10);
data CHAR(10);
BEGIN
SELECT (MAX(nr_comanda)-MIN(nr_comanda)+1) INTO i
FROM Comanda
a:=i;
LOOP
ziua:=LOWER(TO_CHAR(SYSDATE-(a-i),'DAY'));
CASE
WHEN ziua IN ('monday','tuesday','friday') THEN
UPDATE Comanda
SET data_comanda=SYSDATE-(a-i), data_livrare=NEXT_DAY(SYSDATE- (a-i),6), ziua_comenzii = ziua , ziua_livrarii = LOWER(TO_CHAR(NEXT_DAY(SYSDATE- (a-i),6),'DAY'))
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
WHEN ziua='wednesday' THEN
UPDATE Comanda
SET data_comanda=SYSDATE-(a-i), data_livrare=NEXT_DAY(SYSDATE-(a-i),7), ziua_comenzii = ziua , ziua_livrarii = LOWER(TO_CHAR(NEXT_DAY(SYSDATE-(a-i),7),'DAY'))
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
WHEN ziua='thursday' THEN
UPDATE Comanda
SET data_comanda=SYSDATE-(a-i), data_livrare=NEXT_DAY(SYSDATE-(a-i)+7,6), ziua_comenzii = ziua, ziua_livrarii = LOWER(TO_CHAR( NEXT_DAY(SYSDATE-(a-i)+7,6) ,'DAY'))
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
WHEN ziua IN ('saturday','sunday') THEN
UPDATE Comanda
SET data_comanda=SYSDATE-(a-i), data_livrare=NEXT_DAY(SYSDATE-(a-i)+7,2),ziua_comenzii=ziua , ziua_livrarii = LOWER(TO_CHAR(NEXT_DAY(SYSDATE-(a-i)+7,2),'DAY'))
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
ELSE DBMS_OUTPUT.PUT_LINE(ziua);
END CASE
i:=i-1;
EXIT WHEN i=0;
END LOOP
SELECT (MAX(nr_comanda)-MIN(nr_comanda)+1) INTO i
FROM Comanda;
a:=i;
LOOP
z:=TO_NUMBER(TO_CHAR(SYSDATE-(a-i),'DD'));
m:=TO_NUMBER(TO_CHAR(SYSDATE-(a-i),'MM'));
CASE
WHEN z<=15 THEN
ziua:=LOWER(TO_CHAR(LAST_DAY(SYSDATE-(a-i)),'DAY'));
IF ziua IN ('saturday','sunday') THEN
UPDATE Comanda
SET data_platii=NEXT_DAY(LAST_DAY(data_comanda),2)
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
ELSE
UPDATE Comanda
SET data_platii=LAST_DAY(data_comanda)
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
END IF
WHEN z>15 THEN
m:=m+1;
data:='15/'||TO_CHAR(m)||'/2008';
ziua:=LOWER(TO_CHAR(TO_DATE(data,'DD/MM/YYYY'),'DAY'));
IF ziua IN ('saturday','sunday') THEN
UPDATE Comanda
SET data_platii=NEXT_DAY(TO_DATE(data,'DD/MM/YYYY'),2)
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
ELSE
UPDATE Comanda
SET data_platii=TO_DATE(data,'DD/MM/YYYY')
WHERE nr_comanda=(SELECT MAX(nr_comanda)-(a-i) FROM Comanda);
END IF;
END CASE
i:=i-1;
EXIT WHEN i=0;
END LOOP;
UPDATE Comanda
SET ziua_comenzii='luni'
WHERE ziua_comenzii='monday';
UPDATE Comanda
SET ziua_comenzii='marti'
WHERE ziua_comenzii='tuesday';
UPDATE Comanda
SET ziua_comenzii='miercuri'
WHERE ziua_comenzii='wednesday';
UPDATE Comanda
SET ziua_comenzii='joi'
WHERE ziua_comenzii='thursday';
UPDATE Comanda
SET ziua_comenzii='vineri'
WHERE ziua_comenzii='friday';
UPDATE Comanda
SET ziua_comenzii='sambata'
WHERE ziua_comenzii='saturday';
UPDATE Comanda
SET ziua_comenzii='duminica'
WHERE ziua_comenzii='sunday';
UPDATE Comanda
SET ziua_livrarii='luni'
WHERE ziua_livrarii='monday';
UPDATE Comanda
SET ziua_livrarii='vineri'
WHERE ziua_livrarii='friday';
UPDATE Comanda
SET ziua_livrarii='sambata'
WHERE ziua_livrarii='saturday';
END;
PL/SQL procedure successfully completed.
DROP TABLE Livrare_vineri;
CREATE TABLE Livrare_vineri
AS
SELECT nr_comanda, data_comanda, ziua_comenzii, data_livrare, ziua_livrarii, data_platii
FROM comanda
WHERE ziua_livrarii='vineri';
DROP TABLE Livrare_luni;
CREATE TABLE Livrare_luni
AS
SELECT nr_comanda, data_comanda, ziua_comenzii,data_livrare, ziua_livrarii, data_platii
FROM comanda
WHERE ziua_livrarii='luni';
DROP TABLE Livrare_sambata;
CREATE TABLE Livrare_sambata
AS
SELECT nr_comanda, data_comanda, ziua_comenzii,data_livrare, ziua_livrarii, data_platii
FROM comanda
WHERE ziua_livrarii='sambata';
Livrare_luni
NR
COMANDA
DATA
COMANDA
ZIUA
COMENZII
DATA
LIVRARE
ZIUA
LIVRARII
DATA
PLATII
10502
19-Apr-08
sambata
28-Apr-08
luni
15-May-08
10503
20-Apr-08
duminica
28-Apr-08
luni
15-May-08
10509
26-Apr-08
sambata
5-May-08
luni
15-May-08
10510
27-Apr-08
duminica
5-May-08
luni
15-May-08
10516
3-May-08
sambata
12-May-08
luni
2-Jun-08
10517
4-May-08
duminica
12-May-08
luni
2-Jun-08
Livrare_vineri
NR
COMANDA
DATA
COMANDA
ZIUA
COMENZII
DATA
LIVRARE
ZIUA
LIVRARII
DATA
PLATII
10501
18-Apr-08
vineri
25-Apr-08
vineri
15-May-08
10504
21-Apr-08
luni
25-Apr-08
vineri
15-May-08
10505
22-Apr-08
marti
25-Apr-08
vineri
15-May-08
10507
24-Apr-08
joi
2-May-08
vineri
15-May-08
10508
25-Apr-08
vineri
2-May-08
vineri
15-May-08
10511
28-Apr-08
luni
2-May-08
vineri
15-May-08
10512
29-Apr-08
marti
2-May-08
vineri
15-May-08
10514
1-May-08
joi
9-May-08
vineri
2-Jun-08
10515
2-May-08
vineri
9-May-08
vineri
2-Jun-08
10518
5-May-08
luni
9-May-08
vineri
2-Jun-08
Livrare_sambata
NR
COMANDA
DATA
COMANDA
ZIUA
COMENZII
DATA
LIVRARE
ZIUA
LIVRARII
DATA
PLATII
10506
23-Apr-08
miercuri
26-Apr-08
sambata
15-May-08
10513
30-Apr-08
miercuri
3-May-08
sambata
15-May-08
Se observa folosirea pentru operatiile cu date calendaristice a functiilor disponibile in Oracle: LAST-DAY(data) care intoarce ultima zi din luna in care se afla data-argument, NEXT-DAY(data, zi) care furnizeaza data primei zile (luni, marti, etc.) ce urmeaza datei argument si ADD-MONTHS(data, nr.) care aduna "nr." luni la data argument.
De asemenea o importanta deosebita in manipularea datelor o au functiile de conversie intre tipurile majore de date (numere, siruri de caractere si date calendaristice):
TO_CHAR, TO_DATE si TO_NUMBER.
2) La cati clienti, pe cate comenzi si in valoare de cati RON a comercializat marfa furnizorul X ? Pentru aceasta interogare complexa vom folosi un bloc anonim PL/SQL, dar mai intai, pentru a evita anumite neplaceri vom transforma in tabelul Furnizor, in coloana nume_furnizor, toate literele in litere mari cu ajutorul functiei UPPER():
UPDATE furnizor
SET nume_furnizor = UPPER(nume_furnizor);
SELECT nume_furnizor FROM furnizor;
NUME_FURNIZOR
LUK OIL
PETROM S.A.
OMV
MOL HUNGARY
SHELL OIL
GENERAL MOTORS
SET SERVEROUTPUT ON
VARIABLE v CHAR(15);
ACCEPT nr_com PROMPT 'Introduceti numele furnizorului'
DECLARE
nr_com CHAR(15):=&nr_com;
BEGIN
:v:=UPPER(nr_com);
DECLARE
CURSOR dep_cursor1 IS
SELECT DISTINCT a.nr_comanda
FROM articole_comenzi a, stoc_produse p
WHERE a.cod_produs=p.cod_produs AND a.cod_furnizor IN
(SELECT f.cod_furnizor
FROM furnizor f, stoc_produse p1
WHERE f.cod_furnizor=p1.cod_furnizor AND nume_furnizor=:v)
ORDER BY nr_comanda;
CURSOR dep_cursor2 IS
SELECT DISTINCT cod_client
FROM articole_comenzi a, comanda c
WHERE a.nr_comanda=c.nr_comanda
AND cod_furnizor =(SELECT cod_furnizor
FROM furnizor
WHERE nume_furnizor=:v)
ORDER BY cod_client;
dep_rec1 dep_cursor1%rowtype;
dep_rec2 dep_cursor2%rowtype;
numec CHAR(20);
val NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE( :v ||' a livrat marfa pe COMENZILE');
IF NOT dep_cursor1%isopen THEN
OPEN dep_cursor1;
END IF
LOOP
FETCH dep_cursor1 INTO dep_rec1;
EXIT WHEN dep_cursor1%notfound OR dep_cursor1%rowcount>10;
DBMS_OUTPUT.PUT_LINE (dep_rec1.nr_comanda);
END LOOP
DBMS_OUTPUT.PUT_LINE('Adica pe un numar de '||dep_cursor1%rowcount||' comenzi');
CLOSE dep_cursor1;
DBMS_OUTPUT.PUT_LINE( ' LA CLIENTII ');
IF NOT dep_cursor2%isopen THEN
OPEN dep_cursor2;
END IF
LOOP
FETCH dep_cursor2 INTO dep_rec2;
EXIT WHEN dep_cursor2%notfound OR dep_cursor2%rowcount>10;
SELECT firma INTO numec
FROM client WHERE cod_client=dep_rec2.cod_client;
DBMS_OUTPUT.PUT_LINE (numec);
END LOOP
DBMS_OUTPUT.PUT_LINE('Adica la un numar de '||dep_cursor2%rowcount|| ' clienti');
CLOSE dep_cursor2;
SELECT SUM(tva+val_fara_tva) INTO val
FROM articole_comenzi
WHERE cod_furnizor = (SELECT cod_furnizor
FROM furnizor
WHERE nume_furnizor=:v);
DBMS_OUTPUT.PUT_LINE('Valoarea marfurilor comercializate catre acestia fiind de '||val||' RON');
END
END
Dupa lansarea in executie a blocului PL/SQL de mai sus si introducerea de la tastatura a furnizorului dorit (indiferent de formatul upper sau lower al caracterului), vom obtine:
old 2: nr_com CHAR(15):=&nr_com;
new 2: nr_com CHAR(15):='shell oil';
SHELL OIL a livrat marfa pe COMENZILE
10504
10505
10511
10513
10516
10517
Adica pe un numar de 6 comenzi
LA CLIENTII
Henkel Romania
Faur S.A.
Air France
Dacia S.A.
Bush investment inc.
Adica la un numar de 5 clienti
Valoarea marfurilor comercializate catre acestia fiind de 1115962.75 RON
PL/SQL procedure successfully completed.
- Sunt variabile de legatura cu aplicatia in care ruleaza motorul PL/SQL. Trebuie declarate in aplicatie si pot fi accesate si modificate in blocuri.
-Se declara in afara blocului PL/SQL cu ajutorul cuvantului VARIABLE
-Se afiseaza in afara blocului cu comanda PRINT
-Pentru utilizarea lor in cadrul blocului sau intr-o fraza SQL din afara blocului se prefixeaza cu ":".
3) Sa se determine cei mai vanduti trei combustibili dintre produsele comercializate si, evident, cei mai slab tranzactionati combustibili.
Fiind, de asemenea, o solicitare complexa vom folosi un bloc PL/SQL anonim:
SET SERVEROUTPUT ON
DECLARE
CURSOR dep_cursor3 IS
SELECT denumire, SUM(cantitate) AS volum_litri, COUNT(*) AS nr_comenzi, ROUND(SUM(cantitate*pret_unitar)) AS valoare_RON
FROM Articole_comenzi a, Stoc_produse p
WHERE (a.cod_produs=p.cod_produs) AND ((denumire LIKE '%otorina%') OR (denumire LIKE '%enzina%')) AND u_m='Litri'
GROUP BY denumire
ORDER BY volum_litri DESC;
CURSOR dep_cursor4 IS
SELECT denumire, SUM(cantitate) AS volum_litri, COUNT(*) AS nr_comenzi, ROUND(SUM(cantitate*pret_unitar)) AS valoare_RON
FROM articole_comenzi a, stoc_produse p
WHERE (a.cod_produs=p.cod_produs) AND ((denumire LIKE '%otorina%') OR (denumire LIKE '%enzina%')) AND u_m='Litri'
GROUP BY denumire
ORDER BY volum_litri ASC;
dep_rec3 dep_cursor3%rowtype;
dep_rec4 dep_cursor4%rowtype;
BEGIN
DBMS_OUTPUT.PUT_LINE( 'Cei mai vanduti 3 combustibili sunt');
IF NOT dep_cursor3%isopen THEN
OPEN dep_cursor3;
END IF;
LOOP
FETCH dep_cursor3 INTO dep_rec3;
EXIT WHEN dep_cursor3%notfound OR dep_cursor3%rowcount>3;
DBMS_OUTPUT.PUT_LINE('Locul' ||dep_cursor3%rowcount||' '||dep_rec3.denumire ||LPAD(dep_rec3.volum_litri,20,'.') ||' litri vanduti pe '|| dep_rec3.nr_comenzi || ' comenzi in valoare de '||dep_rec3.valoare_RON||' RON');
END LOOP;
CLOSE dep_cursor3;
DBMS_OUTPUT.PUT_LINE(LPAD('=',100,'='));
DBMS_OUTPUT.PUT_LINE( 'Iar cei mai putin vanduti 3 combustibili sunt:');
IF NOT dep_cursor4%isopen THEN
OPEN dep_cursor4;
END IF;
LOOP
FETCH dep_cursor4 INTO dep_rec4;
EXIT WHEN dep_cursor4%notfound OR dep_cursor4%rowcount>3;
IF dep_cursor4%rowcount=1 THEN
DBMS_OUTPUT.PUT_LINE('Locul' ||dep_cursor4%rowcount||' (din coada!!!) '||dep_rec4.denumire ||LPAD(dep_rec4.volum_litri,20,'.') ||' litri vanduti pe '|| dep_rec4.nr_comenzi || ' comenzi in valoare de '||dep_rec4.valoare_RON||' RON');
ELSE
DBMS_OUTPUT.PUT_LINE('Locul' ||dep_cursor4%rowcount||' '||dep_rec4.denumire ||lpad(dep_rec4.volum_litri,20,'.') ||' litri vanduti pe '|| dep_rec4.nr_comenzi || ' comenzi in valoare de '||dep_rec4.valoare_RON||' RON');
END IF
END LOOP
CLOSE dep_cursor4;
END
Dupa executia acestui bloc PL/SQL anonym vom obtine:
Cei
mai vanduti 3 combustibili sunt
Locul1 Benzina fara plumb Mol Dynamic Euro4 .1570 litri vanduti
pe 3 comenzi in valoare de 4193700 RON
Locul2 Benzina fara plumb CO95 .1465 litri vanduti pe 4 comenzi in
valoare de 3128197 RON
Locul3 Motorina Euro4 .1410 litri vanduti pe 3 comenzi in
valoare de 2528437 RON
=========================================================================
Iar cei mai putin vanduti 3 combustibili sunt:
Locul1 (din coada!!!) Benzina V-Power Euro4 ..340 litri vanduti
pe 1 comenzi in valoare de 468180 RON
Locul2 Benzina fara plumb OMV Carerra 100 ..400 litri vanduti pe
1 comenzi in valoare de 664000 RON
Locul3 Benzina fara plumb CO98 ..580 litri vanduti pe 2 comenzi in
valoare de 677030 RON
PL/SQL procedure successfully completed.
4) Ultima intrebare din acest subcapitol se refera la utilizarea asa-numitelor functii analitice (OLAP- On Line Analytical Processing). Astfel obtinerea unor subtotaluri devine mai facila decat in varianta clasica in care frazele SELECT erau conectate prin operatorul UNION.
Functia GROUPING poate fi inclusa in fraza SELECT, argumentul acestei functii fiind coloana da grupare. Ea returneaza 1 atunci cand coloana respectiva este inclusa intr-un bloc de agregare superior sau 0 pentru linii "normale" (din afara subtotalurilor).
Functia CUBE permite ca datele sa fie agregate simultan dupa doi, trei, etc. parametri.
Atunci sa se afiseze clientii si valoare comenzilor acestora pe fiecare luna, subtotal pentru fiecare luna, subtotal pentru fiecare client si toate lunile si subtotal pentru toti clientii si toate lunile (total general):
SELECT
EXTRACT (MONTH FROM data_comanda) AS luna,
CASE GROUPING(nume)
WHEN 1
THEN
CASE GROUPING (EXTRACT (MONTH FROM data_comanda))
WHEN 1
THEN ' TOTAL GENERAL = '
ELSE chr(123)||'----- ----- ---------------Total achizitii pentru luna----- ----- ---------'||chr(125)||' '|| EXTRACT (MONTH FROM data_comanda)
END
ELSE
CASE GROUPING (EXTRACT (MONTH FROM data_comanda))
WHEN 1
THEN ' Total achizitii pentru clientul ----->'|| nume
ELSE nume
END
END AS nume_client,
SUM(val_totala) AS valoare
FROM Comanda co, Client c
WHERE co.cod_client=c.cod_client
GROUP BY CUBE(EXTRACT (MONTH FROM data_comanda), nume)
ORDER BY EXTRACT (MONTH FROM data_comanda), nume
Functii analiticeOLAP (On Line Analytical Processing)
LUNA
NUME_CLIENT
VALOARE
4
Dassin
7110.25
4
Jolt
1547.5
4
Lazarovici
2587.5
4
Millwake
5202
4
Otto
4034.4
4
Popescu
2542.5
4
Sfrija
2548.3
4
Tomescu
2670.7
4
4
28243.15
5
Bush
12321.9
5
Coman
7678.55
5
Lozba
3631.5
5
Otto
1400
5
5
25031.95
Total achizitii pentru clientul ----->Bush
12321.9
Total achizitii pentru clientul ----->Coman
7678.55
Total achizitii pentru clientul ----->Dassin
7110.25
Total achizitii pentru clientul ----->Jolt
1547.5
Total achizitii pentru clientul ----->Lazarovici
2587.5
Total achizitii pentru clientul ----->Lozba
3631.5
Total achizitii pentru clientul ----->Millwake
5202
Total achizitii pentru clientul ----->Otto
5434.4
Total achizitii pentru clientul ----->Popescu
2542.5
Total achizitii pentru clientul ----->Sfrija
2548.3
Total achizitii pentru clientul ----->Tomescu
2670.7
TOTAL GENERAL =
53275.1
7. BLOCURI PL/SQL DENUMITE -PROCEDURI, FUNCTII, TRIGGERI
Spre deosebire de blocurile anonime blocurile numite (sau denumite) prezinta o sectiune suplimentara - antetul (in care se specifica numele, parametrii si alte clauze specifice) si care au urmatoarele proprietati:
-pot fi proceduri /functii;
-se pot stoca la nivel de Oracle Server (proceduri/functii stocate) sau de aplicatie (Developer Suite);
-se pot grupa in pachete de programe (PACKAGE)
-variabilele declarate in zona declarativa a procedurii se numesc FORMAL PARAMETERS. Pentru acestia se pot specifica valori implicite (DEFAULT)
-variabilele utilizate in apelul procedurii/functiei se numesc ACTUAL PARAMETERS
-cand procedura/functia e apelata, variabilele din procedura sunt iincarcate cu valorile variabilelor definite in zona declarativa a blocului anonym;
-in corpul procedurilor/functiilor nu se pot utiliza variabile globale sau de substitutie, acestea vor fi transmise in subprograme cu ajutorul parametrilor;
-pentru afisarea erorilor aparute la compilare se utilizeaza SHOW ERRORS
Parametrii utilizati intr-o procedura (formal parameters-parametri formali) pot fi de tip:
- in (valoarea lui 'actual parameter' este transferata in variabila definita in procedura. Aceasta variabila este considerata read-only). Cand procedura se incheie, controlul revine mediului apelant, actual parameter nu se modifica. Este modul implicit. Un astfel de parametru poate apare doar in partea dreapta a (:=)!
- out (valoarea lui "formal parameter" este transferata in "actual parameter" cand procedura se incheie). Un astfel de parametru poate apare doar in partea stanga a (:=)!
- in out (valorile sunt transferate de la un tip de variabila la celalalt (la lansarea in executie/terminarea procedurii)). Un astfel de parametru poate apare in ambele parti ale (:=)!
1) Sa se creeze o procedura, numita "calcul furnizor", prin care, cu ajutorul a doi parametri de intrare - numele unui furnizor X si numarul unei comenzi Y, sa se determine valoarea produselor furnizate de X pe comanda Y, evident daca acest lucru exista.
Pentru a testa daca un furnizor X a livrat marfa pe o anumita comanda Y se va crea functia check_furnizor care returneaza TRUE in cazul in care se regasesc produse ale lui X pe comanda Y si FALSE in caz contrar:
CREATE OR REPLACE FUNCTION check_furnizor(nf IN furnizor.nume_furnizor%type, c IN comanda.nr_comanda%type)
RETURN Boolean
IS
v NUMBER;
BEGIN
v:=0;
SELECT SUM(cantitate*pret_unitar) INTO v
FROM stoc_produse s, articole_comenzi a
WHERE s.cod_produs=a.cod_produs AND nr_comanda=c AND s.cod_furnizor = (SELECT cod_furnizor FROM furnizor f WHERE nume_furnizor=nf);
IF v<>0 THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;
CREATE OR REPLACE PROCEDURE calcul_furnizor(ff IN Furnizor.nume_furnizor%type,nc IN Comanda.nr_comanda%type, val OUT number)
IS
BEGIN
IF (check_furnizor(ff,nc)) THEN
SELECT SUM(pret_unitar*cantitate) INTO val FROM Articole_comenzi a, Stoc_produse p WHERE a.cod_produs=p.cod_produs AND nr_comanda=nc
AND p.cod_furnizor IN (SELECT p1.cod_furnizor FROM Stoc_produse p1, furnizor f WHERE p1.cod_furnizor=f.cod_furnizor AND nume_furnizor= ff);
DBMS_OUTPUT.PUT_LINE('Furnizorul '||ff||' a livrat marfa pe comanda '||nc||' in valoare de');
ELSE
DBMS_OUTPUT.PUT_LINE('Furnizorul '||ff||' nu a livrat marfa pe comanda '||nc);
END IF;
END;
Pentru lansarea in executie a procedurii folosim urmatorul bloc PL/SQL:
SET SERVEROUTPUT ON
DECLARE
n NUMBER;
f Furnizor.nume_furnizor%type;
c Comanda.nr_comanda%type;
BEGIN
calcul_furnizor(UPPER(&f),&c, n);
DBMS_OUTPUT.PUT_LINE(n);
END;
old 6:
calcul_furnizor(UPPER(&f),&c, n);
new 6: calcul_furnizor(UPPER('mol hungary'),10515, n);
Furnizorul MOL HUNGARY a livrat marfa pe comanda 10515 in valoare de
1135
PL/SQL procedure successfully completed.
old 6:
calcul_furnizor(UPPER(&f),&c, n);
new 6: calcul_furnizor(UPPER('mol hungary'),10501, n);
Furnizorul MOL HUNGARY nu a livrat marfa pe comanda 10501
PL/SQL procedure successfully completed.
2) Sa se creeze o procedura calcul_furnizor_total, care cu ajutorul numelui unui furnizor ca si parametru de intrare sa calculeze numarul de unitati vandute de acesta si valoarea totala .
CREATE OR REPLACE PROCEDURE calcul_furnizor_total(ff IN Furnizor.nume_furnizor%type, val1 OUT NUMBER, val2 OUT NUMBER)
IS
BEGIN
SELECT SUM(cantitate) ,SUM(pret_unitar*cantitate) into val1,val2
FROM Articole_comenzi a, Stoc_produse p
WHERE a.cod_produs=p.cod_produs AND p.cod_furnizor IN
(SELECT p1.cod_furnizor
FROM Stoc_produse p1, Furnizor f
WHERE p1.cod_furnizor=f.cod_furnizor AND nume_furnizor=ff);
END;
Procedura va fi apelata in doua blocuri PL/SQL:
a)
SET SERVEROUTPUT ON
DECLARE
v1 NUMBER;
v2 NUMBER;
CURSOR dep_cursor5 IS
SELECT DISTINCT nume_furnizor
FROM furnizor;
dep_rec5 dep_cursor5%rowtype;
BEGIN
IF NOT dep_cursor5%isopen THEN
OPEN dep_cursor5;
END IF;
LOOP
FETCH dep_cursor5 INTO dep_rec5;
EXIT WHEN dep_cursor5%notfound OR dep_cursor5%rowcount>10;
calcul_furnizor_total(dep_rec5.nume_furnizor, v1,v2);
DBMS_OUTPUT.PUT_LINE ('Furnizorul '||dep_rec5.nume_furnizor||' ---> cantitate totala '||v1||' unitati--->valoare_totala '||v2|| ' RON');
END LOOP;
CLOSE dep_cursor5;
END;
Furnizorul PETROM S.A. ---> cantitate totala 848
unitati--->valoare_totala 5903.25 RON
Furnizorul OMV ---> cantitate totala 1908 unitati--->valoare_totala
9380.4 RON
Furnizorul GENERAL MOTORS ---> cantitate totala 41
unitati--->valoare_totala 7835 RON
Furnizorul LUK OIL ---> cantitate totala 1643 unitati--->valoare_totala
12675.75 RON
Furnizorul SHELL OIL ---> cantitate totala 717 unitati--->valoare_totala
6948.3 RON
Furnizorul MOL HUNGARY ---> cantitate totala 2230
unitati--->valoare_totala 10532.4 RON
PL/SQL procedure successfully completed.
b)
SET SERVEROUTPUT ON
DECLARE
cant NUMBER:=0;
v NUMBER:=0;
v1 NUMBER;
v2 NUMBER;
CURSOR dep_cursor5 IS
SELECT DISTINCT nume_furnizor
FROM furnizor;
dep_rec5 dep_cursor5%rowtype;
BEGIN
IF NOT dep_cursor5%isopen THEN
OPEN dep_cursor5;
END IF;
LOOP
FETCH dep_cursor5 INTO dep_rec5;
EXIT WHEN dep_cursor5%notfound OR dep_cursor5%rowcount>10;
calcul_furnizor_total(dep_rec5.nume_furnizor, v1,v2);
cant:=cant+v1;
v:=v+v2;
END LOOP;
CLOSE dep_cursor5;
DBMS_OUTPUT.PUT_LINE ('Furnizorii au livrat '||cant ||' unitati in valoare de '||v||' RON');
END;
Furnizorii
au livrat 7387 unitati in valoare de 53275.1 RON
PL/SQL procedure successfully completed.
3) Sa se creeze o procedura prin care sa se actualizeze cantitatea disponibila in tabela Stoc_produse, in urma efectuarii comenzilor. In caz ca socul de marfa este insuficient onorarii comenzilor sa se afiseze un mesaj de eroare.
Inainte de descrierea procedurii propriu-zise vom crea functia find_indice(nr_comanda), care returneaza numarul de linii al fiecarei comenzi:
CREATE OR REPLACE FUNCTION find_indice(n_com articole_comenzi.nr_comanda%type)
RETURN NUMBER
IS
ind NUMBER;
BEGIN
SELECT MAX(indice_comanda) INTO ind FROM Articole_comenzi WHERE nr_comanda = n_com;
RETURN ind;
END
CREATE OR REPLACE PROCEDURE ACTUALIZARE(com IN Articole_comenzi.nr_comanda%type)
IS
ind Articole_comenzi.indice_comanda%type;
n NUMBER;
n1 NUMBER;
BEGIN
ind:=find_indice(com);
WHILE(ind>0)
LOOP
SELECT cantitate_disponibila INTO n
FROM Stoc_produse s,Articole_comenzi a
WHERE s.cod_produs=a.cod_produs AND a.nr_comanda= com AND a.indice_comanda=ind;
SELECT cantitate INTO n1 FROM Articole_comenzi
WHERE (nr_comanda=com) AND (indice_comanda=ind);
IF n-n1<0 THEN
RAISE_APPLICATION_ERROR (-20500, 'Eroare la actualizarea stocurilor: Stoc insuficient !!! Refaceti comanda!!!');
ELSE
UPDATE stoc_produse
SET cantitate_disponibila = cantitate_disponibila - (SELECT cantitate FROM Articole_comenzi
WHERE (nr_comanda=com) AND (indice_comanda=ind))
WHERE cod_produs=(SELECT cod_produs FROM Articole_comenzi
WHERE(nr_comanda=com)AND(indice_comanda=ind));
ind:=ind-1;
END IF;
END LOOP;
END
Aceasta procedura poate fi folosita pentru fiecare comanda in parte ca in blocul PL/SQL de mai jos:
SET SERVEROUTPUT ON
DECLARE
nr_com Articole_comenzi.nr_comanda%type:=&nr_com;
BEGIN
actualizare(nr_com);
DBMS_OUTPUT.PUT_LINE('S-au actualizat stocurile in tabela stoc_produse in urma comenzii '|| nr_com);
END;
old 2:
nr_com Articole_comenzi.nr_comanda%type:=&nr_com;
new 2: nr_com Articole_comenzi.nr_comanda%type:=10512;
S-au actualizat stocurile in tabela stoc_produse in urma comenzii 10512
PL/SQL procedure successfully completed.
SELECT s.cod_produs, denumire, cantitate_disponibila
FROM Stoc_produse s, Articole_comenzi a
WHERE s.cod_produs=a.cod_produs AND nr_comanda= 10512
COD_PRODUS
DENUMIRE
CANTITATE_DISPONIBILA
100016
Benzina fara plumb OMV Carerra 100
9850
100017
Ulei transmisie T90 butoi-250l
115
100019
Filtru aer E930 -Renault
70
100021
Solutie parbriz Winter Glass -30
450
Initial stocurile fiind urmatoarele:
100016
Benzina fara plumb OMV Carerra 100
10250
100017
Ulei transmisie T90 butoi-250l
120
100019
Filtru aer E930 -Renault
85
100021
Solutie parbriz Winter Glass -30
500
sau putem aplica procedura de actualizare la toata tabela stoc_produse ca in blocul de mai jos:
SET SERVEROUTPUT ON
DECLARE
CURSOR dep_cursor6 IS
SELECT DISTINCT nr_comanda
FROM Comanda;
dep_rec6 dep_cursor6%rowtype;
BEGIN
IF NOT dep_cursor6%isopen THEN
OPEN dep_cursor6;
END IF;
DBMS_OUTPUT.PUT_LINE ('S-au actualizat comenzile:');
LOOP
FETCH dep_cursor6 INTO dep_rec6;
EXIT WHEN dep_cursor6%notfound;
if dep_recnr_comanda<>10512 THEN
actualizare(dep_recnr_comanda);
DBMS_OUTPUT.PUT_LINE (dep_recnr_comanda);
END IF;
END LOOP;
CLOSE dep_cursor6;
END;
S-au
actualizat comenzile:
10501
10502
10503
10504
10505
10506
10507
10508
10509
10510
10511
10513
10514
10515
10516
10517
10518
PL/SQL procedure successfully completed.
S-a introdus optiunea if dep_recnr_comanda<>10512 pentru a nu dubla actualizarea comenzii 10512.
4) Sa se introduca in baza de date o noua comanda.
Desi pare o cerinta simpla trebuie avut in vedere ca o comanda noua produce efecte directe si indirecte in trei tabele: Comanda, Articole_comenzi, Stoc_produse si Apeluri_tel.
Pe langa facila inserare a unor valori pentru anumite atribute pentru altele trebuie facute calcule destul de complicate. Astfel in tabelul Stoc_produse va trebui sa diminuam stocul_disponibil in functie de cantitatile de produse comandate, in tabelul Articole_comenzi trebuie calculate valoarea atributelor valoare_fara_tva, valoare_tva, iar in tabelul Comanda valoarea atributelor tva, valoare fara tva, valoare totala. Mai mult in acest ultim tabel trebuie evaluate, conform cerintelor enuntate la subcapitolul , data livrarii si data_platii, ziua comenzii si ziua_livrarii. La fel, in tabelul Apeluri_tel, fiecare comanda va avea un corespondent, cod_apel fiind '1' pentru comenzi in valoare mai mica de 1000 RON, '2' pentru comenzi cu valoare cuprinsa intre 1000 si 3000 RON si '2' pentru valoare mai mare ca 3000 RON.
Pentru a putea trata toate modificarile prin care trece baza de date in urma efectuarii unei noi comenzi vom folosi mai multe proceduri si triggeri si un bloc PL/SQL anonim. Toata secventa de operatii care se desfasoara asupra datelor din B.D. reprezinta de fapt o tranzactie. Pe scurt pasii sunt:
Pasul I. Se insereaza o noua linie in tabelul Comanda. Inainte de a se permite aceasta operatiune trigger-ul secure-insert (BEFORE INSERT) verifica ora si ziua. In caz ca suntem in afara programului de lucru se semnaleaza eroare. Dupa inregistrarea efectiva a tuplului in tabelul Comanda trigger-ul update_comanda (AFTER INSERT) calculeaza valoarea atributelor data_livrarii, data_platii, ziua_comenzii si ziua_livrarii plecand de la sysdate.
Pasul II. Se insereaza o noua linie in tabelul Articole_comenzi. Dupa aceasta operatie trigger-ul actualizez_stoc (AFTER INSERT) calculeaza atributele tva si val_fara_tva in Articole_comenzi si total_tva, total_val_fara_tva si val_totala in tabelul Comanda. Mai mult, se apeleaza procedura de actualizare a stocurilor (creata la intrebarea de mai sus -3). In caz ca se depaseste stocul disponibil pentru produsul comandat se semnaleaza eroare.
Pasul III. Se insereaza o noua linie in tabelul Apeluri_tel.
Toata aceasta secventa de operatii are sens numai daca se deruleaza in totalitate. Se observa ca exista motive sa apara erori. De exemplu se poate inregistra o comanda in tabelul Comanda, dar in tabelul Articole_comenzi inregistrarea esueaza datorita depasirii stocului_disponibil.
Pentru a evita astfel de neplaceri avem la dispozitie comenzile ROLLBACK care anuleaza modificarile aduse bazei de date de la ultima tranzactie incheiata cu success (sau de la conectarea la serverul bazei de date) si COMMIT prin care modificarile vor deveni permanente in baza de date si, de asemenea, vizibile de catre ceilalti utilizatori.
Daca tranzactia este prea "stufoasa" avem posibilitatea folosirii unor puncte intermediare de salvare a datelor numite SAVEPOINT-uri.
CREATE OR REPLACE PROCEDURE insert_articole1(nr_com IN NUMBER, ind IN NUMBER, cod_p IN NUMBER, cant IN NUMBER)
IS
f NUMBER;
BEGIN
SELECT cod_furnizor INTO f
FROM stoc_produse
WHERE cod_produs=cod_p;
INSERT INTO Articole_comenzi(nr_comanda, indice_comanda, cod_produs, cod_furnizor, cantitate) VALUES(nr_com,ind,cod_p,f,cant);
END;
(la apelarea acestei proceduri se insereaza in tabelul Articole_comenzi un nou tuplu)
CREATE OR REPLACE PROCEDURE insert_comanda(cod_cl IN client.cod_client%type)
IS
BEGIN
INSERT INTO Comanda(nr_comanda, data_comanda, cod_client) VALUES(secv_com.nextval, sysdate, cod_cl);
END;
(la apelarea acestei proceduri se insereaza in tabelul Comanda un nou tuplu)
CREATE OR REPLACE FUNCTION cod n_com NUMBER)
RETURN NUMBER
IS
v NUMBER;
BEGIN
SELECT val_totala INTO v FROM Comanda
WHERE nr_comanda = n_com;
CASE
WHEN v<1000 THEN RETURN 1;
WHEN v>=1000 AND v<3000 THEN RETURN 2;
WHEN v>=3000 THEN RETURN 3;
END CASE;
END;
(aceasta functie returneaza valoarea atributului cod_apel din tabelul Apeluri_tel)
CREATE OR REPLACE PROCEDURE insert_apeluri cod_cl IN client.cod_client%type)
IS
c NUMBER;
nc NUMBER;
BEGIN
SELECT MAX(nr_comanda) INTO nc FROM Comanda;
c:=cod(nc);
EXECUTE IMMEDIATE 'INSERT INTO Apeluri_tel (cod_client,cod_apel) VALUES ( ' ||cod_cl || ',' ||c|| ')';
END;
(la apelarea acestei proceduri se insereaza in tabelul Apeluri_tel un nou tuplu)
Se oserva folosirea in corpul acestei proceduri a comenzii EXECUTE IMMEDIATE, o facilitate introdusa de Oracle 8i. In mod normal, PL/SQL nu permite crearea si stergerea de tabele, definirea de restrictii, in general comenzi DDL(Data Definition Language). Prin folosirea comenzii de mai sus acest lucru devine posibil (SQL Dinamic).
CREATE OR REPLACE
TRIGGER actualizez_stoc AFTER INSERT ON Articole_comenzi
DECLARE
nc NUMBER;
BEGIN
SELECT MAX(nr_comanda) INTO nc
FROM Comanda;
actualizare(nc);
UPDATE Articole_comenzi ac
SET val_fara_tva=cantitate*(SELECT pret_unitar FROM Stoc_produse sp WHERE ac.cod_produs=sp.cod_produs)/1.19
WHERE nr_comanda=nc;
UPDATE Articole_comenzi ac SET
tva = val_fara_tva*0.19
WHERE nr_comanda=nc;
UPDATE Comanda cc SET
val_totala=(SELECT SUM(cantitate*pret_unitar)
FROM Articole_comenzi a, Stoc_produse sp
WHERE (a.cod_produs=sp.cod_produs) AND
nr_comanda=nc)
WHERE nr_comanda=nc;
UPDATE Comanda cc SET
total_TVA=0.19*val_totala/1.19
WHERE nr_comanda=nc;
UPDATE Comanda cc SET
total_val_fara_TVA=val_totala/1.19
WHERE nr_comanda=nc;
END;
(Dupa inserarea unui tuplu in tabelul Articole_comenzi acest declansator calculeaza valoarea atributelor tva si val_fara_tva in tabelul Articole_comenzi si total_tva, total_val_fara_tva si val_totala in tabelul Comanda)
CREATE OR REPLACE
TRIGGER update_comanda AFTER INSERT ON Comanda
DECLARE
nc NUMBER;
z NUMBER;
m NUMBER;
data CHAR(10);
ziua CHAR(10);
BEGIN
ziua:=LOWER(TO_CHAR(SYSDATE,'DAY'));
SELECT MAX(nr_comanda) INTO nc
FROM Comanda;
CASE
WHEN ziua IN ('monday','tuesday','friday') THEN
UPDATE Comanda
SET data_livrare=NEXT_DAY(SYSDATE,6)
WHERE nr_comanda=nc;
WHEN ziua ='wednesday' THEN
UPDATE Comanda
SET data_livrare=NEXT_DAY(SYSDATE,7)
WHERE nr_comanda=nc;
WHEN ziua ='thursday' THEN
UPDATE Comanda
SET data_livrare=NEXT_DAY(SYSDATE+7,6)
WHERE nr_comanda=nc;
WHEN ziua IN ('saturday','sunday') THEN
UPDATE Comanda
SET data_livrare=NEXT_DAY(SYSDATE+7,2)
WHERE nr_comanda=nc;
END CASE;
UPDATE Comanda
SET ziua_comenzii ziua, ziua_livrarii=LOWER(TO_CHAR(data_livrare,'DAY'))
WHERE nr_comanda=nc;
z:=TO_NUMBER(TO_CHAR(SYSDATE,'DD'));
m:=TO_NUMBER(TO_CHAR(SYSDATE,'MM'));
SELECT LOWER(TO_CHAR(LAST_DAY(data_comanda),'DAY'))
INTO ziua
FROM Comanda
WHERE Nr_comanda=nc;
CASE
WHEN z<=15 THEN
IF ziua IN ('saturday','sunday') THEN
UPDATE Comanda
SET data_platii=NEXT_DAY(LAST_DAY(data_comanda),2)
WHERE nr_comanda=nc;
ELSE
UPDATE Comanda
SET data_platii=LAST_DAY(data_comanda)
WHERE nr_comanda=nc;
END IF;
WHEN z>15 THEN
m:=m+1;
data:='15/'||TO_CHAR(m)||'/2008';
IF ziua IN ('saturday','sunday') THEN
UPDATE Comanda
SET data_platii=NEXT_DAY(TO_DATE(data,'DD/MM/YYYY'),2)
WHERE nr_comanda=nc;
ELSE
UPDATE Comanda
SET data_platii=TO_DATE(data,'DD/MM/YYYY')
WHERE nr_comanda=nc;
END IF;
END CASE;
END;
(Dupa inserarea unui tuplu in tabelul Comanda acest declansator completeaza valoarea atributelor data_livrare, data_platii, ziua_comenzii si ziua_livrarii in tabelul in tabelul Comanda)
CREATE OR REPLACE
TRIGGER SECURE_INSERT BEFORE INSERT ON Comanda
BEGIN
IF (TO_CHAR(SYSDATE,'DY') IN ('SAT', 'SUN')) OR (TO_CHAR (SYSDATE,'HH24:MI') NOT BETWEEN '08:00' AND '16:00') THEN
RAISE_APPLICATION_ERROR (-20500, 'Este permisa inserarea in tabela numai in timpul programului de lucru!');
END IF;
END;
(Acest trigger permite accesul controlat pentru inregistrarea de noi comenzi in baza de date).
SET SERVEROUTPUT ON;
DECLARE
cod_client NUMBER:=&cod_client;
nc NUMBER;
cod_produs NUMBER;
cantitate NUMBER;
BEGIN
insert_comanda(cod_client);
SELECT MAX(nr_comanda) INTO nc
FROM Comanda
cod_produs:=&cod_produs;
cantitate:=&cantitate;
insert_articole1(nc,1,cod_produs,cantitate);
Insert_apeluri(cod_client);
END;
ROLLBACK/COMMIT;
Acest bloc PL/SQL apeleaza procedurile, trigger-ii si functiile de mai sus. In caz de esec (aparitia unor eventuale erori generate pe parcursul desfasurarii tranzactiei) vom incheia blocul PL/SQL cu ROLLBACK, in caz contrar cu COMMIT.
A. eroare generata de incercarea de lansare a unei noi comenzi in afara programului de lucru!(se foloseste ROLLBACK);
old 11:
cantitate:=&cantitate;
new 11: cantitate:=20;
DECLARE
ERROR at
line 1:
ORA-20500: Este permisa inserarea in tabela numai in timpul programului de
lucru!
ORA-06512: at 'SYSMAN.SECURE_INSERT', line 3
ORA-04088: error during execution of trigger 'SYSMAN.SECURE_INSERT'
ORA-06512: at 'SYSMAN.INSERT_COMANDA', line 4
ORA-06512: at line 7
B. eroare generata de incercarea de lansare a unei noi comenzi care depaseste stocul de marfa disponibil! (se foloseste ROLLBACK);
old 11:
cantitate:=&cantitate;
new 11: cantitate:=300;
DECLARE
ERROR at
line 1:
ORA-20500: Eroare la actualizarea stocurilor: Stoc insuficient !!! Refaceti
comanda!!!
ORA-06512: at 'SYSMAN.ACTUALIZARE', line 16
ORA-06512: at 'SYSMAN.ACTUALIZEZ_STOC', line 6
ORA-04088: error during execution of trigger 'SYSMAN.ACTUALIZEZ_STOC'
ORA-06512: at 'SYSMAN.INSERT_ARTICOLE1', line 8
ORA-06512: at line 12
C. Comanda reusita (se foloseste COMMIT);
old 11:
cantitate:=&cantitate;
new 11: cantitate:=35;
PL/SQL procedure successfully completed.
NR
COM.
DATA
COM.
COD
CLIENT
DATA
LIVRARE
DATA
PLATII
TOTAL
TVA
TOTAL
V.F_TVA
VAL
TOT.
ZIUA
COM.
ZIUA
LIVR.
10518
5-May-08
111
9-May-08
2-Jun-08
1390.32
7317.48
8707.8
luni
vineri
10519
5-May-08
106
9-May-08
2-Jun-08
590.12
3105.88
3696
monday
friday
NR
COMANDA
INDICE
COMANDA
COD
PRODUS
COD
FURNIZOR
CANTITATE
TVA
VAL
FARA_TVA
10519
1
100009
1001
35
590.12
3105.88
COD
CLIENT
ORA
APEL
COD
APEL
DESCRIERE
APEL
106
05-MAY-08 03.47.25.593000 PM +01:00
3
8. TIPUL DE DATE LOB
Oracle ofera facilitati pentru manipularea fisierelor in baza de date cu ajutorul atributelor de tip LOB (Large Objects). Dupa cum s-a subliniat si in cap. 5 in functie de zona de pastrare a informatiilor se disting doua categorii de atribute LOB:
- cu stocare interna: Fisierul este stocat chiar in interiorul bazei de date fiind independent de modificarile efectuate in fisierul sursa pe disc. Astfel avem date BLOB (BINARY LOB- date nestructurate) si CLOB (CHARACTER LOB - date sub forma de siruri de caractere).
- cu stocare externa : Fisierul este stocat pe disc, in baza de date fiind inregistrata doar adresa fisierului respectiv. In aceasta categorie intra tipul BFILE.
Plecand de la aceste considerente sa se introduca in tabelul Catalog pentru produsul "Odorizant auto Car fresh" (cod 100014, furnizor 1002) o poza (*. Jpeg) si un film continand reclama (*.avi).
Valoarea unui atribut BLOB este formata din doua parti:
- valoarea LOB propriu-zisa, ce contine sirul de biti din care este formata imaginea
- pointerul LOB (adresa) care indica localizarea fizica in baza de date a valorii LOB de mai sus.
Astfel pentru inserarea unei valori BLOB intr-un tabel vom crea mai intai pointerul LOB (folosind functia Oracle empty_blob()), dupa care vom incarca valoarea LOB. Trebuie precizat ca serverul Oracle nu acceseaza direct fisierele de pe disc, ci trebuie creat obiectul DIRECTORY, corespunzator directorului in care se gasesc fisierele.
CREATE OR REPLACE DIRECTORY MEDIA
AS 'G:IMG';
Directory created.
Dupa care urmam cei doi pasi descrisi mai sus:
6 se inesereaza un nou tuplu in tabelul Catalog, initializand adresele pentru atributele BLOB cu functia empty_blob();
7
INSERT INTO Catalog(Cod_produs, Cod_furnizor, foto, reclama)
VALUES(100014,1002,empty_blob(), empty_blob());
1 row created.
construim un bloc PL/SQL in care:
-se extrag adresele atributelor foto si reclama si se blocheaza inregistrarea cu nu Select For Update;
-se initializeaza doua variabile de tip BFILE cu adresa fizica de pe disc a fisierelor pe care vrem sa le incarcam.
-se incarca continutul fisierelor in baza de date la adresele specificate la primul pas:
Pentru a ne asigura de existenta fisierelor in locatia respectiva, pe disc, utilizam functia DBMS_LOB.FILEEXISTS(adresa BFILE) care returneaza 1 daca exista fisier la adresa respectiva si 0 in caz contrar.
DECLARE
f1 BFILE;
f2 BFILE;
b1 BLOB;
b2 BLOB;
BEGIN
SELECT foto, reclama INTO b1,b2 FROM Catalog WHERE serie_catalog=5001 FOR UPDATE;
f1:=BFILENAME('MEDIA','IM000642.JPG');
f2:=BFILENAME('MEDIA','IM00013MPG');
IF DBMS_LOB.FILEEXISTS(f1)=0 OR DBMS_LOB.FILEEXISTS(f2)=0
THEN
RAISE_APPLICATION_ERROR (-20500, 'Eroare la deschiderea fisierelor');
ELSE
DBMS_LOB.FILEOPEN(f1, DBMS_LOB.FILE_READONLY);
DBMS_LOB.LOADFROMFILE(b1,f1,DBMS_LOB.GETLENGTH(f1));
DBMS_LOB.FILEOPEN(f2, DBMS_LOB.FILE_READONLY);
DBMS_LOB.LOADFROMFILE(b2,f2,DBMS_LOB.GETLENGTH(f2));
DBMS_LOB.FILECLOSE(f1);
DBMS_LOB.FILECLOSE(f2);
COMMIT
END IF;
END;
PL/SQL procedure successfully completed.
Mai repetam pasii de mai sus pentru produsul "Solutie parbriz Winter Glass-30", cod 100021, furnizor 1003.
SELECT Serie_catalog, DBMS_LOB.GETLENGTH(foto) SIZE_FOTO, DBMS_LOB.GETLENGTH(reclama) SIZE_RECLAMA
FROM Catalog
SERIE_CATALOG
SIZE_FOTO
SIZE_RECLAMA
5001
1065984
3955853
5002
886816
464291
9. MANIPULAREA DATELOR ORACLE IN APLICATIILE JAVA
Pentru a realiza conectarea si manipularea datelor unei baze de date o aplicatie Java utilizeaza urmatoarele clase JDBC impreuna cu derivatele lor: Connection, Statement si ResultSet. Se disting urmatoarele etape:
-inregistrarea driverului JDBC;
-crearea unei conexiuni cu BD cu ajutorul unui obiect de tip Connection;
-crearea unui obiect Statement pe baza obiectului Connection, creat mai sus;
-executia unei fraze SQL prin intremediul obiectului Statement, daca e cazul preluarea rezultatului returnat de Server sub forma unui obiect ResultSet;
-validarea actualizarilor (COMMIT) sau invalidarea (ROLLBACK);
-inchiderea conexiunii;
O aplicatie poate initializa si utiliza mai multe obiecte de tip Connection, fiecare comunicand cu serverul ORACLE. Obiectele de tip Connection sunt unitati indivizibile de tranzactie, commiterea realizandu-se la nivelul acestuia.
Pe baza unui obiect Connection se pot construi mai multe obiecte de tip Statement si implicit mai multe seturi de date ResultSet.
Aceste etape sunt urmate in pachetul de clase JAVA din Anexa I. Ele constituie o interfata grafica prin care putem accesa baza de date orcl si tabelele create de noi pe parcursul acestei lucari. Aplicatia deschide o singura conexiune cu baza de date Oracle in clasa principala Start.java.
Manipularea datelor (fraze SELECT, etc.) este asigurata de primul buton din cele patru (Listati .), care utilizeaza metoda executeQuery() a clasei Statement, direct in clasa principala Start.java.
Al doilea buton (Insert produs nou) permite introducerea in tabelul Stoc_Produse a unui produs nou folosind metoda executeUpdate() a clasei Statement intr-o instanta a clasei InsertProdus.java.
Al treilea buton (Insert comanda noua) asigura partea cea mai complexa si anume realizarea unei tranzactii (lnsarea unei comenzi noi, cu efect in tabelele Comanda, Articole_comenzi, Stoc_produse si Apeluri_tel.). Tranzactia se executa prin apelarea de catre aplicatia Java a procedurilor stocate pe serverul ORACLE, folosind clasa CallableStatement (permite transmiterea unor parametri si prelucrarea rezultatelor returnate de procedura sau functia executata) din pachetul java.sql intr-o instanta a clasei InsertComanda.java.
Mai trebuie precizat ca in mod implicit orice actualizare a bazei de date prin intermediul unei intrefete JAVA este definitiva!(COMMIT). Pentru a putea controla acest aspect dezactivam aceasta proprietate cu ajutorul metodei setAutoCommit(false) a clasei Connection la inceputul tranzactiei, iar la sfarsit folosim metodele Rollback() sau Commit() ale aceleiasi clase pentru anularea, respective salvarea tranzactiei. Reactivarea modului de comitere automata poate fi facuta in orice moment prin setAutoCommit(true).
De mentionat ca pentru manipularea datelor BLOB din tabelul Catalog folosirea puntii jdbc:odbc pentru conectarea la baza de date nu mai functioneaza. Continutul atributelor de tip BLOB implica clase si metode ce gestioneaza fluxuri de octeti (InputStream si OutputStream). Atunci este necesara folosirea driverului JDBC Oracle ojdbc14.jar (descarcat de pe site-ul Oracle) si copierea intr-o locatie disponibila mediului run-time Java. Inregistrarea driverului se face cu ajutorul metodei statice registerDriver() definite in clasa DriverManager. Crearea unui obiect Connection se face cu ajutorul metodei statice getConection() a aceleiasi clase DriverManager, informatiile pentru conectare fiind trei parametri de tip sir de caractere:
-jdbc:oracle:thin:@<adresa IP a calculatorului pe care ruleaza serverul Oracle>:<portul de comunicare cu serverul, default 1521>:<numele instantei active a serverului Oracle>;
-user - numele de utilizator Oracle;
-pass - parola.
Al patrulea buton - "Media visualization" (clasa Media.java)- acceseaza datele de tip large object (BLOB) aflate in tabelul Catalog prin deschiderea unui flux.
Un flux este un canal de comunicatie unidirectional intre doua procese. Un flux ce citeste date se numeste flux de intrare. Un flux ce scrie date se numeste flux de iesire. Fluxurile sunt canale de comunicatie seriale pe 8 sau 16 biti. Fluxurile sunt unidirectionale, de la producator la consumator. Fiecare flux are un singur proces producator si un singur proces consumator.
Clasele radacina pentru ierarhiile ce reprezinta fluxuri de caractere sunt:
- Reader - pentru fluxuri de intrare
- Writer - pentru fluxuri de iesire.
Acestea sunt superclase abstracte pentru toate clasele ce implementeaza fluxuri specializate pentru citirea/scrierea datelor pe 16 biti si vor contine metodele comune tuturor. Ca o regula generala, toate clasele din aceste ierarhii vor avea terminatia Reader sau Writer in functie de tipul lor, cum ar fi in exemplele: FileReader, BufferedReader, FileWriter, BufferedWriter, etc. De asemenea, se observa ca o alta regula generala, faptul ca unui flux de intrare XReader ii corespunde uzual un flux de iesire XWriter, insa acest lucru nu este obligatoriu. Cu ajutorul acestor superclase se vor manipula atribute de tip CLOB (Character Large Object).
Clasele radacina pentru ierarhia fluxurilor de octeti sunt:
. InputStream - pentru fluxuri de intrare
. OutputStream - pentru fluxuri de iesire.
Acestea sunt superclase abstracte pentru clase ce implementeaza fluxuri specializate pentru citirea/scrierea datelor la nivel de octet (byte), cazul atributelor BLOB (Binary LargeObject). Ca si in cazul fluxurilor pe caractere denumirile claselor vor avea terminatia superclasei lor: FileInputStream, BufferedInputStream, FileOutputStream, BufferedOutputStream, etc., fiecarui flux de intrare XInputStream corespunzandu-i uzual un flux de iesire XOutputStream, fara ca acest lucru sa fie obligatoriu.
Astfel vom folosi clasa InputStream in programul nostru pentru a descaraca din tabelul Catalog al bazei de date Oracle continutul atributului BLOB si scrierea sa intr-un fisier temporar (proiect.tmp) pe disc. Se foloseste extensia tmp pentru ca este posibil sa nu cunoastem tipul fisierului extras. Deschiderea fisierului se face cu browser-ul Internet Explorer care va determina automat aplicatia necesara vizualizarii (Word, Excel, Windows Media Player, etc.).
Mai trebuie mentionat in incheierea acestei succinte prezentari ca metodele definite in clasele din pachetul java.sql pot genera erori la conectare, interogare, etc. care apar doar la momentul executiei si nu la compilare. Ca urmare orice apel al acestor metode impune gestionarea eventualelor erori prin intermediul blocurilor try catch.