Sunteți pe pagina 1din 28

Capitolul 4

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.

Obiectivul acestui capitol este de a prezenta o serie de


facilităţi analitice implementate de limbajul SQL

Începând cu standardul SQL-2003, au fost implementate o serie de facilităţi


analitice care au permis utilizatorilor să simplifice codul aplicaţiilor scris cu SQL
sau cu alte limbaje de programare şi să îmbunătăţească performanţa la interogare.
Acest capitol va prezenta numai o parte dintre aceste facilităţi analitice şi anume:
extensia ROLLUP/CUBE la clauza GROUP BY, expresia GROUPING SETS, clauza
PIVOT, precum şi o serie de funcţii analitice complexe. Se va utiliza SGBDR Oracle.
În baza de date Oracle se vor încărca informaţii despre produse, agenţi şi vânzările
realizate de agenţi. Se vor utiliza următoarele surse de date:
• fişierul Excel exemplu.xls, foaia Vânzări cu următoarea structură: VanzID
(codul tranzacţiei), ArtID (codul produsului vândut), CodClient (codul
clientului), AgentID (codul agentului care a făcut tranzacţia), cantitatea
vânduta şi vânzarea (preţ*cantitate), Data (data la care s-a făcut tranzacţia,
formatul de dată MM/DD/YYYY);
• un fişier text Agenti.tsv cu informaţii despre agenţi: agentID, nume, pozitie,
oras, judetID) (figura 4.1);
• un fişier Judete.csv cu informaţii despre judeţe: Judetid, Judet, Resedinta,
Regiune);
156 Business Intelligence. Teorie şi practică

• 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);

Figura 4.1 Fişierul Agenti.tsv

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

Figura 4.2 Fişierul exemplu.xls, foaia Articole

Figura 4.3 Tabela Vânzări


158 Business Intelligence. Teorie şi practică

Figura 4.4 Tabela Agenţi

Figura 4.5 Tabela Articole


SQL analitic 159

Figura 4.6 Tabela Judeţe

Figura 4.7 Tabela dimensională Calendar


160 Business Intelligence. Teorie şi practică

S-au creat patru tabele dimensionale: Articole, Agenţi, Calendar, Judete şi o


tabelă de fapte Vânzări cu doi indicatori: cantitate şi vânzarea. Tabela dimensională
Calendar are următoarea structură: timpid number (10) cheie primară generată cu
ajutorul unei secvenţe, perioada date, ziua varchar2(7), luna varchar2(7), an
varchar2(4) (figura 4.7). De asemenea, s-a şters atributul Data din tabela Vânzări şi
s-a definit atributul Timpid cu rol de cheie externă. Schema bazei de date este
prezentată în figura 4.8.

Figura 4.8 Structura tabelelor

S-au identificat următoarele ierarhii:


agenti→oras→judet→regiune
artid→categorieid
timpid→perioada→ziua→luna→an

Cu ajutorul limbajului SQL se vor implementa operaţiile analitice de bază: slice,


dice şi roll-up. Operaţia Slice presupune selecţia unei singure valori. Se utilizează o
singură dimensiune. Următoarea cerere SQL va afişa vânzările realizate de agentul
Ionescu Dan:
SELECT artId, timpid, vanzarea FROM vanzari, agenti WHERE
vanzari.agentid=agenti.agentid and nume ='Ionescu Dan';
Operaţia Dice presupune selecţii în două sau mai multe dimensiuni (un
sub-cub). Nu implică reducerea numărului de dimensiuni ale cubului. Următoarea
cerere SQL va afişa vânzările realizate de agentul Ionescu Dan, în zilele de 13 şi
14 martie 2012. Se utilizează tabelele dimensionale: Agenti şi Calendar:
Select Artid, Vanzarea From Vanzari, Agenti, Calendar Where
vanzari.Timpid=Calendar.Timpid And Vanzari.Agentid=Agenti.Agentid
And Nume ='Ionescu Dan' and perioada in ('14-MAR-12', '13-MAR-12');
SQL analitic 161

Operaţia roll-up dimensional (agregarea datelor) se realizează pe tabela de fapte


