Documente Academic
Documente Profesional
Documente Cultură
SQL analitic
Capitolul 4 SQL analitic ...................................................................................... 155
4.1 Implementarea operaţiei roll-up utilizând extensia ROLLUP
la clauza GROUP BY… ............................................................................ 161
4.2 Utilizarea extensiei CUBE la clauza GROUP BY ..................................... 163
4.3 Utilizarea expresiei GROUPING SETS .................................................... 166
4.4 Operaţia de pivotare ................................................................................... 168
4.5 Utilizarea funcţiilor analitice complexe ..................................................... 171
Rezumat ............................................................................................................ 182
Întrebări… ............................................................Error! Bookmark not defined.
• datele despre produse vor fi extrase din fişierul Excel exemplu.xls, foaia
Articole cu următoarea structură: artid (codul produsului vândut), descriere,
pretunitar, costunitar, stoc, categorieid (codul categoriei din care face parte
produsul) (figura 4.2);
Aceste surse au fost încărcate în baza de date Oracle, utilizând instrumentul SQL
Developer, opţiunea Data Load Wizard/Insert. S-au generat următoarele tabele:
Tabela Vânzări cu următoarea structură: artid varchar2(30) cheie externă (referă
tabela Articole), vanzid integer cheie primară, codclient integer cheie externă (referă
tabela Clienti), agentid integer cheie externă (referă tabela Agenti), cantitate integer,
vanzarea number (10,2), data date (figura 4.3).
Tabela Agenţi cu următoarea structură: agentid integer, nume varchar2(30),
pozitie varchar2(11), oras varchar2(30), judetid varchar2(2) cheie externă (referă
tabela Judete) (figura 4.4).
Tabela Articole cu următoarea structură: artid varchar2(30) cheie primară,
descriere varchar2(40), pretunitar number(10,2), costunitar number(10,2), stoc
integer, categorieid integer (figura 4.5).
Tabela Judeţe cu următoarea structură: Judet varchar2(15), judetid varchar2(2)
cheie primară, resedinta varchar2(21), regiune varchar2(13) (figura 4.6).
SQL analitic 157
Extensia ROLLUP este mai eficientă pentru seturi mari de date, deoarece tabela
se parcurge o singură dată. Sunt posibile şi operaţii roll-up parţiale. Următoarea
cerere SQL:
SELECT oras, an, SUM(cantitate) AS total
FROM vanzari v, agenti a, calendar t
WHERE v.agentid = a.agentid AND v.TimpId = t.TimpId
GROUP BY oras, ROLLUP (an) order by oras;
va genera agregări după (oras, an) şi (oras) şi este identică cu reuniunea a două cereri
SQL:
SELECT oras, an, SUM (cantitate) AS total
SQL analitic 163
Următoarea cerere SQL va genera agregări după (oras), (an), (oras, an), ( )
(figura 4.10):
SELECT oras, an, SUM(cantitate) AS total FROM vanzari v, agenti a, calendar t
WHERE v.agentId = a.agentid AND v.TimpId = t.TimpId GROUP BY CUBE
(oras, an);
şi va returna acelaşi rezultat ca şi reuniunea următoarelor cereri SQL:
SELECT oras, an, SUM(cantitate) AS total
FROM vanzari v, agenti a, calendar t
WHERE v.agentId = a.agentid AND v.TimpId = t.TimpId Group By oras, an
Union
SELECT oras, null, SUM(cantitate) AS total
FROM vanzari v, agenti a, calendar t
Where V.agentid = a.agentid And V.Timpid = T.Timpid Group By oras, null
Union
SELECT null, an, SUM(cantitate) AS total
FROM vanzari v, agenti a, calendar t
Where V.agentid = a.agentid And V.Timpid = T.Timpid Group By null, an
Union
SELECT null, null, SUM(cantitate) AS total
FROM vanzari v, agenti a, calendar t
Where V.agentid = a.agentid And V.Timpid = T.Timpid order by 1;
oras, categorieid, an
categorieid an
oras
()
Extensiile ROLLUP/CUBE simplifică interogarea, permit o analiză mai eficientă
a datelor. Agregarea este foarte importantă în depozitele de date. Pentru a îmbunătăţi
performanţa agregării, Oracle a introdus funcţia GROUPING(), expresia
GROUPING SETS şi clauza PIVOT.
Dezvoltatorii adesea trebuie să determine care tupluri din setul rezultat sunt
totaluri şi care este nivelul de agregare pentru acele totaluri. Funcţia GROUPING()
face distincţie între valorile null existente şi cele care rezultă din agregare, returnând
o valoare 0 pentru primul caz şi o valoare 1, atunci când este detectat un total. Funcţia
GROUPING() se utilizează cu clauza HAVING. Următoarea cerere SQL va afişa
doar cantitatea vândută la nivel de ani, precum şi totalul general (figura 4.11):
SELECT oras, an, SUM(cantitate) AS total, grouping(oras) o
FROM vanzari v, agenti a, calendar t
WHERE v.agentId = a.agentid AND v.TimpId = t.TimpId
Group By Cube (oras, an)
having grouping(oras)=1;
166 Business Intelligence. Teorie şi practică
Următoarea cerere SQL utilizează atributul oras pentru pivotare (numai Iaşi şi
Bucureşti) şi afişează pentru fiecare produs, cantitatea vândută:
Select * From (Select descriere, Oras, Cantitate From vanzari, agenti, articole
Where vanzari.agentid=agenti.agentid And vanzari.artid=articole.artid)
Pivot (sum(cantitate) for oras in (('Bucuresti') as cant_bucuresti, ('Iasi') as
cant_iasi))
order by descriere;
Rezultatul pivotării:
Descriere cant_bucuresti cant_iasi
………………………………………………………………………………………………..
Banda dublu adeziva ogl.19mmx25mx1m 310 (null)
Banda dublu adeziva ogl.25mmx25mx1m 155 (null)
Banda dublu adeziva ornam. 19mmx10m 127 (null)
Banda dublu adeziva ornam. 24mmx10m 137 (null)
Banda dublu adeziva ornam. 4mmx10m 145 (null)
………………………………………………………………………………………
170 Business Intelligence. Teorie şi practică
Se pot utiliza mai multe atribute pentru agregare. Următoarea cerere SQL
utilizează pentru agregare atributele: cantitate şi vanzarea, iar atributul oras (numai
Iaşi şi Bucureşti) pentru pivotare (figura 4.14):
select * from (select descriere, oras, cantitate, vanzarea from vanzari, agenti,
articole where vanzari.agentid=agenti.agentid and vanzari.artid=articole.artid)
pivot(sum(cantitate)as totalcant, sum(vanzarea) as totalvanz for (oras) in
('Iasi','Bucuresti'))
order by descriere;
SQL analitic 171
De asemenea, valorile nule (dacă există) pot fi afişate la început (nulls first) sau
la sfârşit (nulls last):
Select agentid, sum(cantitate) cantitate,
Dense_rank() over (order by sum(cantitate ) desc nulls last) as a_rank
From vanzari group by agentid Order by a_rank;
Pentru fiecare grup, calculează sum(cantitate). Tabela Vanzari are 1282 tupluri
(figura 4.19).
Creare partiţii
Aplicarea funcţiilor analitice pentru fiecare tuplu
al partiţiei/partiţiilor
Clauza ORDER BY
Figura 4.20 Ordinea de procesare [Lane, 2010]
Exemple de partiţii:
• ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING – partiţia este
formată din trei tupluri adiacente (tuplul curent, cel anterior şi următorul
tuplu);
• ROWS BETWEEN CURRENT ROW AND CURRENT ROW – partiţia este
formată numai din tuplul curent;
• ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING – partiţia este
formată numai din tuplul anterior etc.
În cele ce urmează, se vor prezenta câteva exemple de utilizare a partiţiilor.
Următoarea cerere SQL (figura 4.21):
SELECT descriere, luna, SUM (cantitate) AS cantitate_luna, sum(SUM(cantitate))
OVER (PARTITION BY descriere ORDER BY luna ROWS UNBOUNDED
PRECEDING) AS cantitate_cumulata FROM vanzari, calendar, articole
Where Vanzari.Timpid=Calendar.Timpid And Vanzari.Artid=Articole.Artid And
An=2012 Group By Descriere, Luna Order By Descriere, Luna;
va crea partiţii pentru fiecare produs (partition by descriere). Partiţiile au un număr
variabil de tupluri (nu toate produsele se vând în fiecare lună). De exemplu, pentru
produsul “Clips OPEL” partiţia este formată din trei tupluri (lunile 3, 4, 5). Cantitatea
cumulată se calculează pentru fiecare tuplu (adică, lună) din partiţie, începând cu
SQL analitic 177
prima lună a partiţiei şi până la luna curentă. De exemplu, pentru luna 4 cantitatea
cumulată este 177 (121+56) şi pentru luna 5 este 552 (121+56+375):
Clips OPEL 3 121 121
Clips OPEL 4 56 177
Clips OPEL 5 375 552
Următoarea cerere SQL afişează pentru fiecare oraş, agentul cu cele mai mari
vânzări (figura 4.22):
Select Oras, Nume, Total_Vanz, Max_Oras_Vanz
From (Select Oras, Nume, Sum(Vanzarea) As Total_Vanz, Max (Sum(Vanzarea))
Over (Partition By Oras) As Max_Oras_Vanz From Vanzari, Agenti
where vanzari.agentid=agenti.agentid group by oras, nume) where
Total_Vanz=Max_Oras_Vanz;
Următoarea cerere SQL va afişa primii 2 agenţi (în funcţie de vânzări), din fiecare
oras, care au contribuit cu mai mult de 25% la vânzările la nivel de judeţ (figura
4.23):
select judet, oras, nume, vanzari, judet_vanzari
from (select j.judet, a.oras, nume,sum(vanzarea) as vanzari,
sum(sum(vanzarea)) over (partition by j.judet) as judet_vanzari,
rank() over (partition by a.oras order by sum(vanzarea) desc) as rang
from vanzari v , agenti a, judete j where v.agentid=a.agentid and
a.judetid=j.judetid Group By J.Judet, A.Oras, A.Nume Order By J.Judet, A.Oras)
Where Vanzari>0.25*Judet_Vanzari And Rang<=2;
SQL analitic 179
Figura 4.23 Primii doi agenţi din fiecare oraş, care au contribuit cu mai mult
de 25% la vânzările, la nivel de judeţ
Figura 4.24 Agenţii care au vândut mai mult decât media lunară
180 Business Intelligence. Teorie şi practică
Următoarea cerere SQL va afişa pentru fiecare lună, agenţii care au vândut mai
mult decât media lunară (numai pentru 2012) (figura 4.24):
select luna, nume, vanzari, media from (Select an, t.luna , nume, Sum(vanzarea) as
vanzari, Avg(Sum(Vanzarea)) Over (Partition By Luna) As Media
From Vanzari V, Calendar T, Agenti A Where V.Timpid=T.Timpid And
V.Agentid=A.Agentid and an=2012 Group By an, Luna, nume) d
Where D.Vanzari>D.Media And An=2012 Order By Luna ;
Funcţia RATIO_TO_REPORT( ) calculează raportul dintre o valoare şi suma unui
set de valori. Următoarea cerere SQL va afişa pentru fiecare oraş, raportul dintre
valoarea vânzărilor din acel oraş şi total vânzări (numai pentru 19-MAR-2013 -
timpid=26) (figura 4.25):
Select Oras, Sum(vanzarea) As Total, Sum(Sum(vanzarea)) Over () As
Total_general, Ratio_To_Report(Sum(vanzarea)) Over () As Ratio_To_Report
From vanzari, agenti Where vanzari.agentid=agenti.agentid and
vanzari.timpid=26 Group By oras;
similar cu:
GROUP BY CUBE((a, b), c) GROUP BY a, b, c
este identic cu UNION ALL
GROUP BY CUBE(z,c) GROUP BY a, b
UNION ALL
GROUP BY c UNION ALL
GROUP BY ()
GROUP BY CUBE(a, b, c) GROUP BY a, b, c
UNION ALL
GROUP BY a, b
UNION ALL
GROUP BY a,c
UNION ALL
GROUP BY b,c
UNION ALL
GROUP BY a
UNION ALL
GROUP BY b
UNION ALL
GROUP BY c
UNION ALL
GROUP BY ()
Rezumat
Limbajul SQL permite cereri analitice complexe. Se pot:
a) implementa operaţiile analitice de bază: slice, dice, roll-up şi drill-down;
b) agrega datele utilizând extensiile ROLLUP/CUBE,
c) stabili criterii de grupare mai selective cu expresia GROUPING SETS;
d) pivota datele.
De asemenea, se pot utiliza funcţii analitice complexe, dar şi funcţii statistice
complexe (regresie liniară, funcţii statistice pentru testarea ipotezelor etc.).