Vânzări, prin eliminarea unei dimensiuni/mai multor dimensiuni, iar dimensiunile
rămase sunt reprezentate de cheile lor, în clauza GROUP BY:
O dimensiune „eliminată” – dimensiunea Calendar:
SELECT artid, agentId, SUM(cantitate ) AS Total_dupa_Prod_agent
FROM vanzari GROUP BY artId, agentid order by artid; ;
Două dimensiuni „eliminate”: Agenţi şi Calendar:
SELECT artId, SUM(cantitate ) AS Total_dupa_Prod FROM vanzari GROUP BY
artId;
Toate dimensiunile „eliminate”:
SELECT SUM(cantitate ) AS Total_general FROM vanzari;
Operaţia roll-up ierarhic utilizează tabela de fapte şi una sau mai multe tabele
dimensionale. Numărul de rollup-uri ierarhice depinde de numărul de nivele
ierarhice. O ierarhie simplă cu n nivele, generează n rollup-uri. De exemplu, se va
realiza un roll-up ierarhic după ierarhia (agentid→oras). Se vor afişa vânzările după
oras, artid şi timpid:
SELECT oras, artId, TimpId, SUM (cantitate) AS Oras_Total
FROM vanzari v, agenti a WHERE v.agentId = a.agentId
GROUP BY oras, artId, Timpid order by oras;
Următoarea cerere SQL va afişa vânzările lunare pentru fiecare oraş. Se
implementează o operaţie roll-up ierarhic ce utilizează două ierarhii (două
dimensiuni):
SELECT luna, oras, artId, SUM(cantitate ) AS luna_oras_Total
FROM vanzari v, agenti a, calendar c
WHERE v.agentId = a.agentId AND v.timpid = c.timpid
GROUP BY luna, oras, artid order by luna, oras, artid;

4.1 Implementarea operaţiei roll-up utilizând


extensia ROLLUP la clauza GROUP BY
Extensia ROLLUP (x,y) va genera următoarele agregări: (), (x), (x,y). Dacă sunt
specificate n atribute, atunci se vor genera n+1 tipuri de agregări. În SGBDR Oracle
se utilizează clauza GROUP BY… ROLLUP(), iar în MySQL/SQLServer se
utilizează clauza GROUP BY… WITH ROLLUP. Următoarea cerere SQL va calcula
sum(cantitate) după (oras, an), (oras) şi ( ) (figura 4.9):
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 ROLLUP (oras, an);

Rezultatul este identic cu cel obţinut de reuniunea a trei cereri SQL:


SELECT oras, an, SUM(cantitate) AS total
FROM vanzari v, agenti a, calendar t
162 Business Intelligence. Teorie şi practică

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, null, SUM(cantitate) AS total
FROM vanzari v, agenti a, calendar t
Where v.agentid = a.agentid And v.Timpid = t.Timpid ;

Figura 4.9 Utilizarea extensiei ROLLUP

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

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
Order By 1;

În acest caz, nu se generează un total general (total după toate dimensiunile).


Clauza GROUP BY ROLLUP (X) va genera agregări după ( ) şi după (x).
Următoarea cerere SQL va genera agregări după: (an, oras) şi (an):
SELECT an, oras, SUM(cantitate) AS total FROM vanzari v, agenti a, calendar t
Where V.agentid = a.agentid And V.Timpid = T.Timpid
GROUP BY an, ROLLUP (oras) order by an;
Următoarea cerere SQL va genera agregări utilizând trei ierarhii: (an, luna,
perioada), (judet, oras, agentid) şi (categorieid, descriere). Se obţine un cub ierarhic
de date:
Select an, luna, perioada, Judet, Oras, agenti.agentid,
categorieid, descriere, sum(vanzarea) total
From vanzari, agenti, calendar, articole, judete
Where vanzari.Timpid = calendar.Timpid And vanzari.artid = articole.artid
And vanzari.agentid = agenti.agentid and agenti.judetid=judete.judetid
Group By rollup (an, luna, perioada), rollup (Judet, Oras, agenti.agentid),
rollup (Categorieid, Descriere);
Se generează un produs cartezian şi 48 (4x3x4) tipuri de agregare (tabelul 4.1).
Tabel 4.1 Agregarea datelor utilizând trei ierarhii
ROLLUP după ierarhia ROLLUP după ierarhia ROLLUP după ierarhia
dimensiunii Calendar dimensiunii Articole dimensiunii Agenţi
an, luna, perioada categorieid, descriere Judet, oras, agentid
an, luna categorie Judet, oras
an () judet
() ()

4.2 Utilizarea extensiei CUBE la clauza


GROUP BY
Extensia CUBE generează toate tipurilor posibile de agregare. Cubul poate fi un
cub complet sau un cub parţial. De exemplu, cube(x,y) va genera următoarele criterii
de grupare: ( ), (x), (x,y) şi (y), iar cube(x1, x2,…., xn) va genera 2n criterii de grupare
şi anume: ( ), (x1), (x1, x2), (x1, x2, x3), …, (x1, x2, …, xn), (x2), (x2, x3), (x2, x3,
x4), …, (xn).
164 Business Intelligence. Teorie şi practică

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;

Figura 4.10 Utilizarea extensiei CUBE


SQL analitic 165

Următoarea cerere SQL:


SELECT oras, categorieid, an, SUM(cantitate) as total
FROM vanzari v, calendar t, agenti a, articole ar
WHERE v.TimpId = t.TimpId AND v.agentId = a.agentId AND v.artId = ar.artId
GROUP BY CUBE (oras, categorieid, an);
va genera următoarele criterii de grupare/agregare:

oras, categorieid, an

oras, categorieid oras, an 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ă

Figura 4.11 Utilizarea funcţiei GROUPING()

4.3 Utilizarea expresiei GROUPING SETS


Expresia GROUPING SETS permite criterii de grupare mai selective decât
extensia CUBE. 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 grouping sets (oras, an);

este identică cu cererea SQL (tabelul 4.2):


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 All
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 ;
SQL analitic 167

Tabel 4.2 GROUPING SETS versus GROUP BY


Echivalenţa
GROUP BY GROUP BY a UNION ALL
GROUPING SETS(a,b) GROUP BY b
GROUP BY GROUP BY a UNION ALL
GROUPING SETS(a, b, c) GROUP BY b UNION ALL
GROUP BY c
GROUP BY GROUP BY a UNION ALL
GROUPING SETS(a, b, (b, c)) GROUP BY b UNION ALL
GROUP BY b, c
GROUP BY GROUP BY a, b, c
GROUPING SETS((a, b, c))
GROUP BY GROUP BY a UNION ALL
GROUPING SETS(a, (b), ()) GROUP BY b UNION ALL
GROUP BY ()
GROUP BY GROUP BY a UNION ALL
GROUPING SETS(a, ROLLUP(b, c)) GROUP BY ROLLUP(b, c)

În tabelul 4.3 se prezintă corespondenţa dintre ROLLUP şi GROUPING SETS.

Tabel 4.3 Corespondenţa dintre extensia ROLLUP şi expresia GROUPING SETS


ROLLUP GROUPING SETS
SELECT oras, an, SUM(cantitate) AS SELECT oras, an, SUM(cantitate) AS total
total FROM vanzari v, agenti a, calendar t
FROM vanzari v, agenti a, calendar t WHERE v.agentid = a.agentid
WHERE v.agentid = a.agentid AND v.Timpid = t.Timpid
AND v.Timpid = t.Timpid GROUP BY GROUPING SETS ((oras,
GROUP BY ROLLUP (oras, an); an), (oras), ());

ROLLUP(a,b), c GROUPING SETS((c), (a,c), (a,b,c))


ROLLUP (a,b) GROUPING SETS(( ), (a), (a,b))

De asemenea, se pot utiliza mai multe expresii GROUPING SETS în clauza


GROUP BY:
SELECT an, luna, judet, oras, agenti.agentid, sum(vanzarea) total
From vanzari, agenti, calendar, articole, judete
Where vanzari.Timpid = calendar.Timpid And vanzari.artid = articole.artid and
agenti.judetid=judete.judetid And vanzari.agentid = agenti.agentid
Group By grouping Sets (an, luna), grouping sets (judet, oras, agenti.agentid);

Se va genera produsul cartezian a două expresii GROUPING SETS şi anume


agregări după: (an, judet), (an, oras), (an, agentid), (luna, judet), (luna, oras), (luna,
agentid).
168 Business Intelligence. Teorie şi practică

4.4 Operaţia de pivotare


Pivotarea este utilizată în multe aplicaţii cu depozite de date, precum şi în etapa
de transformare a procesului ETL. Adesea datele returnate de o cerere analitică
trebuie afişate într-un format tabular. Cea mai simplă vizualizare a pivotării este
atunci când se selectează două dimensiuni pentru a agrega un indicator/fapt. O
dimensiune este afişată pe rânduri, iar cealaltă dimensiune pe coloane. Clauza
PIVOT a comenzii SELECT permite scrierea de cereri pivot care rotesc rândurile şi
le transformă în coloane. Datele sunt agregate în procesul de rotire/pivotare. O
funcţie de agregare este aplicată pentru fiecare valoare a atributului utilizat în
pivotare. Operaţia de pivotare execută un GROUP BY implicit, utilizând orice
atribut din tabelă. Sintaxa este următoarea [Lane, 2010]:
SELECT * FROM <subset atribute> PIVOT (functie_agregare(<atribut>)
FOR <pivot-atribut> IN (<valoare1>, <valoare2>, ..., <valoaren>) ) AS <alias>
WHERE ...
Cererile pivot utilizează un subset de atribute. Următorul exemplu utilizează o
subcerere pentru a preciza setul de atribute. Funcţia de agregare este sum() şi atributul
care se va agrega este cantitate. Atributul utilizat pentru pivotare este agentid.
Se restricţionează valorile atributului pivotat şi anume: agentid=1 şi agentid=2:
Select * From(Select Timpid, agentid, Cantitate From vanzari)
pivot (sum(cantitate) for agentid in ((1) as agent1, (2) as agent2));
Pentru a stabili subsetul de atribute se poate utiliza şi clauza WITH (figura 4.12):
With Pivot_Data As (Select Timpid, agentid, Cantitate From vanzari)
select * from Pivot_Data Pivot (SUM(cantitate) FOR agentid IN (1,2)) ;
Clauza WITH a apărut în standardul SQL-99 şi permite reutilizarea rezultatului
unei cereri într-o altă cerere complexă, dacă apare în mai multe clauze. Oracle
returnează rezultatul cererii şi-l stochează într-un spaţiu (tablespace) temporar care
poate fi accesat de utilizator. Următoarea cerere SQL afişează judeţele care
contribuie cu mai mult de 10% la total vânzări:
With Judet_Total As (Select judete.Judet, Sum(vanzarea) As Total From vanzari,
agenti, judete Where vanzari.agentid = agenti.agentid and
judete.judetid=agenti.judetid
Group By judete.Judet) Select Judet, Total
From Judet_Total Where Total > (Select Sum(Total) * 0.10 FROM judet_total);
Următoarea cerere SQL afişează vânzările anuale, pentru oraşul Iaşi:
select * from
(select an, oras, vanzarea from vanzari, agenti, calendar where
calendar.timpid=vanzari.timpid and vanzari.agentid = agenti.agentid)
pivot (sum(vanzarea) for oras in('Iasi'));
Rezultatul pivotării:
An ‘Iasi’
2013 60553.96
2012 66131.68
SQL analitic 169

Figura 4.12 Pivotarea în SGBDR Oracle

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ă

Pentru a stabili subsetul de atribute, se poate utiliza şi o tabelă virtuală. De


exemplu, se va crea o tabelă virtuală pivot_data:
create view pivot_data as select timpid, agentid, cantitate from vanzari;
iar cererea pivot utilizează această tabelă virtuală:
Select * From Pivot_Data pivot (sum (cantitate) for agentid in (1,2));
De asemenea, se pot utiliza pentru pivotare mai multe atribute. Următoarea cerere
SQL utilizează atributele oras (numai Iaşi şi Bucureşti) şi an (numai 2012) pentru
pivotare (figura 4.13):
select * from (select descriere, oras, an, cantitate from vanzari, agenti, articole,
calendar where vanzari.agentid=agenti.agentid
and vanzari.artid=articole.artid and vanzari.timpid=calendar.timpid)
pivot(sum(cantitate) for (oras, an) in (('Iasi', 2012) as cant_iasi_2012,
('Bucuresti',2012) as cant_Bucuresti_2012));

Figura 4.13 Pivotare cu două atribute

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

Figura 4.14 Pivotare care utilizează două atribute pentru agregare

4.5 Utilizarea funcţiilor analitice complexe


Oracle a introdus o serie de funcţii analitice cum ar fi: RANK(), Ntile(),
FIRST()/LAST(), RATIO_REPORT(), FIRST_VALUE()/ LAST_VALUE(),
LAG()/LEAD(), etc., care permit realizarea unor cereri BI complexe [Lane, 2010].
De asemenea, SQL analitic permite utilizatorilor să împartă setul de date rezultat din
interogare, în grupuri ordonate de tupluri numite partiţii.
Funcţia RANK() returnează poziţia unui tuplu dintr-o partiţie. Tuplurile partiţiei
sunt ordonate după un criteriu de ordonare. Toate tuplurile cu aceeaşi valoare pentru
criteriu de ordonare sunt considerate având acelaşi rang. Dacă n (> 1) tupluri au
rangul r, atunci următorul tuplu are rangul r + n + 1. Sintaxa funcţiei este: RANK ( )
OVER ([PARTITION BY atribut] ORDER BY criteriu de ordonare)
Următoarea cerere SQL va ierarhiza agenţii după cantitatea vândută (figura 4.15):

Select agentid, sum (cantitate) cantitate,


rank() over (order by sum(cantitate) desc ) as a_rank
From vanzari group by agentid order by a_rank;
172 Business Intelligence. Teorie şi practică

Ce se întâmplă dacă doi agenţi au acelaşi rang? Să presupunem că agentul cu


agentid=1 a vândut aceeaşi cantitate ca şi agentul cu agentid=29. Dacă executăm
cererea anterioară, observăm că cei doi agenţi au rangul 1, iar al treilea agent are
rangul 3 şi nu rangul 2:

Agentid cantitate a_rank


1 5588 1
29 5588 1
5 4375 3

Figura 4.15 Utilizarea funcţiei RANK()

Pentru a elimina această eroare, se utilizează funcţia DENSE_RANK()


(figura 4.16):
Select agentid, sum(cantitate) cantitate,
Dense_rank() over (order by sum(cantitate) desc ) as a_rank
From vanzari group by agentid order by a_rank;
SQL analitic 173

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;

Figura 4.16 Utilizarea funcţiei DENSE_RANK()

Ierarhizare după multiple expresii


Următoarea cerere SQL va ierarhiza agenţii după indicatorii vanzare şi cantitate.
Pentru valori identice ale indicatorului vanzare se face o ierarhizare după valorile
indicatorului cantitate:
Select nume, descriere, Sum(vanzarea) vanzarea, Sum(cantitate) cantitate,
Dense_Rank() Over (Order By Sum(vanzarea ) Desc, Sum(cantitate) Desc) As
A_Rank From vanzari, articole, agenti Where vanzari.artid=articole.artid And
vanzari.agentid=agenti.agentid Group By nume, descriere;
Utilizarea funcţiei DENSE_RANK() cu clauza PARTITION BY
Clauza PARTITION BY împarte setul de date în partiţii. Următoarea cerere SQL
va afişa pentru fiecare agent, produsele vândute, ierarhizate după cantitatea vândută
(figura 4.17):
Select nume, descriere, Sum(Cantitate) Cantitate,
Dense_Rank() Over (Partition By nume Order By Sum(Cantitate ) Desc) As
A_Rank From vanzari, agenti, articole Where vanzari.agentid=agenti.agentid and
vanzari.artid=articole.artid Group By nume, descriere;
Utilizarea de multiple funcţii DENSE_RANK() cu diferite partiţii
Următoarea cerere SQL va afişa pentru fiecare categorie de produse (partition by
categorieid), tranzacţiile efectuate, ierarhizate după cantitatea vândută.
174 Business Intelligence. Teorie şi practică

De asemenea, va afişa pentru fiecare judeţ (partition by judet), tranzacţiile efectuate,


ierarhizate după cantitatea vândută (figura 4.18):
Select categorieid, judet, vanzid, Sum (Cantitate),
Dense_Rank () Over (Partition By categorieid Order By Sum (Cantitate) Desc) As
Rank_Within_categorie, Dense_Rank () Over (Partition By judet Order By Sum
(Cantitate) Desc) As Rank_Within_judet From vanzari, articole, agenti, judete
Where vanzari.agentid= agenti.agentid and vanzari.artid=articole.artid and
agenti.judetid=judete.judetid Group By categorieid, judet, vanzid;

Figura 4.17 Utilizarea clauzei PARTITION BY

Funcţia DENSE_RANK() poate fi utilizată şi cu extensiile ROLLUP/CUBE şi


expresia GROUPING SETS.

Funcţia Ntile(n) împarte tuplurile din fiecare partiţie, în n grupuri (n este o


constantă). Fiecare grup va avea un număr egal de tupluri sau mai mic cu 1.
În următoarea cerere SQL:
Select fourtile, sum(cantitate) as total
From (select cantitate, ntile(4) over (order by cantitate desc) as fourtile from
vanzari) Group by fourtile order by total;
funcţia ntile(4) ordonează tuplurile după valorile atributului cantitate, în ordine
descrescătoare, apoi formează 4 grupuri de câte 321, 321, 320 şi 320 tupluri.
SQL analitic 175

Pentru fiecare grup, calculează sum(cantitate). Tabela Vanzari are 1282 tupluri
(figura 4.19).

Figura 4.18 Utilizarea funcţiei DENSE_RANK() cu multiple partiţii

Figura 4.19 Utilizarea funcţiei Ntile()


176 Business Intelligence. Teorie şi practică

Utilizarea partiţiilor în agregarea datelor


Setul rezultat al unei cereri se poate împărţi în partiţii. Partiţiile sunt create după
ce se execută toate joncţiunile şi clauzele WHERE, GROUP BY şi HAVING, dar
înainte de clauza ORDER BY (figura 4.20). O partiţie poate avea un număr variabil
de tupluri. Tuplul curent este punctul de referinţă pentru stabilirea numărului de
tupluri ale partiţiei [Lane, 2010].

Joncţiuni, WHERE, GROUP


BY, HAVING

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

Figura 4.21 Utilizarea partiţiilor


Media dinamică (Moving average)
Următoarea cerere SQL afişează pentru produsul cu artid= 00967 400 156,
cantitatea vândută în fiecare lună, precum şi cantitatea medie vândută pe trei luni
(luna curentă şi două luni anterioare):
SELECT a.artid, t.luna, SUM (cantitate) AS cant_lunacurenta,
AVG(SUM(cantitate))Over (Partition By A.Artid Order By T.Luna Rows 2
Preceding) As Media_3luni From Vanzari V, Calendar T, Articole A Where
V.Timpid=T.Timpid And V.Artid=A.Artid and A.Artid ='00967 400 156' and
An=2012 GROUP BY a.artid, t.luna ORDER BY a.artid, t.luna;
Rezultatul:
Artid luna cant_lunacurenta Media_3luni
00967 400 156 1 45 45
00967 400 156 3 40 42.5
00967 400 156 6 255 113.33
178 Business Intelligence. Teorie şi practică

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;

Figura 4.22 Agenţii cu cele mai mari vânzări, în fiecare oraş

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;

Figura 4.25 Utilizarea funcţiei RATIO_TO_REPORT ()


Funcţia FIRST_VALUE( )/funcţia LAST_VALUE () specifică primul/ultimul tuplu
al unei partiţii. Următoarea cerere SQL va afişa pentru fiecare lună a anului 2012,
volumul vânzărilor, vânzările din prima lună a anului 2012, precum şi diferenţa
procentuală (figura 4.26):
Select luna, vanzare_luna, vanzare_primaluna, round((vanzare_luna-
vanzare_primaluna)/ vanzare_primaluna, 2)*100 ||'%' as modificare_procentuala
from (Select Luna, Sum(vanzarea) As vanzare_Luna,
SQL analitic 181

FIRST_VALUE(SUM(vanzarea)) OVER (Order By Luna) As vanzare_primaluna


From vanzari, calendar Where vanzari.timpid=calendar.timpid and an=2012
Group By Luna Order By Luna);

Figura 4.26 Utilizarea funcţiei FIRST_VALUE()

Un grup de atribute poate fi tratat ca o unitate (composite column) în timpul


agregării. În tabelul 4.4 se prezintă modul cum se utilizează unui grup de atribute
(b,c) cu extensia ROLLUP/extensia CUBE.

Tabelul 4.4 Utilizarea unui grup de atribute în extensia ROLLUP/CUBE


similar cu:
GROUP BY ROLLUP(a, (b, c)) GROUP BY a, b, c
este identic cu UNION ALL
GROUP BY ROLLUP (a, z). GROUP BY a
UNION ALL
GROUP BY ()
GROUP BY ROLLUP(a, b, c) GROUP BY a, b, c
UNION ALL
GROUP BY a, b
UNION ALL
GROUP BY a
UNION ALL
GROUP BY ()
182 Business Intelligence. Teorie şi practică

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.).

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