Sunteți pe pagina 1din 126

Baze de date

Introducere in SQL Server 2008

Editura
2008
 2008 Editura InfoMega

Toate drepturile rezervate. Reproducerea materialelor din


această lucrare se poate face doar cu acordul scris al
editurii. Autorii îşi asumă răspunderea asupra materialelor
publicate.

Editură acreditată de C.N.C.S.I.S.

 e-mail: office@infomega.ro
 http://www.infomega.ro

CIP Nr. 23548/11.12.2008

Descrierea CIP a Bibliotecii Naţionale

STANCIU, ANDREI
Baze de date : introducere în SQL, Server 2008 / Stanciu Andrei (coord.),
Mihai Florin, Năstase Bucureşti : InfoMega , 2008
Bibliogr.
ISBN 978-973-7853-36-3

I. Mihai, Florin
II. Năstase, Pavel

004.64
Cuprins
CAPITOLUL 1
CONCEPTE ŞI NOŢIUNI DE BAZĂ PRIVIND BAZELE DE DATE
RELAŢIONALE……………………………………………………………… 6
1.1. BAZE DE DATE ŞI SISTEME DE GESTIUNE A BAZELOR DE DATE……… 6
1.2.CONCEPTE DE BAZĂ ALE MODELULUI RELAŢIONAL…………….. 8
1.2.1.Cheia primară……………..……………..……………..………… 9
1.2.2.Cheia externă…………….…………………...………………….10
1.2.3. Restricţii de integritate………………………………………......11
1.2.4.Relaţiile dintre tabele……….……………..……………..………12
1.2.5.Anomalii de actualizare si anomalii de stocare………….......…..12
1.3. NORMALIZAREA BAZELOR DE DATE……………..……………...… 14
1.3.1.Dependenţa funcţională ……………..……………..…………... 15
1.3.2. Dependenţa funcţională completă (totală)……………………... 17
1.3.3.Dependenţa funcţională tranzitivă)……………………............... 18
1.3.4.Formele normale)…………………….......................................... 19
1.3.5.Etapele normalizării)……………………..……………………... 23
1.4. EXERCITII PROPUSE SI REZOLVATE)……………………………. 34

CAPITOLUL 2
SGBD SQL SERVER……………..……………...………………..………… 42
2.1.ARHITECTURA CLIENT-SERVER……………..……………...………. 42
2.2.PREZENTARE SQL SERVER 2008……………..……………...……….. 45
2.3.LIMBAJUL TRANSACT SQL
– PRINCIPALELE TIPURI DE INSTRUCŢIUNI……………..…………. 49
2.4. EXERCIŢII PROPUSE ŞI REZOLVATE……………..……………...… 55

CAPITOLUL 3
DESCRIEREA DATELOR…………..……………...………………..…… 60
3.1.CREAREA TABELELOR ……………..……………...………………….. 61
3.2.INDEXAREA TABELELOR …………..……………...…………………. 65
3.3.IMPLEMENTAREA RESTRICŢIILOR…………..………………………. 67
3.3.1. Restricţii la nivelul tabelelor (check constraints) ………………68
3.3.2. Integritatea referenţială ………………………………………... 68
3.3.3. Proceduri de tip trigger (Declanşatori) ………………………... 69
CAPITOLUL 4
INTEROGAREA DATELOR ………………………………………............. 76
4.1.FUNCŢII PREDEFINITE SQL SERVER………………………………… 76
4.2.OBIECTE DE TIP VIEW…………………………………………………. 94
4.3.PROCEDURI STOCATE………………………………………………… 100
4.3.1.Proceduri de sistem……………………………………………. 101
4.3.2.Declararea variabilelor………………………………………… 102
4.3.3.Parametrizarea procedurilor stocate…………………………… 103
4.3.4.Structuri de control al fluxului………………………………… 105
4.4.FUNCŢII DEFINITE DE UTILIZATOR…………………………………109
4.5.UTILIZAREA CURSOARELOR………………………………………... 117
4.6.EXERCIŢII PROPUSE ŞI REZOLVATE……………………………….. 122

CAPITOLUL 5
FACILITĂŢI PENTRU RAPORTAREA
SI ANALIZA DATELOR…………………………………………………... 128
5.1.ACCESS DATA PROJECTS: REALIZAREA APLICAŢIILOR
CLIENT UTILIZÂND MS OFFICE 2007 …..…………………...……… 128
5.2.REPORTING SERVICES: REALIZAREA RAPOARTELOR………….. 131
5.3.FACILITĂŢI SQL SERVER PENTRU DATAWAREHOUSE…………. 140
5.3.1.Arhitectura unui depozit de date……………………….……… 141
5.3.2.OLAP (On line Analytical Processing) …………………..…... 142
5.3.3.Modelarea multidimensională a datelor……………….……... 143

CAPITOLUL 6
EXPLOATAREA DATELOR IN FORMAT XML………………………. 160
6.1. LIMBAJUL XML - CONCEPTE DE BAZA……………………………. 160
6.2. UTILIZAREA DATELOR ÎN FORMAT XML IN SQL SERVER…….. 167
6.3.CONVERSIA DATELOR XML ÎN STRUCTURI
RELAŢIONALE DE DATE….................................................................... 170
6 Baze de date – Introducere in SQL Server 2008

CAPITOLUL 1
CONCEPTE ŞI NOŢIUNI DE BAZĂ PRIVIND
BAZELE DE DATE RELAŢIONALE

1.1. Baze de date şi sisteme de gestiune a bazelor de date


Domeniul bazelor de date reprezintă obiectul de studiu pentru un număr
impresionant de lucrări de specialitate, iar încercarea de a defini în câteva rânduri
conceptul de bază de date a constituit o provocare pentru toţi autorii consacraţi.
Fără a încerca să reinventăm roata şi fără a stărui prea mult asupra unor aspecte
teoretice, ne vom limita la a expune în aceste paragrafe o serie de concepte
fundamentale, devenite deja clasice.
O definiţie foarte succintă pentru bazele de date a fost formulată de C.J Date: O
bază de date este o colecţie de date persistente ce sunt folosite de sistemele de
aplicaţii informatice ale unei organizaţii1.
Cercetătorii francezi Claude Delobel şi Michel Adiba au prezentat în lucrarea “
Bases de données et systèmes relationnels“2 o definiţie mai elaborată a
conceptului, încercând să surprindă atât aspectele esenţiale privind organizarea
datelor cât şi necesităţile informaţionale ce trebuie satisfăcute: “Baza de date este
un ansamblu structurat de date corelate logic, înregistrate pe suporturi accesibile
calculatorului pentru a satisface cerinţele unuia sau mai multor utilizatori
simultan într-un timp oportun”.
O bază de date conţine atât datele cât şi descrierea acestora, deci asigură
independenţa dintre date şi programele informatice care le exploatează. Bazele de
date asigură coerenţa datelor şi pun la dispoziţia utilizatorilor formalisme de
descriere a datelor.
Un sistem de gestiune a bazelor de date (SGBD) reprezintă o interfaţă între
utilizatori şi baza de date ce trebuie să permită crearea, actualizarea, consultarea şi
administrarea drepturilor de acces la date.
Alegerea unei soluţii pentru managementul datelor trebuie să se bazeze pe
următoarele considerente:
 volumul de date ce urmează a fi stocat şi procesat;

 gradul de expunere la riscuri ;


 tipul de aplicaţii care vor exploata datele (aplicaţii web, aplicaţii de tip
business intelligence, sisteme informatice clasice, etc);

1
C.J.Date, Baze de date Editura E+, 2005
2
Claude Delobel et Michel Adiba, Bases de données et systèmes relationnels, Dunod, Paris, 1992
Capitolul I Concepte şi noţiuni de bază privind 7
bazele de date relaţionale
 previziunile de extindere pe termen mediu în privinţa volumului de date din
cadrul organizaţiei;
 nu în ultimul rând costurile de licenţiere şi politica de licenţiere a
producătorului;
În prezent, pe piaţă sistemelor de gestiune pentru baze de date relaţionale există o
ofertă bogată şi, ca în orice domeniu, se poate vorbi despre câţiva lideri. Tendinţa
producătorilor, pe lângă extinderea permanentă a capacităţilor de stocare şi
procesare, este de a incorpora în cadrul sistemelor instrumente şi facilităţi
specifice celor mai recente tehnologii (datawarehouse, data mining, facilităţi
pentru multimedia şi tipuri speciale de date).
Sistemele destinate gestiunii bazelor de date relaţionale pot fi clasificate în funcţie
de volumul de date pentru care sunt proiectate şi de facilităţile oferite în
următoarele două categorii:
SGBD pentru volum mediu de date (desktop database)
 Microsoft Access  Costuri reduse de licenţiere
 FoxPro  Cerinţe mici în ce priveşte resursele hardware
 Paradox  Interfaţa accesibilă pentru utilizatorii
 FileMaker Pro neexperimentaţi
 Soluţii usor de implementat pentru publicarea
 Lotus Aproach datelor pe web

SGBD pentru volum mare de date (server database)


 Microsoft SQL Server
 Flexibilitate
 Oracle  Scalabilitate
 IBM DB2  Performanţe crescute in procesarea datelor
 Sybase  Facilităţi complexe de administrare
 Instrumente specifice pentru analiza
datelor (implementarea tehnologiilor
Datawarehouse şi Dataminining)
 Securitate sporită a datelor
8 Baze de date – Introducere in SQL Server 2008

Conform unui studiu publicat de IDG3 (International Data Group) la sfârşitul


anului 2007, primii cinci competitori de pe piaţa sistemelor de gestiune pentru
baze de date relaţionale (RDBMS4) după vânzări au fost:

Compania producatoare Milioane USD


ORACLE 8,34
IBM 4,95
Microsoft 3,47
Sybase 0,65
Teradata 0,63
Acelaşi studiu menţionează că, faţă de anul precedent, piaţa a crescut cu circa
12,6%.
Acest studiu este relevant prin prisma conturării unei ierarhii în cadrul
principalilor competitori, referindu-ne strict la încasărie din vânzări (nu la
numărul de licenţe distribuite).
Trebuie specificat că politica de preţuri în domeniul licenţierii sistemelor de
gestiune a BD tinde să fie din ce în ce mai mult influenţată de evoluţia pe piaţă a
unor competitori ce au adoptat politica open-source. Dintre aceştia putem
menţiona: MySQL, Postgre SQL sau SQLite

1.2. Concepte de bază ale modelului relaţional


Aşa cum este cunoscut, bazele modelului relaţional au fost conturate de către
matematicianul firmei IBM, E. F. Codd, în două articole de referinţă publicate în
1969 şi 1970. Modelul relaţional propune o organizare tabelară a datelor stocate
într-o bază de date, independentă de arhitectura hardware şi software a sistemelor
şi un set de operatori pentru extragerea datelor (Codd, 1969 & Codd, 1970). Aşa
cum remarcă şi Fotache (2005), deşi teoria relaţională propune termenul de
“relaţie”, în practică s-a consacrat termenul de “tabelă”.
Baza de date relaţională este formată practic dintr-un ansamblu de tabele (relaţii)
aflate în legătură. Tabela are un nume unic în cadrul bazei de date şi este practic o
structură bidimensională formată din linii şi coloane. Liniile tabelului se mai
numesc tupluri sau înregistrări. Coloanele se mai numesc atribute. Atributele
sunt caracterizate printr-un nume unic în cadrul tabelei şi un domeniu de valori pe
care le poate avea atributul în cadrul tuplurilor tabelei. Fiecare linie/tuplu conţine
câte o valoare atomică din fiecare atribut/coloană din tabelă.

3
Compania americană IDG a fost fondată în 1964 şi, în prezent, dispune de filialele în întreaga
lume, ce desfăşoară studii pe piaţă teghnologiilor informatice şi comunicaţiilor.
4
RDBMS – Relational Database Management Systems
Capitolul I Concepte şi noţiuni de bază privind 9
bazele de date relaţionale
În figura următoare este prezentată structura generală a unei tabele, din punct de
vedere al conceptelor asociate.

1.2.1. Cheia primară

Conform teoriei relaţionale, o tabelă (relaţie) nu poate să conţintă două sau mai
multe rânduri (tupluri) identice. Fiecare linie a unei tabele trebuie să poată fi
identificată într-o manieră clară, prin intermediul unui singur atribut sau a unui
grup de atribute, ce aparţin tabelei. În primul articol publicat de Codd (1969), se
folosea termenul generic “cheie” pentru a sugera acest lucru. Abia în cel de-al
doilea articol publicat de acesta (Codd, 1970), s-a folosit pentru prima dată
termenul “cheie primară”, termen care astăzi ne este foarte familiar.
Pe lângă caracteristica de unicitate amintită, o cheie primară mai trebuie să
respecte două restricţii:
 În cazul cheilor compuse, formate din mai multe atribute, nu se poate elimina
un atribut parte din cheie, fără a distruge caracteristica de unicitate a tuplurilor
unei tabele;
 Cheia primară nu admite sub nici o formă valori nule, iar în cazul cheilor
compuse, nici un atribut parte din cheie nu poate avea valori nule. Conform
uneia din regulile pe care trebuie să le respecte bazele de date relaţionale,
formulate de Codd (1985), valoarea nul este diferită de valoarea “zero” din
punct de vedere numeric şi de asemenea diferită de un şir de caractere de
lungime zero. Practic, valoarea nul este independentă de tipul de date şi se
foloseşte atunci când informaţiile care ar trebui stocate lipsesc (nu se cunosc).
O alta definiţie, de dată mai recentă, într-o mai detaliată este cea a lui Mike
Chapple (Chapple M., 2006). Cheia primară a unui tabel relaţional identifică în
mod unic fiecare înregistrare din tabel. Aceasta poate sa fie un atribut sau un grup
de atribute al acelui tabel ce are proprietatea de unicitate sau poate să fie un
atribut cu valorile generate de către sistemul de gestiune al bazelor de date utilizat
10 Baze de date – Introducere in SQL Server 2008

(cum este spre exemplu Autonumber pentru Microsoft ACCESS sau Guid în
Microsoft SQL Server).
Cheia primară se consideră a fi o cheie naturală, dacă atributul sau grupul de
atribute care formează cheia, face parte din mulţimea atributelor ce caracterizează
entitatea identificată de cheia primară. Valorile cheii naturale exprimă legăturile
din lumea reală care există între aceasta şi entitatea identificată. Dacă cheia
primară este reprezentată de un atribut cu valori arbitrare, unice, dar care nu
exprimă nicio legătură din lumea reală cu entitatea identificată, se consideră o
cheie surogat.
O tabelă poate avea mai multe atribute sau mai multe grupuri de atribute, care să
îndeplinească simultan condiţiile pentru a fi cheie primară a tabelei. Acestea se
numesc chei candidate, însă doar cea care este aleasă ca identificator al tabelei
este denumită cheie primară.
În notaţia utilizată în paragrafele următoare, cheia primară va fi scrisă cu caractere
îngroşate.

1.2.2. Cheia externă

Termenul cheie externă (foreign key), folosit de Codd în articolul din 1970, se
referă la atributele sau grupurile de atribute care pun în legătură rândurile unei
tabele cu rândurile altei tabele. În cazuri excepţionale, pot exista chei externe care
pun în legătură rândurile unui tabel cu înregistrări ale aceluiaşi tabel, privite dintr-
o anumită perspectivă. Altfel spus cheia externă este un atribut sau un grup de
atribute ale unui tabel definite sub formă de cheie primară în alt tabel (sau chiar în
acelaşi tabel) şi serveşte pentru a defini legăturile dintre tabele.
Pe baza noţiunii de cheie externă, s-a dezvoltat conceptul de restricţie de
integritate referenţială. Între două tabele există o restricţie de integritate
referenţială atunci când, dacă valorile cheii externe nu au valori nule, acestea
trebuie să fie neapărat dintre valorile cheii primare cu care se află în legătură.
Conceptul de integritate referenţială, este unul din conceptele fundamentale, care
asigură coerenţa datelor stocate în baza de date.
Capitolul I Concepte şi noţiuni de bază privind 11
bazele de date relaţionale

În figura anterioară se observă modul de acţiune al integrităţii referenţiale.


Atributul CUIClient din tabela Contract nu poate lua valori decât dintre valorile
atributului CUIClient din tabela Client.
În notaţia utilizată în paragrafele următoare, cheia externă va fi subliniată cu linie
întreruptă.
1.2.3. Restricții de integritate

Datele stocate într-o bază de date, trebuie să fie coerente, să corespundă realităţii.
În acest sens, restricţiile de integritate definesc setul de constrângeri pe care
trebuie să îl respecte datele, în aşa fel încât să fie considerate coerente.
Succint, restricţiile de integritate ale unei baze de date relaţionale, se pot clasifica astfel:
 Restricţii de integritate specifice modelului relaţional. Acestea sunt:

 Restricţia cheii primare – conform acestei restricţii, atributul (sau grupul de


atribute) ce îndeplineşte rolul de cheie primară, trebuie să aibă valori unice
şi nenule. Aceste aspecte sunt descrise pe larg în paragraful 1.2.1. despre
cheia primară.
 Restricţia de integritate referenţială – conform acestei restricţii, valoarea
unei chei externe trebuie să fie dintre valorile cheii primare corespondente
sau poate fi nulă. Aceste aspecte sunt descrise pe larg în paragraful 1.2.2.
despre cheia externă.
 Restricţii de integritate definite de utilizator. Sunt restricţii care se aplică
asupra atributelor sub forma unor reguli de validare. Aceste restricţii rezultă
din analiza sistemului informaţional al organizaţiei pentru care se proiectează
baza de date. Regulile de validare pot fi definite folosind atribute dintr-un
singur tabel sau din tabele diferite (de exemplu TarifNegociat>0, Cantitate>0
and Cantitate<1000) ale bazei de date.
12 Baze de date – Introducere in SQL Server 2008

1.2.4. Relațiile dintre tabele

Relaţiile care pot exista între două tabele, pot fi de trei tipuri:
 1-1 (unu la unu) – în acest tip de relaţie unei valori a cheii primare dintr-
un tabel îî corespunde cel mult o valoare din câmpul cheie externă al
tabelului cu care se află în legătură.
 1-n (unu la mai mulţi) – acest tip de relaţie este cel mai întâlnit. Unei
valori a cheii primare dintr-un tabel îi corespund mai multe valori a cheii
externe din tabelul cu care se află în legătură.
 m-n (mai mulţi la mai mulţi) – acest tip de relație nu poate fi exprimată
ca o relaţie simplă între două tabele, ci doar prin intermediul unei tabele de
legătură, care va “împărţi” relaţia m-n în două legături: 1-n și n-1.

1.2.5. Anomalii de actualizare și anomalii de stocare

Orice bază date relaţională, pentru a nu deveni un coşmar atât pentru cei care o
utilizează, cât şi pentru cei care i-au proiectat structura, trebuie structurată corect
în tabele, iar atributele corect repartizate în tabele. Pentru a fi mai expliciţi,
considerăm următorul tabel (într-o variantă simplificată) referitor la facturile
emise de către o firmă:

La o analiză atentă a tabelului, se constată că în cadrul acestuia se manifestă mai


multe tipuri de anomalii, numite şi anomalii de actualizare:
 Anomalii la adăugare – din tabelul prezentat în figura anterioară se observă
că adăugarea unui client nou, este condiţionată de existenţa unei facturi către
acesta. Dacă s-ar dori adăugarea unui client potenţial (de exemplu, pentru a-i
trimite o ofertă), căruia nu i s-a emis încă nici o factură, acest lucru nu se
poate. Aceeaşi anomalie se manifestă dacă se doreşte adăugarea unui produs
nou. Acest lucru se poate face doar dacă produsul se şi facturează. Dacă s-ar
adăuga doar datele despre client sau despre un produs, atunci celelalte atribute
ale tabelului ar avea valori nule, însă aşa cum s-a amintit anterior, pentru cheia
primară sau pentru componente din cadrul acesteia, nu sunt permise valorile
nule.
Capitolul I Concepte şi noţiuni de bază privind 13
bazele de date relaţionale
 Anomalii la modificare – modificarea anumitor elemente, în tabelul prezentat
anterior, poate fi constitui o problemă serioasă. Spre exemplu, dacă se schimbă
denumirea unui produs, această operaţie ar trebui realizată, în toate apariţiile
produsului în cadrul tabelului. Dacă ar fi vorba de mii de facturi, în care apare
acel produs, ar trebui modificată denumirea în toate înregistrările, lucru care ar
putea fi dificil în lipsa unor instrumente care să faciliteze această operaţie.
Practic anomalia la modificare se manifestă, ori de câte ori intenţionăm să
modificăm o anumită valoare a unui atribut şi suntem nevoiţi să modificăm
mai multe linii ale tabelului.
 Anomalii de ştergere – şi operaţiile de ştergere se realizează cu probleme în
tabelul prezentat în figura anterioară. Dacă s-ar dori ştergerea facturii cu
numărul 3, s-ar pierde şi informaţiile referitoare despre clientul “SC Mimpex
SRL”, aceasta fiind singura apariţie a acestuia în cadrul tabelului. Cu alte
cuvinte, anomalia la ştergere se manifestă ori de câte ori, atunci când
intenţionăm ştergerea unei linii dintr-un tabel se şterg şi alte informaţii în afara
celor care trebuiau şterse.
În afară de anomaliile de actualizare, într-un tabel mai pot fi întâlnite şi anomalii
de stocare a datelor:
 Redundanţe – în afara celor trei categorii de anomalii descrise anterior, se
constată că anumite informaţii se repetă în cadrul tabelului. De exemplu,
denumirea clientului, trebuie completată pentru fiecare produs, din fiecare
factură. Dacă am avea o factură cu 1000 produse, denumirea clientului ar
trebui completată de 1000 ori. La fel se întâmplă cu denumirea produsului. Ori
de câte ori se facturează un produs, chiar dacă este acelaşi, trebuie completată
de fiecare dată denumirea acestuia. Aceste repetări ale aceleiaşi informaţii, în
cadrul aceluiaşi tabel pot genera multe probleme:
 Risipă de spaţiu de stocare – chiar dacă problema spaţiului de stocare ar fi
rezolvată prin achiziţionarea unor echipamente de stocare de mare
capacitate, o problemă tot rămâne: prelucrările efectuate pe o tabelă de
dimensiuni foarte mari, poate fi mult mai lentă (în funcţie şi de SGBD-ul
utilizat), decât dacă tabela ar avea dimensiuni mai mici.
 Producerea unor erori în date şi/sau risipă de timp – repetarea aceleiaşi
informaţii în mai multe rânduri ale unei tabele poate pune probleme
serioase şi operatorului care introduce date. În cazul în care nu sunt puse la
dispoziţie prin intermediul interfeţei cu utilizatorul, facilităţi pentru cazurile
de repetare a aceleaşi informaţii în cadrul mai multor rânduri, operatorul ar
trebui să introducă/modifice în tabelă, fiecare apariţie a informaţiei. Spre
exemplu, dacă ar introduce o factură cu 100 produse, ar trebui să introducă
DataFacturării, CUIClient şi DenumireClient de 100 ori. Nu în ultimul
rând, trebuie spus că în cazul în care greşeşte o singură dată data facturii (în
14 Baze de date – Introducere in SQL Server 2008

cele 100 rânduri este chiar probabil să se întâmple acest lucru), acel rând va
figura ca şi cum ar fi facturat în altă zi decât cea a facturării. O eventuală
statistică a facturărilor pe zile, va fi eronată.
 Atribute calculate (derivate din altele) – orice atribut care se stochează şi
care este rezultatul unor calcule aplicate unor atribute care sunt şi ele stocate în
cadrul tabelului. În tabelul luat ca exemplu în figura anterioară se observă că
atributul ValoareFacturata este obţinut prin înmulţirea atributelor
CantitateFacturata şi PretFacturare. Această situaţie poate să genereze multe
situaţii neplăcute:
 Risipă de spaţiu – cu aceleaşi efecte care s-au amintit anterior.
 Risipă de timp şi/sau erori în date – atât la inserare, cât şi la modificare.
Mai mult, dacă se modifică valorile atributelor din care se obţine atributul
derivat, trebuie modificată şi valoarea acestuia.
Având în vedere toate tipurile de anomalii de actualizare şi cele de stocare a
datelor, este clar că pentru a avea o bază de date coerentă, care să nu manifeste
erorile descrise mai sus, se impun:
 definirea corectă a atributelor;
 structurarea coerentă a acestora în tabele;
 stabilirea corectă legăturilor între tabele;
 definirea corectă a unor restricţii de integritate asociate.
Acest proces poate fi, în funcţie de context destul de dificil şi subiectiv. Toate
aceste demersuri nu se fac din mers sau după ureche, ci în urma unui proces
complex de analiză a atributelor şi a relaţiilor care există între ele în realitatea care
se modelează, proces care serveşte proiectării unei baze de date corecte.
Există mai multe metode de proiectare a unei baze de date relaţionale, în lucrarea
de faţă autorii nu îşi propun să trateze ăn detaliu teoria proiectării bazelor de date
relaţionale, ci doar o prezentare succintă a părţilor esenţiale din metoda
normalizării.

1.3. Normalizarea bazelor de date


Teoria normalizării presupune parcurgerea riguroasă a unor paşi care să permită
obţinerea unei baze de date, în care să nu se manifeste anomalii de actualizare sau
anomalii de stocare. În acest sens, E. F. Codd a enunţat trei reguli, numite forme
normale şi notate 1NF, 2NF şi 3NF care ar trebui să fie respectate de orice bază
de date relaţională pentru a fi considerată corectă. Ulterior acestor enunţuri, au
apărut 4NF şi 5NF formulate de Ronald Fagin şi BNCF formulată de Boyce şi
Codd. BNCF reprezintă o variantă reformulată mai riguros a 3FN. Aşa cum s-a
menţionat lucrarea de faţă nu urmăreşte aprofundarea detaliată a acestor elemente,
Capitolul I Concepte şi noţiuni de bază privind 15
bazele de date relaţionale
motiv pentru care expunerea va fi limitată doar la primele trei forme normale
enunţate de Codd, considerate a fi suficiente în multe lucrări de specialitate,
pentru proiectarea unei baze de date corecte.
Pentru a înţelege procesul de normalizare şi formele normale care stau la baza
acestuia, trebuie înţeleasă noţiunea de dependenţă funcţională dintre atribute.
1.3.1. Dependenţa funcţională

Determinarea corectă a dependenţelor care există între atribute, reprezintă în final


fundamentul obţinerii unei baze de date corecte, în care nu se manifestă anomalii
de actualizare sau de stocare a datelor.
Spunem că între două atribute A şi B ale aceleiaşi tabele, există dependenţă
funcţională, dacă o singură valoare a atributului A determină o singură valoare din
atributul B. Cu alte cuvinte o singură apariţie a unei valori din atributul A poate fi
asociată unei singure apariţii a unei valori din atributul B. Dependenţa funcţională
dintre cele două atribute se simbolizează cu → , adică A → B (A determină
funcţional pe B). Atributul A se mai numeşte determinant, iar atributul B
determinat. Determinarea corectă a dependenţelor funcţionale se bazează în
totalitate pe cunoaşterea legăturilor/restricţiilor care există în realitate între
valorile atributelor.
Exemple:
Considerăm tabela Persoane:

CNP Nume Prenume


1720602122345 Mihai Ion
2730602434344 Mihai Daniela
1720404343454 Stanciu Ion
2740404323432 Stanciu Daniela

Se observă că:
CNP → Nume
O singură valoare din CNP poate fi asociată cu o singură valoare din Nume.
CNP → Prenume
O singură valoare din CNP poate fi asociată cu o singură valoare din Prenume.
Nume CNP
O singură valoare din Nume nu poate fi asociată cu o singură valoare din CNP.
16 Baze de date – Introducere in SQL Server 2008

Prenume CNP
O singură valoare din Prenume nu poate fi asociată cu o singură valoare din CNP.
Nume Prenume
O singură valoare din Nume nu poate fi asociată cu o singură valoare din
Prenume.
Prenume Nume
O singură valoare din Prenume nu poate fi asociată cu o singură valoare din
Nume.
Dependenţele funcţionale pot exista şi în cazul grupurilor de atribute. Este posibil
ca atât determinantul cât şi determinatul să fie formate din grupuri de atribute. În
această situaţie, putem generaliza că dacă X este un atribut sau un grup de
atribute şi Y este la rândul lui un atribut sau un grup de atribute, X determină
funcţional pe Y (X→Y), dacă o singură valoare (combinaţie de valori) din X poate
fi asociată cu o singură valoare (combinaţie de valori) din Y.
Exemple:
CNP→(Nume, Prenume)
O singură valoare din CNP poate fi asociată cu o singură combinaţie de valori din
atributele Nume şi Prenume.
Fie tabela Facturi:
SerieFactura NrFactura DataFactura CUIClient DenumireClient
AA 1111 1/1/2008 RO100 XYZ SA
AA 2222 1/1/2008 RO200 ABC SRL
BB 1111 1/1/2008 RO100 XYZ SA
BB 2222 2/2/2008 RO200 ABC SRL

Câteva dependenţe funcţionale ce pot fi identificate pe tabela Facturi:


(SerieFactura, NrFactura) → DataFactura
O singură combinaţie de valori din atributele SerieFactura şi NrFactura poate fi
asociată cu o singură valoare din atributul DataFactura.
(SerieFactura, NrFactura) → CUIClient
O singură combinaţie de valori din atributele SerieFactura şi NrFactura poate fi
asociată cu o singură valoare din atributul CUIClient.
(SerieFactura, NrFactura) → DenumireClient
O singură combinaţie de valori din atributele SerieFactura şi NrFactura poate fi
asociată cu o singură valoare din atributul DenumireClient.
Capitolul I Concepte şi noţiuni de bază privind 17
bazele de date relaţionale
DataFactura (SerieFactura, NrFactura)
O singură valoare din atributul DataFactura nu poate fi asociată cu o singură
combinaţie de valori din atributele SerieFactura şi NrFactura, deoarece în aceeaşi
zi pot fi emise mai multe facturi, fiecare având altă serie şi/sau număr.
1.3.2. Dependenţa funcţională completă (totală)

În cazul în care determinantul unei dependenţe funcţionale este format dintr-un


grup de atribute, se poate spune că dependenţa funcţională dintre un grup de
atribute X şi un atribut sau grup de atribute Y (X→Y) este considerată dependenţă
funcţională completă (totală), dacă o singură combinaţie de valori din X
determină o singură valoare sau combinaţie de valori din Y şi suplimentar, nici un
atribut A care face parte din grupul X nu determină funcţional pe Y (A X,
A Y).
Notă. În toate cazurile în care determinantul este format dintr-un singur atribut,
dependenţa funcţională este şi completă (totală).
Exemple:
Considerăm tabela ProduseFacturate, unde o factură nu poate să conţină
duplicate din acelaşi produs:
Denumire
SerieFactura NrFactura CodProdus Cantitate Pret
Produs
AA 1111 1 Cuie 10 11
AA 2222 2 Tabla 20 12
BB 1111 1 Cuie 10 11
BB 2222 2 Tabla 30 20

Se observă dependenţele funcţionale:


(SerieFactura, NrFactura, CodProdus)→DenumireProdus
Unei singure combinaţii de valori din grupul (SerieFactura, NrFactura,
CodProdus) îi poate fi asociată o singură valoare din atributul DenumireProdus.
(SerieFactura, NrFactura, CodProdus)→Cantitate
Unei singure combinaţii de valori din grupul (SerieFactura, NrFactura,
CodProdus) îi poate fi asociată o singură valoare din atributul Cantitate (sau altfel
spus un produs poate fi facturat o singură dată pe aceeaşi factură, într-o singură
cantitate).
(SerieFactura, NrFactura, CodProdus)→Pret
Unei singure combinaţii de valori din grupul (SerieFactura, NrFactura,
CodProdus) îi poate fi asociată o singură valoare din atributul Pret.
18 Baze de date – Introducere in SQL Server 2008

Aceste dependenţe funcţionale nu sunt însă şi complete (totale), deoarece în


tabela ProduseFacturate, avem şi următoarea dependenţă funcţională între un
atribut din grupul (SerieFactura, NrFactura, CodProdus) şi un alt atribut:
CodProdus→DenumireProdus
Dependenţele funcţionale (SerieFactura, NrFactura, CodProdus)→Cantitate şi
(SerieFactura, NrFactura, CodProdus)→Pret sunt dependenţe funcţionale
complete doar dacă tabelul ProduseFacturate nu ar avea în componenţă şi
atributul DenumireProdus:
SerieFactura NrFactura CodProdus Cantitate Pret
AA 1111 1 10 11
AA 2222 2 20 12
BB 1111 1 10 11
BB 2222 2 30 20

Înţelegerea corectă a dependenţelor funcţionale complete, prezintă interes în


interpretarea corectă a 2NF, aşa cum se va vedea în paragrafele următoare.
1.3.3. Dependenţa funcţională tranzitivă

Considerăm trei atribute sau grupuri de atribute pe care le notăm cu A,B,C. Dacă
între A şi B există dependenţă funcţională (A→B) şi între B şi C există
dependenţă funcţională (B→C), între A şi C există de asemenea dependenţă
funcţională, însă este considerată dependenţă funcţională tranzitivă, deoarece
(A→B→C).
Dependenţele funcţionale tranzitive, prezintă interes în interpretarea 3NF.
Exemplu:
În tabela Facturi:
SerieFactura NrFactura DataFactura CUIClient DenumireClient
AA 1111 1/1/2008 RO100 XYZ SA
AA 2222 1/1/2008 RO200 ABC SRL
BB 1111 1/1/2008 RO100 XYZ SA
BB 2222 2/2/2008 RO200 ABC SRL

Avem dependenţă funcţională tranzitivă între:


(SerieFactura, NrFactura)→DenumireClient
Explicaţie:
Se observă că (SerieFactura, NrFactura)→CUIClient, dar şi
CUIClient→DenumireClient. Asta înseamnă că dependenţa funcţională dintre
(SerieFactura, NrFactura)→DenumireClient nu este una directă, ci una
tranzitivă prin intermediul atributului CUIClient.
Practic avem: (SerieFactura, NrFactura)→CUIClient→DenumireClient
Capitolul I Concepte şi noţiuni de bază privind 19
bazele de date relaţionale
1.3.4. Formele normale

1NF – un tabel se află în forma normală unu, dacă are toate atributele atomice şi
nerepetitive.
Un atribut este considerat atomic, dacă nu mai poate fi descompus în alte atribute.
Deşi la prima vedere pare o chestiune simplă, atomicitatea atributelor a fost încă
de la începutul enunţării acestor reguli de către Codd, un subiect de controverse.
Aceasta deoarece, dacă ar fi să se respecte strict, atomicitatea atributelor s-ar
constitui într-o limită a bazelor de date relaţionale în anumite situaţii.
Pentru a explica acest lucru, am luat în considerare două exemple „clasice”, ce se
referă la atributele Data (cu sensul de data calendaristică) şi Adresa, care sunt de
fapt în esenţă două atribute compuse, care ar putea fi descompuse în alte atribute
atomice.

Data (atribute de tip data calendaristică)


Orice atribut de tip data calendaristică este de fapt un atribut compus, format din
trei componente: zi, luna, an. Limbaje de programare mai vechi, cum era de
exemplu COBOL, dar şi unele SGBD-uri, nu implementau tipul de dată
calendaristică ca un întreg, ci lăsau programatorului sarcina de a defini trei
atribute, câte unul pentru fiecare componentă: zi, luna şi an. Apoi, pentru
validarea şi prelucrarea datei calendaristice, programatorii îşi scriau secvenţe de
program speciale pentru acest lucru: validarea zilelor din lună (luni cu 30 zile, 31
zile, 28 zile, 29 zile), ani bisecţi, etc.
În prezent, SGBD-urile şi limbajele de programare oferă suport complet pentru
data calendaristică ca tip de date. Este clar că este mai simplu să se utilizeze direct
tipul de dată calendaristică oferit de SGBD-uri/limbaje de programare decât să
lucreze cu data sub formă de trei atribute (zi, luna, an). SGBD-urile care oferă
suport pentru data calendaristică, oferă şi suport complet pentru validarea şi
prelucrarea acesteia.

Adresa
Orice adresă este un atribut compus, format cel puţin din următoarele
componente: strada, număr, bloc, scară, etaj, apartament.
Descompunerea adresei în componente, poate fi făcută, numai dacă prezintă
interes pentru domeniul aferent bazei de date.
De exemplu, pentru o agenţie imobiliară este clar că prezintă interes utilizarea
unei adrese descompusă în baza de date ca atribute atomice: strada, număr, bloc,
etc. Una este să cauţi un imobil sau o ofertă de vânzare/cumpărare direct după
20 Baze de date – Introducere in SQL Server 2008

stradă şi alta este să cauţi imobilul printr-o prelucrare asupra unui atribut care pe
lângă alte informaţii conţine şi strada.
Dacă ne situăm însă în cazul unui vânzător de carte, există probabilitatea ca pe
acesta să nu îl intereseze stocarea adreselor cumpărătorilor în varianta mai multe
atribute atomice, ci doar ca un singur atribut Adresa.
Având în vedere că SGBD-urile actuale oferă suport si pentru alte tipuri de date
compuse, sau chiar tipuri de date utilizator, decizia privind descompunerea sau
nedescompunerea unui atribut în componente rămâne la latitudinea proiectanţilor
bazelor de date, în funcţie de cerinţe.

2NF – un tabel se află în forma normală doi, dacă se află în forma normală unu şi
toate atributele sale non cheie se află în dependenţă funcţională completă faţă de
cheia primară.

Exemplul 1:
Considerăm tabela Facturi:
Facturi(SerieFactura, NrFactura, Data, CUIClient, DenumireClient)
Cunoscând faptul că o factură se emite unui singur client, avem:
 Tabelul respectă 1NF
 Între grupul de atribute care este cheie primară (SerieFactura, NrFactura)
şi celelalte atribute există dependenţă funcţională completă, aşa cum se
remarcă din graful dependenţelor funcţionale următor:

SerieFactura, NrFactura

Data
CUIClient DenumireClient

 Nu există nici o dependenţă funcţională parţială între o componentă a grupului


de atribute care este cheie primară şi celelalte atribute.
În concluzie, tabelul respectă 2NF.

Exemplul 2:
Considerăm tabelul MateriiPrimeConsumate:
Capitolul I Concepte şi noţiuni de bază privind 21
bazele de date relaţionale
MateriiPrimeConsumate(NrBonConsum, CodMatPrima, Cantitate,
DenMatPrima)
Cunoscând faptul că o materie primă consumată nu poate fi menţionată decât o
singură dată pe acelaşi bon de consum, avem:
 Tabelul respectă 1NF;
 (NrBonConsum, CodMatPrimă)→Cantitate şi
 (NrBonConsum, CodMatPrimă)→DenMatPrima
 Dependenţa funcţională (NrBonConsum,
CodMatPrimă)→DenMatPrima nu este una completă, deoarece şi
CodMatPrimă→DenMatPrima, ceea ce înseamnă că o componentă din
grupul de atribute (CodMatPrima) care formează cheia primară determină
funcţional un alt atribut din tabel (DenMatPrima), aşa cum se remarcă şi
din graful dependenţelor funcţionale următor:

NrBonConsum, CodMatPrimă

Cantitate DenMatPrima

În concluzie, tabelul nu respectă 2NF. Soluţia pentru a respecta 2NF este un nou
tabel în care va intra dependenţa funcţională parţială dintre
CodMatPrimă→DenMatPrima şi eliminarea acesteia din tabelul
MateriiPrimeConsumate. CodMateriePrima în tabelul MateriiPrimeConsumate
va fi cheie externă (subliniat cu linie întreruptă), însă va face parte din cheia
primară a tabelului.

NrBonConsum, CodMatPrimă

Cantitate DenMatPrima

MateriiPrimeConsumate(NrBonConsum, CodMatPrima, Cantitate)


MateriiPrime(CodMatPrima, DenMatPrima)
22 Baze de date – Introducere in SQL Server 2008

3NF – un tabel se află în forma normală trei, dacă se află în forma normală doi şi
nu există dependenţe funcţionale tranzitive între cheia primară şi celelalte atribute.
Exemplul 1:
Considerăm tabela Facturi:
Facturi(SerieFactura, NrFactura, Data, CUIClient, DenumireClient)
Cunoscând faptul că o factură se emite unui singur client, avem:
 Tabelul respectă 1NF
 Tabelul respectă 2NF – aşa cum s-a stabilit anterior
 Pentru a stabili dacă respectă 3NF sunt căutate eventualele dependenţe
funcţionale tranzitive între cheia primară şi celelalte atribute:

SerieFactura, NrFactura

Data
CUIClient DenumireClient

 Din graful dependenţelor funcţionale anterior, se remarcă că există o


dependenţă funcţională între atributul CUIClient şi atributul
DenumireClient:CUIClient→DenumireClient.
Rezultă că: (SerieFactura, NrFactura)→CUIClient→DenumireClient
Concluzia este că tabela Facturi nu respectă 3NF, deoarece între cheia primară şi
atributul DenumireClient există dependenţă funcţională tranzitivă. De fapt
dependenţa funcţională tranzitivă prezentă ascunde un alt tabel (cu cheia primară
CUIClient), care se află inclus în tabela Facturi.
Ca să respecte 3NF, se elimină dependenţa funcţională tranzitivă din tabela
Facturi şi se formează un nou tabel cu dependenţa funcţională găsită între cele
doua atribute din tabela Facturi, atribute care nu erau cheie primară în acea tabelă.
Determinantul CUIClient, va fi cheia primară a noului tabel.
Capitolul I Concepte şi noţiuni de bază privind 23
bazele de date relaţionale

SerieFactura, NrFactura

Data
CUIClient DenumireClient

Avem tabelele în 3NF:


Facturi(SerieFactura, NrFactura, Data, CUIClient)
Clienti(CUIClient, DenumireClient)
Între cele două tabele va exista o legătură definită prin intermediul atributului
CUIClient, atribut care în tabelul Facturi va fi cheie externă (subliniat cu linie
întreruptă).
În concluzie, într-un tabel trebuie să rămână doar dependenţele funcţionale
complete (2NF) şi directe (3NF), care nu se manifestă şi prin intermediul unor
atribute sau grupuri de atribute intermediare.
Dependenţele funcţionale tranzitive, cele care interesează în 3NF, pot fi observate
cu uşurinţă cu ajutorul grafului dependenţelor funcţionale, graf ce poate fi elaborat
după determinarea tuturor dependenţelor funcţionale dintre atribute.

1.3.5. Etapele normalizării

La modul general, proiectarea unei baze de date, indiferent de tipul acesteia şi de


metoda folosită, presupune:
 Stabilirea atributelor şi a regulilor de gestiune (restricţiilor) ce se aplică
acestora, în urma unui proces de analiză a sistemului informaţional unde se va
implementa baza de date;
 Gruparea atributelor în tabele (prin normalizare sau utilizând altă modalitate)
 Stabilirea legăturilor între tabele
 Stabilirea restricţiilor de integritate şi a mecanismelor de securitate.
Procesul de normalizare, trebuie să urmărească, în principal, câteva aspecte, care
s-au expus în paragrafele anterioare:
 Eliminarea riscului de apariţie a anomaliilor de actualizare şi stocare a datelor;

 Obţinerea unui model relaţional al bazei de date, în aşa fel încât nevoia de a-l
reproiecta ulterior să fie cât mai redusă. Este evident că modificarea
fundamentală în timp a regulilor de gestiune care se aplică atributelor culese
24 Baze de date – Introducere in SQL Server 2008

din sistemul informaţional, poate afecta procesul de normalizare ce are ca


finalitate modelul relaţional al bazei de date. Regulile de gestiune influenţează
în mod direct dependenţele funcţionale dintre atribute, acestea reprezentând de
fapt baza procesului de normalizare, orice modificare a acestora având indirect
influenţă asupra modelului relaţional al bazei de date. Interpretarea mai
flexibilă a acestor reguli, poate să conducă la un model care să nu fie afectat în
timp, în cazul schimbării anumitor reguli de gestiune, însă o flexibilitate prea
ridicată poate să însemne mai multe tabele în baza de date şi o gestiune mai
greoaie acestora împreună cu restricţiile asociate.
Pentru a exemplifica procesul de normalizare şi etapele acestuia, am luat în
considerare exemplul unei societăţi comerciale care doreşte să-şi implementeze un
sistem informatic pentru gestiunea contractelor şi a ofertelor puse la dispoziţia
clienţilor. Sistemul va utiliza o bază de date relaţională. Având în vedere că
realitatea dintr-un sistem informaţional este de obicei foarte complexă pentru a
putea fi expusă în totalitate în paginile unui manual, am ales să prezentăm o
variantă simplificată a atributelor utilizate în sistemul informaţional. Din analiza
sistemului informaţional actual au rezultat următoarele atribute:

Nr. Atribut
crt.
1 CUI client
2 Denumire client
3 Adresa client
4 Telefon client
5 Număr contract
6 Localitate client
7 Tara client
8 Data început contract
9 Data finalizării contract
10 Denumire client contract
11 CNP angajat supraveghere contract
12 Nume angajat
13 Prenume angajat
14 Data angajării
15 Adresa angajatului
16 Telefonul angajatului
17 Data început supraveghere
18 Data sfârşit supraveghere
19 Cod ofertă
20 Descriere ofertă
21 Tarif bază
22 Tarif negociat
23 Data început valabilitate ofertă
24 Data sfârşit valabilitate ofertă
25 Discount acordat
Capitolul I Concepte şi noţiuni de bază privind 25
bazele de date relaţionale
Reguli de gestiune:
 Un contract se încheie cu un singur client;

 Derularea unui contract este supravegheată de unul sau mai mulţi angajaţi. Un
angajat poate supraveghea un contract pe o perioadă determinată (între Data
început supraveghere şi Data sfârşit supraveghere); Perioada în care un
angajat supraveghează derularea unui contract nu este aceeaşi cu perioada de
derulare a contractului;
 Dacă perioada de derulare a unui contract este mare, nu se poate ca acelaşi
angajat să supravegheze contractul în mai multe perioade diferite de timp;
 Fiecare contract se bazează pe o singură ofertă de servicii, în urma căruia se
încheie contractul;
 Societatea poate avea în derulare simultan mai multe oferte de servicii;
 Serviciile ofertate sunt prezentate sub forma unor descrieri netipizate, în care
sunt prezentate detaliile de execuţie ale acestora;
 O ofertă de servicii este valabilă într-o singură perioadă de timp (între Data
început valabilitate ofertă şi Data sfârşit valabilitate ofertă), nefiind posibilă
reactivarea unor oferte mai vechi, pentru alte perioade;
 Fiecare ofertă de servicii are un tarif fix (Tarif bază), însă în urma
negocierilor, tariful la care se încheie contractul poate fi diferit (Tarif
negociat), datorită unor factori cum ar fi: durata contractului, fidelizare clienţi,
etc;
 Discount acordat se calculează ca fiind diferenţa între Tarif bază şi Tarif
negociat;
 Notă. Toate relaţiile de calcul determinate din analiza informaţională sunt
asociate regulilor de gestiune.
a). În forma normală unu (1NF), se construieşte o singură tabelă (numită şi
universală) ce va conţine toate atributele, având în vedere toate aspectele
prezentate deja în paragraful 1.3.4. Forme normale. Aceasta va conţine toate
atributele, mai puţin cele:
 Sinonime. Atributele sinonime reprezintă de fapt mai multe apariţii ale
aceluiaşi atribut. Una din restricţiile 1NF este ca atributele să nu se repete.
 Cele rezultate din calcule pe baza altor atribute. Trebuie făcută menţiunea
că în cazul unor calcule foarte laborioase şi a unor accesări dese a anumitor
atribute calculate proiectanţii unei baze de date, în urma unei analize atente,
pot face excepţii, în sensul că se pot memora în mod controlat şi asemenea
atribute.
26 Baze de date – Introducere in SQL Server 2008

Se observă că în lista de atribute prezentată iniţial, atributul Denumire client


contract este sinonim cu Denumire client, iar atributul DiscountAcordat, este
atribut calculat. În urma eliminării acestor două atribute, dicţionarul simplificat al
datelor se prezintă astfel:

Nr. Atribut Descriere


crt.
1 CUICl CUI client
2 DenumireCl Denumire client
3 AdresăCl Adresa client
4 TelefonCl Telefon client
5 NrContract Număr contract
6 LocalitateCl Localitate client
7 TaraCl Tara client
8 DataContract Data început contract
9 DataFinContract Data finalizării contract
10 CNPAngajat CNP angajat supraveghere
contract
11 NumeAng Nume angajat
12 PrenumeAng Prenume angajat
13 DataAng Data angajării
14 AdresăAng Adresa angajatului
15 TelefonAng Telefonul angajatului
16 DataÎnceput Data început supraveghere
17 DataSfârşit Data sfârşit supraveghere
18 CodOfertă Cod ofertă
19 DescriereOfertă Descriere ofertă
20 TarifBază Tarif bază
21 TarifNegociat Tarif negociat
22 DataÎnceputValab Data început valabilitate ofertă
23 DataSfârşitValab Data sfârşit valabilitate ofertă

Aspectele legate de 1NF sunt cu mult mai complexe decât par la prima vedere. În
afară de problema atomicităţii atributelor, expusă în paragraful 1.3.4. mai apare o
problemă la înglobarea tuturor atributelor din dicţionarul datelor într-un singur
tabel: stabilirea unei chei primare pentru acest tabel. Având în vedere că un
singur tabel cu toate atributele conţine foarte multe redundanţe, este foarte greu sa
se stabilească o cheie primară formată dintr-un singur atribut. Chiar şi stabilirea
unei chei primare formată ca grup din mai multe atribute poate deveni o problemă.
Nu trebuie uitat că o cheie primară trebuie să nu conţină valori nule şi să aibă
valori unice, aşa cum s-a prezentat şi în paragraful 1.2.1. Stabilirea unei chei
primare pentru tabela universală din 1NF se poate realiza în câteva moduri:
 Găsirea unui singur atribut (aproape imposibil, poate doar dacă baza de date
este foarte simplă) sau a unui grup de atribute care să îndeplinească condiţiile
de a fi declarate cheia primară a tabelului (unicitate şi valori nenule);
Capitolul I Concepte şi noţiuni de bază privind 27
bazele de date relaţionale
 În cazuri excepţionale se pot desemna chiar toate atributele (teoria permite
acest lucru) tabelei ca fiind cheie primară, cu condiţia să nu mai existe
redundanţe în liniile tabelei.
 În cazul în care, chiar dacă s-ar desemna toate atributele ca fiind cheie primară
a tabelului, tot ar mai exista redundanţe în liniile tabelului, atunci se poate
recurge la o cheie surogat (vezi paragraful 1.2.1 pentru detalii), arbitrară, care
nu are legătură cu realitatea ce se doreşte a fi transpusă în baza de date, însă
care asigură unicitatea rândurilor tabelului. Este o soluţie de compromis, însă
având în vedere că este puţin probabil să se utilizeze o bază de date direct în
1NF, poate fi o soluţie rapidă şi acceptabilă în multe situaţii.
Pentru exemplul luat în considerare (pentru simplificare, am ales varianta cu cheie
primară surogat), avem tabela Contracte în 1NF:
Contracte(IDRand, CUICl, DenumireCl, AdresăCl, TelefonCl, LocalitateCl,
TaraCl, NrContract, DataContract, DataFinContract, CNPAngajat, NumeAng,
PrenumeAng, DataAng, AdresăAng, TelefonAng, DataÎnceput, DataSfârşit,
CodOfertă, DescriereOfertă, TarifBază, TarifNegociat, DataÎnceputValab,
DataSfârşitValab)
b) Pentru a face trecerea tabelei din 1NF în forma normală doi (2NF)
trebuiesc identificate toate dependenţele funcţionale complete dintre atribute
(şi/sau grupuri de atribute). Pentru a nu pierde din dependenţele funcţionale şi
pentru a putea fi observate mai uşor, se poate întocmi o matrice (tabel) a
dependenţelor funcţionale care oferă o viziune de ansamblu asupra acestora.
În tabelul următor sunt prezentate toate dependenţele funcţionale complete (notate
cu cifra 1) dintre atribute:
28 Baze de date – Introducere in SQL Server 2008

Observaţii:
 Toate atributele (grupurile de atribute) care determină alte atribute prin
dependenţă funcţională completă se numesc determinanţi şi sunt într-o
primă fază chei candidate (vezi paragraful 1.2.1), din care se vor alege
cheile primare ale tabelelor rezultate.
 Pentru atributele care nu se află în dependenţă funcţională completă faţă de
nici un atribut (ca determinaţi) şi nici nu determină funcţional alte atribute
se caută grupuri de atribute care să le determine. Este cazul atributelor
DataInceput şi DataSfarsit. Pentru acestea s-a găsit grupul (CNPAngajat,
NrContract) care le determine prin dependenţă funcţională completă:
(CNPAngajat, NrContract)→DataInceput şi (CNPAngajat,
NrContract)→DataSfârşit.
Notă. Dacă regula de gestiune “Dacă perioada de derulare a unui contract
este mare, nu se poate ca acelaşi angajat să supravegheze contractul în
mai multe perioade diferite de timp” ar fi permis ca acelaşi angajat să
supravegheze de mai multe ori în timp acelaşi contract, dependenţele
funcţionale (CNPAngajat, NrContract)→DataInceput şi (CNPAngajat,
NrContract) →DataSfârşit nu sunt valabile.
 Eventuale alte dependenţe, gen (CNPAngajat, NrContract)→NumeAng,
nu sunt dependenţe funcţionale complete, deoarece o componentă din
Capitolul I Concepte şi noţiuni de bază privind 29
bazele de date relaţionale
determinant are o dependenţă funcţională cu un alt atribut CNPAngajat
→NumeAng.
 Dacă regulile de gestiune permit, este posibil ca un atribut care nu este nici
determinant şi nici determinat să facă parte chiar el dintr-un grup de
atribute care va fi determinant pentru alte atribute.
 În cazul în care pentru un atribut care nu este nici determinant şi nici
determinat nu se poate găsi un determinant format dintr-un grup de atribute
(inclusiv varianta în care atributul ar fi inclus în grup), poate fi luată în
considerare şi utilizarea unei chei surogat, cu valori arbitrare, dar care să
asigure unicitatea rândurilor viitorului tabel.
 În cazul în care există dependenţe funcţionale reciproce între determinanţi,
pentru a nu avea tabele identice se renunţă la dependenţele unuia dintre ei.
Este cazul CUICl→DenumireCl şi DenumireCl→CUICl. În continuarea
demersului o să luăm în considerare doar dependenţele funcţionale ale
determinantului CUICl.
Având în vedere toate aspectele prezentate, pentru scrierea 2NF, matricea
dependenţelor funcţionale până în acest moment se prezintă astfel:
30 Baze de date – Introducere in SQL Server 2008

Ţinând cont că unele dintre dependenţe funcţionale se regăsesc printre


dependenţele funcţionale ale altor determinanţi (mai generali), tabelele în 2NF se
pot scrie astfel:
Contract(NrContract, DataContract, DataFinContract, TarifNegociat, CUICl,
DenumireCli, AdresaCl, TelefonCl, LocalitateCl, TaraCl, CodOfertă,
DescriereOfertă, TarifBază, , DataÎnceputValab, DataSfârşitValab)
Angajat(CNPAngajat, NumeAng, PrenumeAng, DataAng, AdresăAng,
TelefonAng)
Lucreaza(CNPAngajat, NrContract, DataÎnceput, DataSfârşit)
Restricţia conform căreia toate atributele ce nu sunt cheie primară (şi nici nu fac
parte din cheia primară) se află în dependenţă funcţională completă faţă de aceasta
este respectată, deci tabelele sunt în 2NF.
c) Pentru a face trecerea de la 2NF la forma normală trei (3NF), dintre
dependenţele funcţionale complete determinate anterior la 2NF, trebuiesc
identificate care sunt dependenţe funcţionale tranzitive. Aceste dependenţe se
vor elimina din tabelele scrise în 3NF. Cea mai simplă variantă de identificare a
dependenţelor funcţionale tranzitive, este desenarea unui graf al dependenţelor
funcţionale deduse la 2NF din baza matricei dependenţelor funcţionale. În graf se
observă foarte uşor care din dependenţele funcţionale sunt directe (pentru a fi
reţinute) şi care sunt dependenţele funcţionale tranzitive. Acestea din urmă vor
forma alte tabele.
Principiul simplificat de formare a tabelelor în 3NF este următorul: dacă avem în
2NF un tabel cu atributele A, B, C, unde A este cheie primară rezultă:
 Tabel în 2NF: T(A, B, C)

 A→B, A→C şi B→C adică A→B→C, unde A→C este dependenţă


funcţională tranzitivă
 În 3NF vor rămâne în tabele doar dependenţele funcţionale complete şi
directe, adică:
 A→B şi B→C de unde rezultă
 Tabelele în 3NF: T(A, B) şi T1(B,C), unde B din tabelul T va fi cheie
externă.
Pentru exemplul luat în considerare anterior, graful dependenţelor funcţionale,
realizat pe baza matricei dependenţelor funcţionale prezentate la 2NF, este:
Capitolul I Concepte şi noţiuni de bază privind 31
bazele de date relaţionale

DescriereOferta

DataInceputValab
TarifBaza CodOferta

DataSfarsitValab
DataContra
ct NrContract
TarifNegociat DenumireCl

DataFinContract AdresaCl
CUICl
TelefonCl

LocalitateCl
TaraCl NumeAng

PrenumeAng
CNPAngajat
AdresaAng
NrContract, CNPAng

TelefonAng

DataInceput DataSfarsit

Dacă marcăm dependenţele funcţionale directe cu o linie îngroşată şi pe cele care


rezultă că sunt tranzitive cu linie întreruptă, vom avea:
32 Baze de date – Introducere in SQL Server 2008

DescriereOferta

DataInceputValab
TarifBaza CodOferta
DataSfarsitValab

DataContra NrContract
ct
TarifNegociat DenumireCl

DataFinContr AdresaCl
act CUICl
TelefonCl

LocalitateCl
TaraCl NumeAng

CNPAngaj PrenumeAng
at
NrContract, AdresaAng
CNPAngajat
TelefonAng

DataInceput
DataSfarsit

Matricea dependenţelor funcţionale în care se pot observa şi dependenţele


funcţionale tranzitive (notate cu 1T) este:
Capitolul I Concepte şi noţiuni de bază privind 33
bazele de date relaţionale

Aşa cum se remarcă din graful dependenţelor funcţionale şi din matricea


dependenţelor funcţionale, tabelele (fără dependenţele funcţionale tranzitive) în
3NF (cu linie întreruptă sunt menţionate cheile externe), sunt:
Client(CUIClient, DenumireCli, AdresaCl, TelefonCl, LocalitateCl, TaraCl)
Contract(NrContract, DataContract, DataFinContract, TarifNegociat,
CUIClient, CodOferta)
Oferta(CodOfertă, DescriereOfertă, TarifBază, DataÎnceputValab,
DataSfârşitValab)
Angajat(CNPAngajat, NumeAng, PrenumeAng, DataAng, AdresăAng,
TelefonAng)
Lucreaza(CNPAngajat, NrContract, DataÎnceput, DataSfârşit)
Schematic, tabelele, legăturile dintre ele, precum şi tipul acestora se prezintă
astfel:
34 Baze de date – Introducere in SQL Server 2008

Client
CUICl, DenumireCl, AdresaCl, TelefonCl, LocalitateCl, TaraCl
1

Oferta
CodOfertă, DescriereOfertă, TarifBază, DataÎnceputValab, DataSfârşitValab
1

Contract n n
NrContract, DataContract, DataFinContract, TarifNegociat, CUICl, CodOferta
1

Lucrează n
n
CNPAngajat, NrContract, DataÎnceput, DataSfârşit

Angajat
1
CNPAngajat, NumeAng, PrenumeAng, DataAng, AdresăAng, TelefonAng

1.4. . Exerciţii propuse şi rezolvate


O societate specializată în organizarea de cursuri de perfecţionare, doreşte să
implementeze un sistem informatic pentru gestiunea acestora. În vederea
proiectării bazei de date aferente, din analiza sistemului informaţional actual au
rezultat următoarele atribute:

Nr. Atribut
crt.
1 Cod modul
2 Denumire modul
3 Data începerii modul
4 Data finalizării modul
5 Taxa modul
6 Cod disciplină
7 Denumire disciplină
8 Nr ore
9 CNP cursant
10 Nume cursant
11 Prenume cursant
Capitolul I Concepte şi noţiuni de bază privind 35
bazele de date relaţionale
Nr. Atribut
crt.
12 Iniţiale cursant
13 Adresa cursant
14 Telefon cursant
15 Procent reducere
16 Nr contract
17 Data contract
18 Descriere clauze contract
19 Nume prenume cursant
20 Taxa netă contract
21 Total taxe

Reguli de gestiune:
 Un modul de curs poate avea în componenţă mai multe discipline.
 O disciplină poate să se găsească cu număr de ore diferit, în componenţa
mai multor module.
 Un cursant se poate înscrie la mai multe module (chiar dacă se suprapun
perioadele). Pentru fiecare înscriere, se încheie contract distinct, în care se
menţionează clauzele contractuale, procentul de reducere primit la înscriere
şi taxa netă ce urmează a fi plătită de cursant în urma acordării reducerii.
 Taxa netă contract este calculată ca fiind diferenţa dintre Taxa modul şi
reducerea calculată pentru un modul, în funcţie de Procent reducere
acordat unui cursant;
Notă. Toate relaţiile de calcul determinate din analiza informaţională sunt
asociate regulilor de gestiune.
a). În forma normală unu (1NF), se construieşte o singură tabelă (numită şi
universală) ce va conţine toate atributele, având în vedere toate aspectele
prezentate deja în paragraful 1.3.4. Forme normale. Aceasta va conţine toate
atributele, mai puţin cele sinonime şi cele rezultate din calcule pe baza altor
atribute.
Se observă că în lista de atribute prezentată iniţial, atributul Nume prenume
cursant este sinonim cu atributele Nume cursant, Iniţiale cursant şi Prenume
cursant. Având în vedere că Nume cursant, Iniţiale cursant şi Prenume cursant
sunt atomice, am optat pentru eliminarea atributului Nume prenume cursant. Se
observă că atributele Taxa netă contract şi Total taxe sunt calculate şi se elimină.
În urma eliminării acestor trei atribute, dicţionarul simplificat al datelor se
prezintă astfel:
36 Baze de date – Introducere in SQL Server 2008

Nr.
Atribut Descriere
crt.
1 CodModul Cod modul
2 DenModul Denumire modul
3 DataIncepere Data începerii modul
4 DataFinalizare Data finalizării modul
5 TaxaModul Taxa modul
6 CodDisciplina Cod disciplină
7 DenDisciplina Denumire disciplină
8 NrOre Nr ore
9 CNPCursant CNP cursant
10 NumeCursant Nume cursant
11 PrenumeCursant Prenume cursant
12 InitialeCursant Iniţiale cursant
13 AdresaCursant Adresa cursant
14 TelCursant Telefon cursant
15 ProcReducere Procent reducere
16 NrContract Nr contract
17 DataContract Data contract
18 Clauze Descriere clauze contract

Pentru simplificare, am ales varianta cu cheie primară surogat, avem tabela


Cursuri în 1NF:
Cursuri(IDRand, CodModul, DenModul, DataIncepere, DataFinalizare,
TaxaModul, CodDisciplina, DenDisciplina, NrOre, CNPCursant, NumeCursant,
PrenumeCursant, InitialeCursant, AdresaCursant, TelCursant, ProcReducere,
NrContract, DataContract, Clauze)
b). Pentru a realiza trecerea tabelei din 1NF în forma normală doi (2NF)
trebuiesc identificate toate dependenţele funcţionale complete dintre atribute
(şi/sau grupuri de atribute).
În tabelul următor sunt prezentate toate dependenţele funcţionale complete (notate
cu cifra 1) dintre atribute:
Capitolul I Concepte şi noţiuni de bază privind 37
bazele de date relaţionale

Observaţie:
Grupul de atribute (CodModul, CodDisciplină) determină atributul NrOre,
deoarece în regulile de gestiune este menţionat că “o disciplină poate să se
găsească cu număr de ore diferit, în componenţa mai multor module”.

Ţinând cont că unele dintre dependenţe funcţionale se regăsesc printre


dependenţele funcţionale ale altor determinanţi (mai generali), pentru a respecta
2NF, tabelele se pot scrie astfel:
Disciplina(CodDisciplină, DenDisciplina)
Contract(NrContract, CodModul, DenModul, DataIncepere, DataFinalizare,
TaxaModul, CNPCursant, NumeCursant, PrenumeCursant, InitialeCursant,
AdresaCursant, TelCursant, ProcReducere, DataContract, Clauze)
ModulDisciplina(CodModul, CodDisciplina, NrOre)
Restricţia conform căreia toate atributele ce nu sunt cheie primară (şi nici nu fac
parte din cheia primară) se află în dependenţă funcţională completă faţă de aceasta
este respectată, deci tabelele sunt în 2NF.
38 Baze de date – Introducere in SQL Server 2008

c). Pentru a face trecerea de la 2NF la forma normală trei (3NF), dintre
dependenţele funcţionale complete determinate anterior la 2NF, trebuiesc
identificate care sunt dependenţe funcţionale tranzitive. Aceste dependenţe se
vor elimina din tabelele scrise în 3NF. Cea mai simplă variantă de identificare a
dependenţelor funcţionale tranzitive, este desenarea unui graf al dependenţelor
funcţionale deduse la 2NF din baza matricei dependenţelor funcţionale. În graf se
observă foarte uşor care din dependenţele funcţionale sunt directe (pentru a fi
reţinute) şi care sunt dependenţele funcţionale tranzitive. Acestea din urmă vor
forma alte tabele.
În figura următoare este prezentat graful dependenţelor funcţionale, realizat pe
baza matricei dependenţelor funcţionale prezentate la 2NF (cu linie întreruptă sunt
marcate dependenţele funcţionale tranzitive):

PrenumeCursant InitialeCursant

AdresaCursant

TelCursant
NumeCursant

CNPCursant

Clauze

DenModul NrContract

DataIncepere DataContract

DataFinalizare

TarifModul CodModul

CodDisciplina

CodModul, CodDisciplina
DenDisciplina

NrOre
Capitolul I Concepte şi noţiuni de bază privind 39
bazele de date relaţionale

Matricea dependenţelor funcţionale în care se pot observa şi dependenţele


funcţionale tranzitive (notate cu 1T) este:

Aşa cum se remarcă din graful dependenţelor funcţionale şi din matricea


dependenţelor funcţionale, tabelele (fără dependenţele funcţionale tranzitive) în
3NF (cu linie întreruptă sunt menţionate cheile externe), sunt:

Disciplina(CodDisciplină, DenDisciplina)
Cursant(CNPCursant, NumeCursant, PrenumeCursant, InitialeCursant,
AdresaCursant, TelCursant)
Contract(NrContract, CodModul, DataIncepere, DataFinalizare, CNPCursant,
ProcReducere, DataContract, Clauze)
Modul(CodModul, DenModul, TaxaModul)
ModulDisciplina(CodModul, CodDisciplina, NrOre)
40 Baze de date – Introducere in SQL Server 2008

Schematic, tabelele, legăturile dintre ele, precum şi tipul acestora se prezintă


astfel:
Cursant
CNPCursant, NumeCursant, PrenumeCursant, InitialeCursant, AdresaCursant, TelCursant

Contract n
NrContract, CodModul, DataIncepere, DataFinalizare, CNPCursant, ProcReducere, DataContract,
Clauze

n
Modul
1
CodModul, DenModul, TaxaModul

n ModulDisciplina
CodModul, CodDisciplina, NrOre

n
Disciplina

1 CodDisciplină, DenDisciplina
42 Baze de date – Introducere in SQL Server 2008

CAPITOLUL 2
SGBD SQL SERVER

2.1. Arhitectura Client-Server


Până la arhitectura Client/Server din zilele noastre istoria Sistemelor Informatice a
parcurs 2 etape importante:
 sisteme centralizate in care datele erau memorate pe un mainframe si toate
prelucrările se executau pe acesta iar utilizatorii interogau BD folosind
terminale de intrare/ieşire simple;
 sisteme file/server (semicentralizate) in care datele erau memorate pe server
iar staţiile de lucru accesau serverul, încărcau fişierele de date de pe server la
nivel local, le prelucrau si eventual rezultatele prelucrării erau memorate tot
pe server.
Deoarece sistemele file/server erau limitate la un număr redus de utilizatori
simultani a apărut arhitectura client/server iar server-ul de fişiere a fost înlocuit
cu un server de baze de date, care utilizează un SGBD dotat cu un proces
suplimentar care “ascultă cererile clienţilor şi le răspunde în mod direct.
Arhitectura client server are la bază patru elemente:
 delimitarea netă dintre serviciile de prezentare şi cele de manipulare a
informaţiilor;
 flexibilitate, în ceea ce priveşte dezvoltările ulterioare implementării;
 punerea în funcţiune a unui mecanism de asigurare a securităţii şi integrităţii
pentru datele rezidente pe servere;
 arhitectura deschisă în sensul federalizării unei multitudini de platforme
(mainframe, mini, micro-calculatoare) şi de produse (echipamente şi aplicaţii-
program) ale diferiţilor actori de pe piaţa tehnologiei informaţionale.
O arhitectura client/server reprezintă un model de descompunere a unei aplicaţii
în doua componente distincte: o componenta client si o componenta server.
Componenta client se executa pe o staţie de lucru unde recepţionează date de la un
utilizator, le structurează si transmite cereri de realizare a unor servicii pe baza
acestor date către componenta server. De cealaltă parte, server-ul aşteaptă cereri
de la clienţi iar in momentul când acesta recepţionează o cerere, o procesează si
returnează rezultatul clientului. Clientul va comunica aceste rezultate
utilizatorului prin intermediul interfeţei sale. În locul unei procesări exclusive pe
staţii, se realizează o distribuţie a sarcinilor între server si client, sistemele
client/server fiind sisteme distribuite.
Capitolul II SGBD SQL SERVER 43

In cazul in care serverul are nevoie de informaţii de la un alt server, devine el


însuşi client. Un server trebuie sa fie capabil sa răspundă la mai multe cereri din
partea clienţilor, o eventuala cerere de la un client nu trebuie blocata pana când se
va răspunde cererii precedente. Pentru rezolvarea acestei probleme se foloseşte
tehnica de multithreading, potrivit căreia, pentru fiecare client se deschide un
nou fir de execuţie pentru a-l deservi, astfel serverul rămâne deschis spre a primi
noi cereri.
Arhitectura client/server are următoarele caracteristici:
 furnizează o separare clara a funcţionalităţilor pe baza ideii de serviciu:
server-ul furnizează anumite servicii, în timp de clientul este un “consumator”
de servicii;
 resurse partajate: un server poate servi mai mulţi clienţi simultan si
controlează accesul acestora la resursele partajate;
 protocoale asimetrice: între server si clienţi exista o relaţie 1-n. Clienţii sunt
cei care iniţiază dialogul cu un server prin cererea unui anumit serviciu.
Serverele sunt entităţi pasive care aşteaptă cererile clienţilor si transmit
acestora doar replici la cererile recepţionate;
 transparenta locaţiei: serverul este un proces care poate fi localizat pe acelaşi
calculator ca si clientul sau pe un calculator diferit, aflat în reţea. În general
aplicaţiile client/server ascund clienţilor informaţiile referitoare la poziţia
serverului în cadrul unei reţele, redirectând cererile de servicii atunci când este
necesar. Un program poate fi client, server, sau ambele;
 comunicaţie bazata pe mesaje: clienţii si serverele interacţionează între ele
prin intermediul mecanismului de transmisie de mesaje. Mesajul reprezintă
mecanismul de cerere si replicare a unui serviciu;
 încapsularea serviciilor: un mesaj specifica server-ului serviciul cerut.
Determinarea modului în care este satisfăcuta cererea cade în responsabilitatea
server-ului. Server-ele pot fi modificate, actualizate si sau optimizate fără
afectarea clienţilor acestora, atâta timp cât interfaţa publica a mesajelor nu este
modificata;
 scalabilitate: sistemele client/server pot fi scalate pe orizontala sau verticala.
Scalarea orizontala reprezintă influenţarea stricta a performantei la creşterea
sau scăderea numărului de clienţi. Scalarea verticala semnifică migrarea către
servere rapide sau spre multi-servere;
 integritate: datele si programele de pe server sunt memorate centralizat, ceea
ce implica o actualizare si securizare eficiente a acestora. În acelaşi timp,
clienţii rămân independenţi de server.
44 Baze de date – Introducere in SQL Server 2008

Orice sistem client server este alcătuit din minimum trei componente principale:
interfaţa cu utilizatorul, aplicaţia (prelucrările sau procesele) şi sistemul de
gestiune a bazelor de date. Arhitectura client/server poate fi implementata sub
forma a trei modele.
Arhitectura pe un nivel (one tier architecture) apare când serverul şi clientul sunt
una şi aceeaşi aplicaţie.
În arhitectura pe două nivele (two layer architecture), datele se găsesc într-un alt
mediu şi sunt accesate prin intermediul primului nivel. Această arhitectură descrie
perfect aplicaţiile client-server.

Figura 1 Arhitectura client / server pe două niveluri


Datele sunt stocate într-un server de baze de date (SQL Server sau Oracle).
Managementul tranzacţiilor este împărţit între client (validări şi prelucrări locale)
şi server (proceduri stocate şi triggere). Deşi suportă un număr destul de mare de
utilizatori concurenţi, această arhitectură are doua limitări:
 scăderea performanţelor odată cu sporirea numărului de utilizatori;
 implementarea serviciilor de management al tranzacţiilor prin intermediul
procedurilor proprietare ale SGBD-ului limitează flexibilitatea şi portabilitatea
logicii aplicaţiilor de pe un sistem pe altul.
De cele mai multe ori, regulile de validare (bussiness rules) sunt implementate în
întregime în server.
In arhitectura pe trei nivele (three tier architecture) regulile de validare sunt
stocate într-un mediu propriu (de obicei, într-un calculator separat), astfel încât
aplicaţiile client le pot folosi independent una de alta şi independent de datele
existente. În acest fel, aplicaţia client oferă interfaţa, serverul de baze de date
oferă datele, iar al doilea nivel se ocupă de validarea datelor.
Capitolul II SGBD SQL SERVER 45

Figura 2. Arhitectura client / server pe trei niveluri


Acest lucru înseamnă că există două comunicaţii client-server: una între aplicaţiile
client şi nivelul al doilea, iar cealaltă între nivelul al doilea şi serverul de baze de
date. Aplicaţiile client nu comunica niciodată direct cu serverul de baze de date.
Numărul posibil de utilizatori concurenţi creşte semnificativ şi, în acelaşi timp,
execuţia logicii aplicaţiei pe o staţie separată de serverul de baze de date duce la
îmbunătăţirea performanţelor.
Avantajele arhitecturii client/server:
 este uşor de implementat şi cu costuri modeste de dezvoltare si întreţinere;

 permite suportul noilor tehnologii, mai ales cele orientate pe obiecte;


 suporta adăugare de componente hardware noi;
 asigură suportul pentru noi componente: scanere, camere video;
 se pot implementa aplicaţii software realizate de diferiţi producători.

2.2. Prezentare SQL Server 2008


SQL Server 2008 este ultima versiune din categoria serverelor pentru gestiunea
bazelor de date oferite de compania Microsoft. Crearea, consultarea şi actualizarea
bazelor de date pe server, precum şi celelalte operaţii cum ar fi gestionarea
accesului la date pot fi programate în SQL Server utilizând limbajul SQL.
Dialectul limbajului SQL utilizat de SQL Server poarta numele de TRANSACT
SQL (sau T-SQL).
SQL Server, ca produs software, este dezvoltat de circa 20 ani. Prima versiune
existentă pe piaţă a fost SQL Server 1.0, lansată în 1989 şi a reprezentat rodul
unei colaborări între companiile Microsoft, Sybase şi Ashton-Tate1 pentru a crea o
soluţie competitivă în faţa ofertelor Oracle şi IBM.

1
În prezent, compania Sybase dezvoltă propriu SGBD iar Ashton-Tate a fost achiziţionată de
Borland. Ashton-Tate a fost compania care s-a făcut cunsocută în domeniul IT printr-un alt SGBD
46 Baze de date – Introducere in SQL Server 2008

Versiunea SQL Server 7.0 din 1998 (cunoscută sub numele de cod Sphinx) a
reprezentat primul server de date pentru care a fost implementată o interfaţă
grafică complexă de administrare şi, în ediţia 1999 (tot versiunea 7.0, dar cu nume
de cod Plato) au fost implementate tehnologiile OLAP.
Au urmat versiunile SQL Server 2000 (nume de cod Shiloh), 2003 (nume de cod
Liberty) şi 2005 (nume de cod Yukon) care au consolidat poziţia SQL Server în
topul primelor 3 cele mai bine vândute SGBD pentru baze de date relaţionale.
În prezent SQL Server este dezvoltat doar de firma Microsoft, devenind una dintre
cele mai importante aplicaţii dezvoltate de către aceasta.
SQL Server 2008 (nume de cod Katmai) este un produs ajuns la maturitate
compatibil cu cele mai recente tehnologii existente.
Ca şi versiunile precedente, SQL Server 2008 este disponibil în mai multe ediţii
ce diferă prin facilităţile oferite, resursele hardware capabile să le exploateze
(număr de procesoare, memorie RAM, etc.) şi politica de licenţiere (număr de
utilizatori, costuri licenţe, etc.)
Principalele ediţii ale SQL Server 2008 sunt:
 Enterprise
Reprezintă soluţia cu cele mai extinse facilităţi. Oferă utilizatorilor toate
instrumentele necesare privind administrarea şi securitatea datelor precum şi un
set complet de instrumente pentru dezvoltarea aplicaţiilor business intelligence.
 Standard
Ediţia Standard pune la dispoziţia utilizatorilor toate instrumentele pentru
administrarea bazelor de date şi pentru programare utilizând Transact SQL.
Principalele diferenţe faţă de ediţia Enterprise constau în lipsa unor facilităţi
specifice Datawarehouse, numărul limitat de procesoare pe care le poate exploata
serverul (4 la ediţia Standard, pe când la Enterprise numărul de procesoare este
limitat doar de posibilităţile sistemului de operare) şi numărul de instanţe ale
serverului ce pot fi create (16 în cazul Standard. şi 50 în cazul Enterprise).
Versiunea Standard este disponibilă şi într-o variantă numită SQL Server
Standard for Small Business care este limitată la accesarea serverului de pe
maxim 75 de staţii-client.
 Developer
Este o ediţie dedicată dezvoltatorilor de aplicaţii. Această ediţie prezintă toate
facilităţile disponibile în varianta Enterprise dar nu este destinată utilizării ca
server productiv, ci doar pentru teste şi programare.

(Dbase)
Capitolul II SGBD SQL SERVER 47

 Workgroup
Varianta Workgroup este destinată firmelor de dimensiuni mai mici. Oferă toate
funcţionalităţile necesare pentru administrarea datelor şi include instrumente
pentru raportare.
Capacitatea de procesare a datelor este limitată pentru maxim 2 procesoare şi 3
GB memorie RAM. Este o soluţie pentru costuri reduse (o licenţă de server cu 5
utilizatori poate fi achiziţionată la circa 750 USD, faţă de varianta Enterprise care
necesită peste 12.000 USD)
 Web
Este o soluţie indicată pentru administrarea bazelor de date ce urmează a fi
exploatate în mediul online. Poate fi utilizată atât pentru site-uri cu un trafic redus
cât şi pentru a gestiona baze de date ce deservesc portaluri web cu cerinţe ridicate
din punct de vedere al fluxurilor de date
 SQL Server Express
Reprezintă o soluţie oferită gratuit pentru utilizatorii ce doresc să realizeze
aplicaţii client pentru sisteme de mici dimensiuni sau pentru a învăţa. Oferă
funcţionalităţi de bază pentru administrarea datelor.
SQL Server Expres este componenta menită să înlocuiască Microsoft Desktop
Engine din versiunile mai vechi şi este integrată în Microsoft Visual Studio 2008.
Această soluţie poate fi oricând upgradată, dacă se doreşte utilizarea facilităţilor
din alte ediţii.
 Compact
Varianta Compact este o soluţie gratuită pentru gestionarea bazelor de date
destinate aplicaţiilor de tip desktop, a aplicaţiilor pentru mobile devices sau de tip
client web pe platforme Windows.

Componente, servicii şi instrumente


Pe lângă facilităţile de administrare a datelor şi a utilizatorilor bazei de date, în
funcţie de ediţia achiziţionată SQL Server prezintă o serie de servicii şi
instrumente extrem de utile.
Principalele componente ale serverului sunt următoarele:
 Database Engine – reprezintă nucleul sistemului de gestiune a bazelor de
date. Este practic componenta responsabilă cu memorarea, procesarea şi
asigurarea securităţii datelor.
 Analysis Services – reprezintă un set de instrumente ce pot fi utilizate
pentru analiza datelor, extrem de utile în dezvoltarea de aplicaţii specifice
domeniului business intelligence (aplicaţii OLAP şi data mining)
48 Baze de date – Introducere in SQL Server 2008

 Reporting Services - include componentele de server şi client necesare


pentru crearea şi gestionarea rapoartelor. Se pot realiza situaţii de raportare
în format tabular, matriceal, grafic sau orice alt format proiectat de
utilizatori.
 Integration Services – reprezintă un set de aplicaţii şi obiecte
programabile ce pot fi utilizate în copierea, mutarea sau transformarea
datelor între diferite destinaţii.

Principalele instrumente disponibile pentru managementul serverului sunt


următoarele:
 SQL Server Management Studio – Este un mediu integrat ce permite
accesul, configurarea şi administrarea componentelor SQL Server. Prin
interfaţa grafică uşor de utilizat permite şi utilizatorilor mai puţin
experimentaţi să realizeze operaţii simple precum crearea bazelor de date şi a
tabelelor, vizualizarea şi modificarea datelor, etc..
 SQL Server Configuration Manager – Este o aplicaţie ce permite realizarea
de configurări privind serviciile disponibile pe server, protocoalele utilizate de
server şi cele utilizate de clienţi.
 SQL Server Profiler – Reprezintă o interfaţă grafică prin intermediul căreia
pot fi monitorizate componentele Database Engine şi Analysis Services.
 Database Engine Tuning Advisor – este un instrument util pentru
optimizarea bazelor de date prin optimizarea indecşilor şi partiţiilor.
 Business Intelligence Development Studio – Este un mediu integrat de
dezvoltare pentru soluţiile bazate pe componentele Analysis Services,
Integration Services şi Reporting Services.
 Connectivity Components – permite instalarea şi configurarea
componentelor necesare pentru comunicarea client-server şi a altor librării
necesare (ODBC, OLE DB, etc.)
 SQL Server Books Online – reprezintă principala sursă de documentare
pentru SQL Server
Capitolul II SGBD SQL SERVER 49

2.3. Limbajul Transact SQL – principalele tipuri de


instrucţiuni
Transact-SQL (consacrat sub numele de T-SQL) este un dialect al limbajului SQL
(Structured Query Language) consacrat ca un limbaj neprocedural, declarativ ce a
fost implementat pentru prima data in anii ‘70 de firma IBM pentru gestionarea
bazelor de date organizate conform modelului relaţional .
Limbajul SQL a evoluat pe parcursul anilor şi, chiar dacă fiecare producător de
sisteme de gestiune a bazelor de date şi-a pus amprenta asupra aplicaţiilor proprii,
rezultând mai multe dialecte, setul principal de instrucţiuni nu diferă în mod
substanţial.
T-SQL permite reprezentă un instrument extrem de eficient atât pentru
manipularea cât si pentru actualizarea si administrarea bazei de date.
In funcţie de rolul pe care îl au instrucţiunile SQL pot fi grupate in:
 instrucţiuni pentru definirea datelor;
 instrucţiuni pentru manipularea datelor;
 instrucţiuni pentru selecţia şi sintetizarea datelor;
 instrucţiuni pentru gestiunea tranzacţiilor;
 instrucţiuni privind controlul accesului la date;

Observaţie: Întrucât prezenta lucrare are un aspect introductiv, ne vom


limita la exemplificarea rapidă, într-o formă simplificată a celor mai uzuale
instrucţiuni T-SQL.

Convenţii şi reguli de sintaxă Transact-SQL


Principalele convenţii T-SQL referitoare la scrierea sintaxelor ce folosesc cuvinte
cheie ale acestui limbaj sunt:

Convenţii în Descriere
sintaxe scrise
MAJUSCULE Cuvintele cheie T-SQL sunt scrise cu
majuscule în cadrul sintaxelor.
italic Parametrii care trebuie să fie furnizaţi de
către utilizator.
bold Nume de proceduri, tabele, coloane, etc.
| (bară Sugerează că în cadrul sintaxelor, dacă avem
verticală) cuvinte separate prin bara verticală, doar
una din posibilităţi trebuie folosită.
[ ] Elemente de sintaxă opţionale.
50 Baze de date – Introducere in SQL Server 2008

( ) Elemente de sintaxă obligatorii.


[ , ... n ] [, Indică faptul că elementul precedent de
... N] sintaxă poate fi repetat de un anumit număr
de ori, separat prin virgule.
[... n ] [... N] Indică faptul că elementul precedent de
sintaxă poate fi repetat de un anumit număr
de ori, fără a fi separat prin caractere
speciale.
[;] Indică sfârşitul unei clauze SQL. Este
opţional.
<label>:: = Asocierea unui nume pentru un bloc de
sintaxă. Acest nume poate fi folosit în mai
multe locuri în cazul în care blocul
respectiv se repetă.

Modul de referire al obiectelor SQL Server în frazele SQL


Sintaxa completă de referire a unui obiect SQL Server este:
[nume_server]. [nume_baza_date]. [nume_schema].nume_obiect
[nume_schema] – se referă la numele schemei (colecţia/proprietarul de obiecte)
de care aparţine obiectul la care se face referire. Schema de obiecte nu este acelaşi
lucru cu noţiunea de bază de date. În cazul în care nu se definesc scheme de către
utilizatori, schema implicită din care aparţin toate obiectele unei baze de date este
dbo (database owner). Schemele de obiecte sunt transmisibile între utilizatori.
[nume_baza_date] – numele bazei de date din care face parte obiectul.
[nume_server] – numele serverului unde este stocată baza de date, din care
face parte obiectul referit.
În cele mai multe cazuri, sintaxa de mai sus nu trebuie scrisă complet, ci este
suficient doar numele obiectului la care se face referire, sau se optează pentru o
scriere parţială a elementelor opţionale din sintaxă. Folosirea sintaxei complete de
referire a obiectelor sau folosirea unei sintaxe parţiale se impune ori de câte ori,
dacă s-ar scrie doar numele obiectului, nu ar fi suficient, pentru regăsirea acestuia.
Există obiecte pentru care este obligatorie scrierea schemei din care face parte,
chiar dacă numele său este unic în baza de date unde se foloseşte. De exemplu
invocarea unei funcţii definite de utilizator se face folosind cel puţin sintaxa:
nume_schema.nume_funcţie().
Exemplul 1:
SELECT * FROM Produs
Se vor selecta toate câmpurile din tabelul Produs al bazei de date curente (unde se
execută fraza SQL).
Capitolul II SGBD SQL SERVER 51

Exemplul 2:
SELECT * FROM Facturi.dbo.Produs
Se vor selecta toate câmpurile din tabelul Produs din baza de date Facturi, de pe
serverul curent unde se execută fraza SQL. Tabelul Produs aparţine de schema
dbo. Practic în această variantă de scriere, nu mai are importanţă baza de date de
unde se execută fraza SQL.
Exemplul 3:
SELECT * FROM [s-win-sql-cig\cig].Facturi.dbo.Produs
Se vor selecta toate câmpurile din tabelul Produs din baza de date Facturi, de pe
serverul [s-win-sql-cig\cig]. Practic în această variantă de scriere, nu mai
are importanţă baza de date de unde se execută fraza SQL şi nici serverul. Dacă
sistemul de securitate permite accesarea obiectelor de pe serverul [s-win-sql-
cig\cig], nu mai are importanţă serverul SQL unde se execută fraza SQL.
Exemplul 4:
SELECT * FROM dbo.numefunctie()
Se vor selecta toate coloanele returnate de funcţia numefunctie() care aparţine
de schema dbo. Aşa cum am amintit, la apelarea funcţiilor de finite de utilizator
se scrie obligatoriu şi schema de care aparţin.

Instrucţiunea INSERT
Permite adăugarea de noi înregistrări într-un tabel.
Sintaxă simplificată:
INSERT
{ nume_tabel | nume_view }
{ [ ( lista_coloane ) ]
{ VALUES ( [ ,...n] )}
}
| DEFAULT VALUES
Unde:
 nume_tabel– reprezintă numele tabelului în care se vor insera înregistrări;
 nume_view - reprezintă numele unui view actualizabil în care se vor insera
înregistrări;
 (lista_coloane) – reprezintă lista de câmpuri în care vor fi adăugate
valori. Se vor preciza între paranteze şi se vor separa prin virgulă.
 VALUES - este utilizată pentru a introduce o listă de valori specificate
pentru fiecare câmp în parte.
52 Baze de date – Introducere in SQL Server 2008

 DEFAULT – solicită adăugarea în noua înregistrare a valorilor implicite


definite la nivelul fiecărui câmp. În cazul câmpurilor pentru care nu au fost
definite valori implicite se va adăuga valoarea Null.

Instrucţiunea DELETE
Permite ştergerea de înregistrări.

DELETE
[ TOP ( expression ) [ PERCENT ] ]
FROM <tabel>]
[ WHERE { <criterii>] [; ]

Unde:
 TOP ( expression ) [ PERCENT ] –specifică un număr de
înregistrări sau un procent din numărul total de înregistrări ce vor fi şterse.
În cadrul instrucţiunilor Insert, Delete şi Update, înregistrările nu pot fi însă
ordonate.
 FROM <tabel> - specifică numele tabelului din care se vor şterge
înregistrările.
 WHERE <criterii> - permite specificarea uneia sau mai multor condiţii
pentru limitarea numărului de înregistrări ce vor fi şterse. Condiţiile vor fi
separate prin operatorii logici AND/OR. Lipsa clauzei WHERE va conduce
la ştergerea tuturor înregistrărilor.

Instrucţiunea UPDATE
Permite actualizarea înregistrărilor.

UPDATE
[ TOP ( expression ) [ PERCENT ] ] { <tabel|view> }
SET { nume_coloană = { expresie }
[ WHERE { <condiţii> [; ]

 TOP ( expression ) [ PERCENT ] – specifică un număr de


înregistrări sau un procent din numărul total de înregistrări ce vor fi
modificate. Înregistrările nu pot fi însă ordonate.
 <tabel|view> - numele tabelului sau obiectului de tip view ce conţine
datele de actualizat.
 În clauza SET se specifică numele câmpului ce va fi actualizat şi expresia
care stă la baza modificărilor.
Capitolul II SGBD SQL SERVER 53

 WHERE <condiţii> - specifică eventualele condiţii pentru limitarea


înregistrărilor ce vor fi actualizate. Condiţiile vor fi separate prin operatorii
logici AND/OR.

Instrucţiunea SELECT
Probabil cea mai utilizată instrucţiune, permite selecţia înregistrărilor din una sau
mai multe tabele, realizarea de calcule precum şi grupări de date.
Sintaxa instrucţiunii este complexă, dar poate fi sintetizată la următoarele clauze
de bază:

SELECT [DISTINCT] [[ TOP ( expression ) [ PERCENT ] ]


<listă_Selecţie> [INTO nume_tabel_nou]
FROM sursă_de_date
[ WHERE criterii ]
[ GROUP BY listă_câmpuri_grupare ]
[ HAVING condiţii pentru înregistrări grupate ]
[ ORDER BY listă_ordonare [ ASC | DESC ] ]
[ COMPUTE Funcţie_agregat BY nume_câmp]

 <listă_Selecţie> din clauza SELECT permite specificarea unei liste


de câmpuri şi/sau expresii calculate separate prin virgulă, ce vor fi afişate în
cadrul setului de rezultate. Lista poate fi înlocuită prin caracterul generic *,
substituţie ce va conduce la afişarea tuturor câmpurilor din sursa de date
precizată în clauza FROM.
 Precizarea opţiunii [DISTINCT] înaintea listei de selecţie va conduce la
eliminarea înregistrărilor duplicate din lista de rezultate.
 TOP ( expression ) [ PERCENT ] – specifică restricţionarea listei
de rezultate returnat de interogare la primele N înregistrări sau la un anumit
procent. Această opţiune trebuie corelată cu ordinea de sortare a setului de
rezultate impusă prin clauza [ORDER BY].
 INTO permite crearea unui tabel nou pe baza setului de rezultate.
 FROM – specifică sursa de date (sursa poate fi compusa din tabele, obiecte
tip view, alte instrucţiuni de tip SELECT sau funcţii ce returnează date în
format tabelar). Legăturile cheie primară – cheie externă între tabelele ce
compun sursa de date pot fi specificate în clauza FROM prin intermediul
operatorului JOIN sau în clauza WHERE.
 WHERE – permite definirea unor criterii de filtrare a datelor înainte de
grupare. Criteriile vor fi separate prin operatorii logici AND şi/sau OR.
54 Baze de date – Introducere in SQL Server 2008

 GROUP BY – conţine câmpurile după care se vor grupa datele.


 HAVING – criterii de filtrare a datelor rezultate în urma grupării. Criteriile
vor fi separate prin operatorii logici AND şi/sau OR. În clauza vor fi
specificate condiţiile ce implică utiliyarea funcţiilor de tip agregat (SUM,
COUNT, MIN, MAX, AVG).
 ORDER BY – conţine lista de câmpuri sau expresii utilizate pentru sortarea
datelor. Cuvintele cheie ASC sau DESC impun sortarea ascendentă sau
descendentă şi sunt opţionale. Implicit sortarea se realizează ascendent.
 COMPUTE – permite utilizarea unor funcţii de tip agregat pentru calculul de
totaluri sau subtotaluri. Exemple privind utilizarea acestei clauze sunt
prezentate în capitolul 4 al prezentei lucrări.

Interogări cu subinterogari
Utilizarea subinterogărilor se bazează pe faptul că o instrucţiune SELECT poate fi
utilizată pentru a returna una sau mai multe înregistrări, ce pot fi folosite într-o
altă instrucţiune INSERT, DELETE, UPDATE sau SELECT.
Atunci când subinterogarea returnează mai multe înregistrări, pentru comparaţii,
se pot utiliza cuvintele cheie:
 IN (testează apartenenţa unei valori la o mulţime)
 ANY ori SOME (compară o valoare cu oricare element al unei mulţimi)
 ALL (compară o valoare cu toate elementele unei mulţimi)
 EXISTS (verifică dacă o instrucţiune SELECT a returnat un rezultat)
Câteva exemple de utilizare a subinterogărilor sunt prezentate în subcapitolul 2.4
din prezenta lucrare.

Interogări de tip UNION


Aceste interogări permit reunirea înregistrărilor ce provin din tabele sau
instrucţiuni de selecţie diferite. O selecţie-reuniune conţine mai multe tabele sau
instrucţiuni SELECT asupra cărora se aplică operatorul UNION.
Toate sursele trebuie să conţină acelaşi număr de câmpuri iar câmpurile din
sursele de date să fie de acelaşi tip şi ordinea câmpurilor trebuie să fie aceeaşi.
Clauza Order By se poate specifica numai după ultima instrucţiune SELECT
Implicit, prin reuniune se elimină înregistrările duplicate. Pentru afişarea
înregistrărilor duplicate se foloseşte clauza ALL.
Sintaxa simplificată pentru acest tip de interogări este următoarea:
Capitolul II SGBD SQL SERVER 55

<Instrucţiune SELECT 1 >


UNION [ ALL ] <instrucţiune SELECT 2>
[ UNION [ ALL ] <instrucţiune SELECT 3> ] [ ...n ] ]

2.4. Exerciţii propuse şi rezolvate


Exemplele următoare sunt realizate pe baza de date proiectată în cadrul primului
capitol al prezentei lucrări.
Ex. 1: Să se obţină lista ordonată alfabetic a localităţilor în care firma are clienţi
organizaţi ca societăţi pe acţiuni.
Întrucât nu avem alte indicii, se va presupune că denumirea clientului se termiă în
şirul de caractere SA sai S.A.. În acest sesns vom utiliza operatorul LIKE îmreună
cu caracterul generic de înşocuire %. În T-SQL şirurile de caractere sunt încadrate
între caractere de tip apostrof.
SELECT DISTINCT LocalitateCl FROM Client
WHERE DenumireCl LIKE '%SA' OR DenumireCl LIKE '%S.A.'
ORDER BY LocalitateCl
Ex. 2: Să se obţină lista ordonată alfabetic cu numerele de contracte pentru cele
mai mari 5 contracte ca tarif negociat.
SELECT TOP 5 NrContract, TarifNegociat
FROM Contract
ORDER BY TarifNegociat DESC

Ex.3: Afişaţi toate informaţiile despre clienţii din afara României..


SELECT * FROM Client
WHERE TaraCl<>'Romania'
Ex. 4: Calculaţi pentru fiecare contract valoarea cu TVA si valoare cu TVA în
euro la un curs de 3.9 lei/euro, ordonând lista descrescător după valoare, iar pentru
contractele cu aceeaşi valoare, cronologic după DataContract.
SELECT NrContract, TarifNegociat*0.19 AS [Valoare],
TarifNegociat*1.19/3.9 AS [Valoare in Euro]
FROM Contract
ORDER BY Valoare DESC, DataContract ASC
Observaţie: Numele de coloane atribuite prin cuvântul cheie AS pot fi folosite în
clauza ORDER BY, dar nu pot fi utilizate în alte expresii din instrucţiunea
SELECT. În cazul de faţă, “valoare” este recunoscut în ORDER BY, nu şi în
expresia pentru calculul valorii în euro.
56 Baze de date – Introducere in SQL Server 2008

Ex. 5: Calculaţi câţi furnizori are firma în fiecare localitate si afişaţi lista ordonata
descrescător pentru localităţile in care exista mai mult de 20 de furnizori.
SELECT LocalitateCL, Count(CUIcL) AS [Total Clienti]
FROM Client
GROUP BY LocalitateCL
HAVING Count(CUIcL)>20
ORDER BY [Total Clienti]
Ex. 6: Calculaţi Valoarea totala a contractelor pe fiecare luna din anul 2008 şi
afişaţi rezultatele doar pentru lunile in care au existat mai mult de 3 contracte.
SELECT Month(DataContract) AS Luna,
Sum(TarifNegociat) As Total
FROM Contract
WHERE DataContract BETWEEN '1/1/2008' AND '12/31/2008'
GROUP BY Month(DataContract)
HAVING Count(TarifNegociat)>3
Ex. 7: Să se obţină lista o listă a contractelor încheiate după 1 ianuarie 2008. Se
va specifica şi numele şi ţările clienţilor contractanţi.
--Soluţia 1 (exprimarea legăturii în clauza FROM)
SELECT NrContract, DataContract, DenumireCl, ŢaraCl
FROM Client INNER JOIN Contract
ON Client.CUICl=Contract.CUICl
WHERE DataContract >= '1/1/2008'

sau
--Soluţia 2 (exprimarea legăturii în clauza WHERE)
SELECT NrContract, DataContract, DenumireCl, ŢaraCl
FROM Client, Contract
WHERE DataContract >= '1/1/2008'
AND Client.CUICl=Contract.CUICl

Ex. 8: Să se modifice cerinţa precedentă pentru a calcula câte contracte s-au


încheiat pe fiecare ţară.

SELECT NrContract, DataContract, DenumireCl, ŢaraCl


FROM Client INNER JOIN Contract
ON Client.CUICl=Contract.CUICl
WHERE DataContract >= '1/1/2008'
ORDER BY TaraCL
COMPUTE Count(NrContract) BY TaraCL
Capitolul II SGBD SQL SERVER 57

Ex. 9: Care sunt ofertele care nu au fost contractate niciodată ?


SELECT CodOferta, DescriereOferta
FROM Oferta
WHERE CodOferta NOT IN (SELECT CodOferta FROM Contract)
Ex. 10 : Să se afişeze lista cu cele numerele contractelor pentru care s-a negociat
cel mai mare tarif.
SELECT NrContract, TarifNegociat
FROM Contract
WHERE TarifNegociat>= ALL(SELECT TarifNegociat FROM Contract)
Ex 11: Să se şteargă din tabelul Contract toate contractele încheiate în 2007 cu
clienţi din Spania.
DELETE FROM Contract
WHERE Year(DataContract)=2007
AND CUICl IN(SELECT CUICl FROM Client WHERE TaraCL =
‘Spania’)
Ex 12: Să se micşoreze cu 10% tarifele negociate în luna ianuarie 2008 cu clienţii
din Iaşi şi Sibiu.
UPDATE Contract SET TarifNegociat = TarifNegociat*0.9
WHERE DataContract BETWEEN ‘1/1/2008’ AND ‘31/1/2008’
AND CUICL IN(SELECT CUICl FROM Client
WHERE ORAS IN(‚Iasi’,’Sibiu’));
60 Baze de date – Introducere in SQL Server 2008

CAPITOLUL 3
DESCRIEREA DATELOR
Descrierea datelor se referă la operaţiile de creare a bazei de date, definirea
câmpurilor tabelelor, stabilirea cheilor primare, implementarea eventualelor
restricţii şi descrierea legăturilor cheie primară-cheie externă prin intermediul
diagramelor de relaţii
Toate aceste operaţii pot fi realizate prin instrucţiuni Transact SQL sau prin
intermediul interfeţei grafice oferite de Microsoft SQL Server Management
Studio1.
De exemplu, pentru crearea unei baze de date se poate utiliza comanda T-SQL
CREATE DATABASE, iar pentru ştergere DROP DATABASE.
Exemplu:
-- crearea bazei de date
CREATE DATABASE Test1
-- ştergerea bazei de date
DROP DATABASE Test1
Crearea unei baze de date se
poate realiza la fel de simplu
selectând secţiunea Databases
în panoul Object Explorer şi
alegând opţiunea New
Database din meniul
contextual.

Întrucât interfaţa SQL Server Management Studio este extrem de uşor de utilizat
şi prezintă o multitudine de facilităţi pentruu descrierea datelor, în capitolul de
faţă nu se va insista pe comenzile T-SQL, ce pot substitui aceste facilităţi.

1
Aplicaţia SQL Server Management Studio poate fi accesată prin intermediul meniului Start
Programs Microsoft SQL Server  SQL Server Management Studio
Capitolul III DESCRIEREA DATELOR 61

3.1. Crearea tabelelor


Pentru a crea un tabel într-o bază de date
SQL Server se va deschide ierarhia de
foldere aferentă bazei de date în panoul
Object Explorer, se va selecta secţiunea
Tables, şi din meniul contextual se va alege
opţiunea New Table…
În fereastra de proiectare a tabelului se vor specifica numele câmpurilor şi tipul de
date pentru fiecare câmp.

Figura 1 – Fereastra de proiectare a tabelului

Cheia primară se poate stabili selectând câmpul cheie şi, din meniul contextual, se
va alege opţiunea Primary Key.
Pentru câmpurile pentru care nu se doreşte să fie acceptată valoare Null în
momentul introducerii datelor se va preciza acest lucru în coloana Allow Nulls.
După completarea numelor de câmpuri şi a tipurilor de date, fereastra de
proiectare a tabelului se poate închide, precizând un nume pentru tabel în
momentul în care se solicită salvarea acestuia.
Tabelele create se vor regăsi în folderul
Tables al bazei de date. Prin apelarea
meniului contextual pe un tabel selectat
sunt disponibile, printre altele, următoarele
opţiuni:
 Design (permite redeschiderea
tabelului în modul de proiectare)
 Select Top 1000 rows (va afişa
primele 1000 înregistrări)
 Edit Top 200 rows (va deschide
tabelul [n modul de editare/adăugare
afişând primele 200 de înregistrări)
Principale tipuri de date din SQL Server sunt prezentate în tabelul următor:
62 Baze de date – Introducere in SQL Server 2008
Capitolul III DESCRIEREA DATELOR 63
64 Baze de date – Introducere in SQL Server 2008

Proprietăţi la nivelul câmpurilor


Pe lângă tipul de date, pentru fiecărui câmp dintr-un tabel îi pot fi atribuite
proprietăţi specifice. Câteva dintre cele mai utilizate dintre acestea sunt
exemplificate în continuare:
 Default value or binding – permite definirea unei valori implicite cu care va
fi completat câmpul atunci când nu se specifică nicio altă valoare.

Atribuirea unei
valori implicite
pentru câmpul
Studii

Figura 2 Setarea unei valori implicite.

 Computed item specification – permite crearea de coloane calculate in cadrul


tabelelor prin specificarea unei formule de calcul.

Atribuirea unei
formule de calcul
pentru câmpul
TVA

Figura 3 Câmpuri calculate


Capitolul III DESCRIEREA DATELOR 65

 Identity specification permite crearea unui contor pe un câmp de tip numeric


prin modificarea următoarelor proprietăţi:
 Is Identity: YES
 Identity Increment: Pasul de incrementare
 Identity seed: specificarea bornei de pornire
Observaţie: Valorile câmpului vor fi atribuite automat si nu pot fi modificate de
utilizator.

Crearea unui câmp cu


autonumerotare pentru
NrContract

Figura 4 Identity specification

3.2. Indexarea tabelelor


Un index într-o bază de date este foarte asemănător celui utilizat în cazul cărţilor
pentru regăsirea rapidă a anumitor informaţii.
Un index este memorat ca o structura distinctă ce este asociată unui tabel în
scopul micşorării timpului de răspuns în momentul interogării tabelului.
Un index conţine chei alcătuite din realizărilor unuia sau mai multor câmpuri
memorate într-o structură ce permite regăsirea rapida pe mediul de stocare a
înregistrărilor asociate valorilor cheie.
66 Baze de date – Introducere in SQL Server 2008

Avantaje
Indecşii au rolul de a reduce volumul de date ce trebuie citit pentru ca o interogare
să returneze setul de rezultate solicitat întrucât căutarea pe baza unui index
presupune consultarea unor valori cheie ordonate şi nu parcurgerea tuturor
înregistrărilor din tabele..
Prin crearea de indecşi conform cu cele mai frecvente cereri de interogare se pot
ameliora în mod semnificativ performanţele obţinute la consultarea bazei.
Nu în ultimul rând, câmpurile index pot fi utilizate pentru a impune unicitatea
realizărilor pentru un anumit câmp din baza de date.
Proiectarea superficială a indecşilor sau lipsa acestora vor conduce la performanţe
scăzute în exploatarea bazelor de date. Crearea indecşilor trebuie însă sa pună in
balanţa avantajele conferite de obţinerea mai rapidă a rezultatelor pe de o parte, şi
creşterea spaţiului de stocare necesar, de cealaltă parte.
Indecşii creaţi pe câmpuri ce permit domenii largi de valori şi cei creaţi pe baza
mai multor câmpuri vor solicita spaţiu suplimentar de stocare.
Observaţie: Indecşii pot fi modificaţi sau steri fără a modifica modelul bazei de
date. SQL Server permite optimizarea interogărilor prin selecţia celui mai eficient
index de căutare dintre cei disponibili.

Tipuri de indecşi
Principalele tipuri de indecşi utilizaţi in SQL Server sunt:
 Indecşi de tip Cluster ( in acest caz datele din tabel sunt memorate şi sortate
conform valorilor indexului. Intr-un tabel nu poate exista decât un index de tip
cluster. In cazul intr-un tabel nu a fost definit un index de tip cluster, datele din
tabel sunt memorate fără nici un criteriu de ordonare.
 Index de tip Non-Cluster: Acest tip de indecşi au o structura separata de
înregistrările tabelului si nu afectează în vreun fel modul de ordonare în care
sunt memorate acestea. Doar valorile din cadrul indexului sunt ordonate, nu si
cele din tabelul asociat. Fiecare valoare a indexului are alocat un indicator ce
permite regăsirea rapidă a înregistrărilor aferente. Un indicator de la o valoare
a indexului către o înregistrare este numit “pointer” sau “row locator”.
 Full Text Index reprezintă un tip special de indecşi gestionaţi prin
intermediul serviciului Microsoft Full-Text Engine for SQL Server
(MSFTESQL). Acest tip oferă avantaje în cazul interogărilor complexe asupra
datelor de tip şir de caractere.
 Indecşi de tip XML
Capitolul III DESCRIEREA DATELOR 67

Crearea indecşilor
Indecşii pot fi creaţi prin comenzi SQL sau, mai simplu, utilizând interfaţa SQL
Server Management Studio.
In momentul stabilirii cheii primare a unui tabel este creat automat un index de tip
cluster. In momentul in care un câmp este creat cu clauza UNIQUE i se asociază
automat un index de tip non cluster.
Alţi indecşi pentru un tabel se pot adăuga deschizând tabelul în modul de
proiectare şi apelând din meniul Table Designers  Indexes/Keys…

Figura 5 Fereastra Indexes/keys permite adăugarea de noi indecşi2

3.3. Implementarea restricţiilor


În SQL Server există mai multe mecanisme prin care se pot implementa restricţii
asupra datelor. Cele mai utilizate modalităţi de implementare a restricţiilor sunt:
 Restricţii la nivelul tabelelor (check constraints)
 Restricţiile de integritate referenţială
 Declanşatorii (proceduri de tip trigger)

2
Fereastra indexes-keys poate fi accesata prim intermediul meniului Table Designer.
68 Baze de date – Introducere in SQL Server 2008

3.3.1. Restricţii la nivelul tabelelor (check constraints)

La nivelul tabelelor se pot stabili reguli de validare pentru fiecare câmp în parte
(de exemplu se poate impune apartenenţa valorilor câmpului la un domeniu de
valori), dar şi reguli de validare bazate pe compararea valorilor mai multora dintre
câmpurile ale tabelului.
Pentru a implementa astfel de reguli, se va deschide tabelul în modul de proiectare
şi din meniul Table Designers se va selecta opţiunea Check Constraints.
Figura următoare ilustrează fereastra Check Constraints. Proprietăţile ce trebuie
completate sunt Name (numele restricţiei) si Expression (condiţia ce trebuie
îndeplinită).

Figura 6 Caseta Check Constraints

Restricţiile la nivelul tabelelor sunt vizibile în fereastra Object Explorer, în


secţiunea Check Constraints aferentă tabelului pentru care au fost definite.
3.3.2. Integritatea referenţială

Integritatea referenţială presupune ca realizările cheii externe să se regăsească în


mulţimea realizărilor cheii primare sau să aibă valoarea Null şi a fost detaliată în
primul capitol al acestei cărţi.
Cea mai simplă metodă de stabilire a restricţiilor de integritate este prin
intermediul diagramelor.
Pentru a realiza o diagramă se va selecta secţiunea Database Diagrams din baza
de date şi, din meniul contextual se va alege opţiunea New Database Diagrams.
Capitolul III DESCRIEREA DATELOR 69

Realizarea unei diagrame presupune tractarea cu mouse-ul a câmpului cheie


primară peste câmpul cheie externă unei diagrame presupune tractarea câmpului
cheie primară peste câmpul cheie externă din tabelul corespondent.
După stabilirea unei legături cheie primară – cheie externă, proprietăţile legăturii
se pot vizualiza şi modifica prin intermediul casetei Properties (se va selecta din
meniul View – Properties)

Figura 7 Modificarea proprietăţilor unei legături

Dacă se doreşte realizarea unei legături cheie primară – cheie externă fără a
impune integritatea referenţială, se va modifica proprietatea Enforce foreign key
constraint (în cadrul diagramei respectiva legătura va fi trasată cu linie punctată).
În fereastra de proprietăţi a diagramei se pot specifica pentru fiecare legătură
cheie primară-cheie externă dacă se doreşte ştergerea sau actualizarea în cascadă
(în cadrul secţiunii INSERT AND UPDATE specification)

3.3.3. Proceduri de tip trigger (Declanşatori)

Un declanşator este un tip special de procedură care se execută atunci când în


baza de date survine o instrucţiune de modificare, ştergere sau adăugare
(UPDATE, DELETE, INSERT).
Declanşatorii sunt creaţi prin intermediul comenzii CREATE TRIGGER si sunt
proceduri ataşate tabelelor. Pentru un tabel este posibil sa se definească mai mulţi
declanşatori.
Declanşatori sunt executaţi după o instrucţiune de actualizare, o singura dată,
indiferent de numărul de înregistrări afectate de respectiva instrucţiune in cadrul
tabelei.
Restricţiile de integritate referenţială au prioritate in faţa declanşatorilor. Dacă o
operaţie de actualizare are ca rezultat încălcarea integrităţii referenţiale, se
anulează execuţia declanşatorilor.
70 Baze de date – Introducere in SQL Server 2008

În general, pentru a evita procesarea unui volum mare de date atunci când o
operaţie de actualizare nu a produs modificări asupra datelor, în cadrul triggerelor
se testează ce rezultate au avut asupra datelor operaţiile care au declanşat
triggerul.
Astfel, cu ajutorul funcţiei UPDATE() se poate determina in interiorul unei
proceduri trigger ce coloane urmează a fi actualizate. (Sintaxa este
UPDATE(nume_câmp) si se poate utiliza in contextul unei structuri de tip IF
pentru a evalua realizările câmpului care au suferit modificări).
O altă modalitate de a determina daca în urma unei comenzi au fost afectate
înregistrări este utilizarea funcţiei @@ROWCOUNT care returnează numărul de
înregistrări ce au fost afectate de o interogare.
Declanşatorii sunt executaţi imediat după operaţia de actualizare, dar înainte ca
aceasta sa fie încheiată in baza de date, utilizându-se tehnica tranzacţionării
operaţiilor.
Conceptul de tranzacţie
Tranzacţionarea este o tehnică prin care este asigurată coerenţa bazei de date în
cursul actualizării acesteia.
O tranzacţie reprezintă un set de operaţii de actualizare care, fie se execută toate,
fie nici una. Operaţiile cuprinse într-o tranzacţie nu pot fi executate parţial: dacă
una dintre ele eşuează atunci toate celelalte sunt anulate.
O tranzacţie poate fi:
 Salvată (commited) – când toate operaţiile tranzacţiei au fost încheiate cu
succes iar baza de date este actualizată cu noile modificări.
 Derulată înapoi (rollback) – toate operaţiile tranzacţionate sunt anulate iar
baza de date este restaurată la starea dinaintea tranzacţiei.
Într-un mediu multiuser, datele actualizate prin intermediul unei tranzacţii nu sunt
vizibile altor utilizatori decât numai la salvarea tranzacţiei.
Execuţia unei operaţii de modificare/adăugare/ştergere şi a procedurii trigger pe
care o declanşează pot fi considerate o singura tranzacţie.
Dacă declanşatorul generează o eroare se va executa o comandă ROLLBACK
TRANSACTION şi toate operaţiile sunt anulate.

Tabele temporare ce însoţesc operaţiile de actualizare


In programarea triggerelor se pot utiliza două tabele logice ce însoţesc
tranzacţiile: tabelul INSERTED şi tabelul DELETED.
Capitolul III DESCRIEREA DATELOR 71

 Pentru operaţiile de tip Insert tabelul INSERTED memorează temporar


înregistrările ce urmează a fi adăugate în baza de date.
 Pentru operaţiile de tip Delete tabelul DELETED memorează temporar
înregistrările ce urmează a fi şterse.
 Pentru operaţiile de tip Update tabelul DELETED memorează temporar
vechile valori din înregistrările ce urmează a fi modificate, iar tabelul
INSERTED noile valori ale acestor înregistrări.
Aceste tabele nu pot fi modificate. Ele sunt utilizate doar in scop de consultare,
pentru a valida sau a anula (ROLLBACK) operaţiile de actualizare, in funcţie de
valorile ce urmează a fi modificate.

Sintaxe si exemple
Instrucţiunea prin care se poate crea un declanşator este CREATE TRIGGER, iar
instrucţiunea prin care se poate modifica un declanşator este ALTER TRIGGER.
(sintaxa este similară instrucţiunii CREATE TRIGGER).
Instrucţiunea prin care se poate şterge un declanşator este:
DROP TRIGER nume_trigger

Sintaxa simplificată a instrucţiunii CREATE TRIGGER este următoarea:


CREATE TRIGGER nume_trigger ON nume_tabel {FOR|INSTEAD OF}
{INSERT|UPDATE|DELETE} AS Instrucţiuni SQL
Observaţii:
 In loc de cuvântul cheie FOR se poate utiliza, cu aceleaşi rezultate AFTER

 Dacă, în loc de FOR se utilizează INSTEAD OF, instrucţiunile SQL din cadrul
triggerului înlocuiesc operaţia de tip INSERT, UPDATE sau DELETE care nu
se mai execută.

Exemplul 1
Fie tabelul ANGAJATI (CodAngajat, Nume, Functie, Salariu, CodDepartament)
Realizaţi un trigger pentru operaţia de ştergere care sa nu permită ştergerea
angajaţilor care au CodDepartament=1.
72 Baze de date – Introducere in SQL Server 2008

CREATE TRIGGER [Stergere_angajat] ON [ANGAJATI] FOR DELETE AS


IF @@rowcount=0 RETURN
IF exists(SELECT CodDepartament FROM DELETED
where coddepartament=1)
BEGIN
RAISERROR ( 'NU SE POT STERGE DIN DEPARTAMENTUL 1', 18, 1)
ROLLBACK TRANSACTION
END
Observaţii:
 In prima instrucţiune IF s-a verificat dacă există vreo înregistrare afectată.
In cazul in care numărul de înregistrări afectate este zero se va renunţa la
parcurgerea celorlalte instrucţiuni prin executarea comenzii RETURN
 In cea de-a doua instrucţiune IF s-a testat dacă in tabelul DELETED (ce
conţine înregistrările ce vor fi şterse, există vreun angajat de la
departamentul 1. Clauza EXISTS permite evaluarea instrucţiunii SELECT
dintre paranteze în sensul determinării existenţei unor rezultate ce satisfac
condiţiile specificate).
 In cazul in care s-a stabilit că există in tabelul DELETED angajaţi din
departamentul 1, urmează a fi executate instrucţiunile ROLLBACK
TRANSACTION si RAISERROR. Întrucât sunt mai multe instrucţiuni de
executat, acestea au fost încadrate intr-un bloc de tip BEGIN … END.
 Instrucţiunea ROLLBACK TRANSACTION conduce la anularea operaţiei
de ştergere.
 Instrucţiunea RAISERROR permite afişarea unui mesaj de eroare.
Sintaxa simplificata pentru aceasta instrucţiune este:
RAISE ERROR (‘mesaj de afişat’, nivel de severitate al erorii,
cod de identificare)
 Nivelul de severitate poate fi specificat între 0 si 25.
 Codul de identificare poate fi atribuit de utilizator intre 0 si 255 şi e folosit
pentru depanarea procedurilor.

Exemplul 1
Realizaţi un trigger care sa nu permită modificarea salariului unui angajat la o
valoare mai mica decât salariul pe care îl are în momentul actualizării.
Capitolul III DESCRIEREA DATELOR 73

CREATE TRIGGER MODIFICARE_SAL ON ANGAJATI


FOR UPDATE AS

IF EXISTS(SELECT *
FROM DELETED, INSERTED
WHERE DELETED.CodAngajat=INSERTED.CodAngajat
AND DELETED.SALARIU>INSERTED.SALARIU)
BEGIN
RAISERROR('NU SE POATE SUB MINIM', 18,2)
ROLLBACK TRANSACTION
END
Observaţii:
Instrucţiunea SELECT operează atât pe tabelul INSERTED, unde sunt memorate
temporar noile valori ale salariilor, cât şi pe tabelul DELETED unde se pot regăsi
valori ce urmează a fi înlocuite. Sunt comparate salariile din DELETED cu cele
din INSERTED pentru acelaşi angajat si, daca există măcar un caz în care noua
valoare a salariului este mai mică, se anulează tranzacţia prin instrucţiunea
ROLLBACK TRANSACTION.

Exemplul 3
Realizaţi un trigger care sa adauge in tabelul Arhiva_Angajaţi toţi angajaţii ce vor
fi şterşi din tabelul Angajaţi. Tabelul Arhiva_Angajaţi are următoarele câmpuri:
CodA, Nume, DataPlecare. DataPlecare se va completa automat cu data la care a
fost şters angajatul

CREATE TRIGGER Arhivare on Angajati FOR DELETE AS


BEGIN
INSERT INTO Arhiva_Angajati(coda, nume, DataPlecare)
SELECT CodAngajat, Nume, getdate()as DataPlecare
FROM DELETED
END
Declanşatorii creaţi pot fi vizualizaţi in
baza de date in secţiunea Triggers a
tabelului pentru care au fost realizaţi.
Un trigger poate fi dezactivat temporar
prin intermediul meniul contextual.
76 Baze de date – Introducere in SQL Server 2008

CAPITOLUL 4
INTEROGAREA DATELOR

4.1. Funcţii predefinite SQL Server


Limbajul Transact SQL pune la dispoziţia programatorilor un set complet de
funcţii ce facilitează realizarea de calcule complexe sau conversii de date.
În continuare este prezentată o selecţie a celor mai utile funcţii grupate pe
categorii:

Funcţii pentru conversia între tipuri de date


 Funcţia CONVERT
Converteşte rezultatul unei expresii într-un anumit timp de date.
Sintaxa:
CONVERT (tip_date_rezultat [(lungime)], expresie [,
stil_conversie])
 tip_date_rezultat – tipul de date în care se converteşte expresie
 lungime – lungimea tipului de date, doar pentru tipurile de date care
suportă
 expresie – orice expresie admisibila SQL, al cărei rezultat se va converti în
tip_date_rezultat
 stil_conversie – cod folosit atunci când se doreşte utilizarea unor modele
de conversie SQL;
Câteva coduri uzuale pentru stil_conversie pentru conversia in/din data/ora:

Cod Format data/ora rezultat


101 mm/dd/yyyy
103 dd/mm/yyyy
108 hh:mi:sec
113 dd mon yyyy hh:mi:ss:mmm

Exemplul 1

SELECT getdate()
Capitolul IV INTEROGAREA DATELOR 77

Se returnează data serverului în formatul implicit. Aşa cum se observă din


imaginea anterioară, formatul implicit al datei (componenta calendaristică) este
yyyy-mm-dd. Atunci când se doreşte introducerea unei date calendaristice într-o
bază de date sau obţinerea unei date calendaristice din baza de date, în mod
implicit, se va utiliza formatul datei serverului. În cazul în care acest format nu
este pe placul utilizatorului, se poate schimba formatul de introducere/afişare cu
ajutorul funcţiei CONVERT. De asemenea trebuie să amintim că dacă nu se
utilizează funcţia CONVERT şi utilizatorul care introduce date calendaristice nu
cunoaste formatul serverului, se pot memora date eronate sau se pot obţine erori.
Utilizând funcţia CONVERT se poate obţine data calendaristică şi în formatul
dd/mm/yyyy, utilizând codul 103 pentru parametrul stil_conversie.

SELECT CONVERT(char(10), getdate(), 103)

Utilizând codul 113 pentru parametrul stil_conversie, se obţine:

SELECT CONVERT(char(30), getdate(), 113)

Exemplul 2
În acest exemplu vom prezenta modul cum se specifică corect o dată
calendaristică introdusă de utilizator în formatul propriu (dd/mm/yyyy), dar
transformată cu ajutorul funcţiei CONVERT:

SELECT CONVERT(date, '10/12/2008', 103)

Dacă nu s-ar fi utilizat parametrul stil_conversie, varianta implicită de


introducere a datei calendaristice ar fi fost eronată (în cazul în care formatul
implicit nu este cunoscut sau este ignorat de utilizator):

SELECT CONVERT(date, '10/12/2008')

În acest caz se observă că data calendaristică 10.decembrie.2008 a fost interpretată


ca fiind 12.octombrie.2008.
Pentru a preîntâmpina erorile care pot fi generate de manipularea greşită a datelor
calendaristice, recomandăm folosirea funcţiei CONVERT (cu parametrul
stil_conversie) în toate situaţiile în care se utilizează date calendaristice în alt
format decât cel al serverului.
78 Baze de date – Introducere in SQL Server 2008

Exemplul 3
Utilizarea funcţiei CONVERT pentru transformarea datelor de natura numerică:
SELECT TOP 5 NrContract, TarifNegociat,
CONVERT(decimal(20,1), TarifNegociat) As TarifOZecimala,
CONVERT(numeric(20,2), TarifNegociat) As Tarif2Zecimal2
FROM Contract FROM Contract

Exemplul 4
Utilizarea componentei timp, din data calendaristică:

SELECT CONVERT(time, getdate())

 Funcţia CAST
Are acelaşi rolul ca şi funcţia convert (converteşte rezultatul unei expresii într-
un anumit timp de date) . Sintaxa este mai simplă şi nu prezintă toate facilităţile
funcţiei convert:
CAST(Expresie AS Tip de data)

Funcţii pentru date de tip calendaristic


 Funcţia GETDATE()
Returnează data şi ora serverului.

 Funcţia DAY
Returnează ziua dintr-o dată calendaristică.
Sintaxa:
DAY (expresie)
Capitolul IV INTEROGAREA DATELOR 79

 Funcţia MONTH
Returnează luna dintr-o dată calendaristică.
Sintaxa:
MONTH(expresie)

 Funcţia YEAR
Returnează anul dintr-o dată calendaristică.
Sintaxa:
YEAR(expresie)

Exemplu
SELECT top 5 day(DataContract) as Zi,
month(DataContract) as Luna,
year(DataContract) as An
FROM Contract

 Funcţia DATEPART
Extrage dintr-o data calendaristica/ora o anumită informatie calendaristica,
identificata printr-un cod.
Sintaxa:
DATEPART (informaţie_calendaristica, expresie_data/ora)
În tabelul următor sunt prezentate câteva valori uzualepentru parametrul
informatie_calendaristica:

Cod
Rezultat
informatie_calendaristica
year An
month Luna
day Zi
dayofyear Numar zi din an
quarter Trimestru
80 Baze de date – Introducere in SQL Server 2008

week Numar saptamana din an


hour Ora
minute Minutele
second Secundele

Exemplu
Extragerea din data curentă a serverului a mai multor informaţii calendaristice.

SELECT DATEPART(DAY, getdate()) as ZI,


DATEPART(MONTH, getdate()) as LUNA,
DATEPART(YEAR, getdate()) as AN,
DATEPART(DAYOFYEAR, getdate()) as NRZI,
DATEPART(QUARTER, getdate()) as TRIM,
DATEPART(WEEK, getdate()) as SAPT,
DATEPART(HOUR, getdate()) as ORA,
DATEPART(MINUTE, getdate()) as MINUT,
DATEPART(SECOND, getdate()) as SEC

 Funcţia DATEDIFF
Sintaxa:
DATEDIFF(informaţie_calendaristica, data/ora start, data/ora final)

Calculează diferenţa dintre două date calendaristice (data/ora final şi data/ora


start). Rezultatul este furnizat într-un anumit tip de informaţie calendaristica,
identificată printr-un cod (valorile uzuale sunt prezentate la funcţia DATEPART.
Exemplu
Calcularea diferenţei între data 1/1/2009 şi 15/6/2006 exprimată în mai multe
tipuri de informaţie calendaristică:

SELECT
DATEDIFF(DAY, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as ZILE,
DATEDIFF(MONTH, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as LUNI,
DATEDIFF(YEAR, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as ANI,
DATEDIFF(DAYOFYEAR, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as NRZILE,
Capitolul IV INTEROGAREA DATELOR 81

DATEDIFF(QUARTER, CONVERT(datetime, '15/6/2006', 103),


CONVERT(datetime, '1/1/2009', 103)) as TRIM,
DATEDIFF(WEEK, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as SAPT,
DATEDIFF(HOUR, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as ORE,
DATEDIFF(MINUTE, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as MINUT,
DATEDIFF(SECOND, CONVERT(datetime, '15/6/2006', 103),
CONVERT(datetime, '1/1/2009', 103)) as SECUNDE

 Funcţia DATEADD
Sintaxa:
DATEADD (informaţie_calendaristica, n, data/ora start)
Calculează o data calendaristică (viitoare sau anterioară), plecând de la o anumită
dată calendaristică (data/ora start) la care se adăuga un n (in format informaţie
calendaristică aşa cum a fost prezentat la funcţia DATEPART). În cazul în care
se doreşte calcularea unei date anterioare faţă de data luată ca bază (data/ora
start), argumentul n se precizează cu minus.
Exemplu
Calcularea datei de peste 1000 zile faţă de data de 1/1/2009 şi a datei de acum
10000 zile faţă de 1/1/2009.

SELECT
DATEADD(DAY, 10000, CONVERT(datetime, '1/1/2009', 103))
as DataViitoare,
DATEADD(DAY, -10000, CONVERT(datetime, '1/1/2009', 103))
as DataAnterioara
82 Baze de date – Introducere in SQL Server 2008

Funcţii pentru prelucrarea şirurilor de caractere


 Funcţia LTRIM
Sintaxa:
LTRIM(expresie)
Elimină spaţiile nesemnificative de la stânga unei expresii de tip şir de caractere.
 Funcţia RTRIM
Sintaxa:
RTRIM(expresie)
Elimină spaţiile nesemnificative de la dreapta unei expresii de tip şir de caractere
Exemplu
SELECT LTRIM(' BAZE DE DATE') as SpatiiStanga,
RTRIM('BAZE DE DATE ') as SpatiiDreapta,
LTRIM(RTRIM(' BAZE DE DATE ')) as SpatiiStDr

 Funcţia LEN
Sintaxa:
LEN(expresie)
Returnează numărul de caractere dintr-o expresie.

 Funcţia LOWER
Sintaxa:
LOWER(expresie)
Returnează şirul de caractere din expresie scris cu litere mici.

 Funcţia UPPER
Sintaxa:
UPPER(expresie)
Returnează şirul de caractere din expresie scris cu litere mari.
Capitolul IV INTEROGAREA DATELOR 83

Exemplu

SELECT TOP 10 NumeAng, PrenumeAng,


LOWER(NumeAng) as NumeLitereMici,
UPPER(NumeAng) as NumeMajuscule,
LEN(PrenumeAng) as NrLiterePrenume
FROM Angajat

 Funcţia LEFT
Sintaxa:
LEFT(expresie, n)
Returnează n caractere de la stânga unei expresii şir de caractere

 Funcţia RIGHT
Sintaxa:
RIGHT(expresie, n)
Returnează n caractere de la dreapta unei expresii şir de caractere

 Funcţia SUBSTRING
Sintaxa:
SUBSTRING (expresie, start, n)
Returnează n caractere dintr-o expresie şir de caractere, începând de la poziţia
start.
Exemplu

SELECT TOP 10 NumeAng,


LEFT(LTRIM(NumeAng), 4) as PatruStanga,
84 Baze de date – Introducere in SQL Server 2008

RIGHT(RTRIM(NumeAng), 4) as PatruDreapta,
SUBSTRING(NumeAng, 3, 2) as DouaDupaTrei
FROM Angajat

Alte funcţii matematice

 Funcţia POWER
Returnează valoarea unui număr ridicat la o putere.
Sintaxa
POWER(expresie numerică,puterea)

Exemplu:

SELECT POWER(2,3) as [ 2 LA PUTEREA 3]

 Funcţia ABS

Funcţia ABS returnează valoarea absolută a unei expresii numerice.


Sintaxa:
ABS (expresie numerica)
Exemplu:

SELECT ABS (123) AS [Ex1], ABS(-23) AS [Ex2]


Capitolul IV INTEROGAREA DATELOR 85

 Funcţia SIGN
Sintaxa: SIGN(expresie numerică)
Returnează una dintre valorile:
 -1 dacă expresia este negativă
 0 dacă expresia este zero
 +1 dacă expresia este pozitivă
Exemplu:
SELECT SIGN(-10) AS EX1, SIGN(0) AS EX2 , SIGN(100) AS EX3

 Funcţiile CEILING, FLOOR şi ROUND


Sunt funcţii utilizate pentru rotunjirea valorilor numerice. Diferenţele dintre aceste
funcţii sunt redate în exemplul următor:

SELECT CEILING (23.1) AS EX1, CEILING(23.8) AS EX2,


FLOOR(23.1) AS EX3, FLOOR(23.8) AS EX4,
ROUND(23.1,0) AS EX5, ROUND(23.8,0) AS EX5

Funcţii de tip agregat


Prin intermediul acestor funcţii se pot realiza calcule la nivelul grupurilor de
înregistrări. Sintaxele funcţiilor ce permit calculul mediei, sumei, minimului,
maximului sau a numărului de valori la nivelul unui grup de înregistrări sunt
următoarele:

AVG ( [ DISTINCT ] expresie )


SUM ( [DISTINCT ] expresie )
MIN (expresie)
MAX (expresie)
COUNT ([ DISTINCT ] expresie | * } )
86 Baze de date – Introducere in SQL Server 2008

Funcţiile de tip agregat pot fi utilizate in expresii într-o frază SQL în cadrul uneia
dintre următoarele instrucţiuni/clauze:
 SELECT
 HAVING
 COMPUTE
 Dacă o funcţie agregat este utilizată în cadrul instrucţiunii SELECT, celelalte
câmpuri preluate din sursa de date în instrucţiunea SELECT se vor regăsi drept
câmpuri de grupare în cadrul instrucţiunii GROUP BY.
 Clauza HAVING permite specificarea de condiţii la nivelul grupurilor de
înregistrări .
 Instrucţiunea COMPUTE permite calcularea de totaluri generale sau subtotaluri
şi este specificată la sfârşitul instrucţiunii de selecţie (după ORDER BY).
Specificarea opţiunii DISTINCT în cazul funcţiilor AVG, SUM sau COUNT
permite ca valorile identice să fie considerate o singură dată în momentul
efectuării calculelor.
Specificarea simbolului * între parantezele funcţiei COUNT va conduce la
numărarea tuturor valorilor domeniului pe care se aplică funcţia, inclusiv a
valorilor nule.
Exemple:
Se dă tabelul Colaboratori:

1. Calculaţi câte coduri de departamente sunt in tabelul Colaboratori:


SELECT COUNT(CODDEPARTAMENT) As EX1,
COUNT(DISTINCT CodDepartament) AS Ex2,
COUNT(*) AS Ex3
FROM Colaboratori
Capitolul IV INTEROGAREA DATELOR 87

Rezultatele interogării sunt prezentate


în figura alăturată.

Observaţie: Clauza Group By nu este necesară întrucât funcţia Count se aplică


întregului domeniu, iar în instrucţia SELECT nu figurează şi alte câmpuri înafara
funcţiilor.
Cele trei exemple de utilizare a funcţiei COUNT returnează rezultate diferite
deoarece al doilea exemplu utilizează opţiunea DISTINCT, iar al 3-lea va număra
şi valoarea NULL prezentă la unul dintre angajaţi în câmpul CodDepartament.
2. Calculaţi salariul minim si salariul maxim pe fiecare compartiment unde sunt
mai mult de doi Angajati.
SELECT CodDepartament, MIN(salariu) as SalariuMinim,
MAX(salariu) as SalariuMaxim
FROM Colaboratori
GROUP BY CodDepartament
HAVING COUNT(CNP) > 2
3. Să se obţină lista alfabetică a angajaţilor din departamentul IT şi să se calculeze
total general salarii.
Observaţie: Întrucât se doreşte utilizarea unei funcţii de grupare împreună cu o
listă de selecţie ce conţine înregistrări negrupate se va utiliza clauza COMPUTE

SELECT NUME, SALARIU


FROM Colaboratori
WHERE CodDepartament='IT'
ORDER BY NUME
COMPUTE SUM(SALARIU)
88 Baze de date – Introducere in SQL Server 2008

4. Să se obţină lista angajaţilor ordonată alfabetic pe departamente şi să se


calculeze salariul mediu pe fiecare departament:

SELECT NUME, SALARIU, CodDepartament


FROM Colaboratori
ORDER BY CodDepartament, NUME
COMPUTE AVG(SALARIU) BY CodDepartament

Observaţie: În acest caz, clauza COMPUTE permite specificarea câmpului pe baza


cărora se vor realiza subtotaluri utilizând sintaxa
COMPUTE <funcţie> BY nume_camp
Utilizarea clauzei COMPUTE BY necesită ca rezultatele selecţiei să fie ordonate
după câmpul precizat în instrucţiunea BY (în cazul de faţă CodDepartament).
Rezultatele exemplului precedent sunt prezentate în figura următoare:

Funcţii de clasificare
 ROW_NUMBER( ) OVER (< order_by_clause > )
Atribuie un număr de ordine fiecărei înregistrări in funcţie de un criteriu de
ordonare specificat în clauza OVER.
Capitolul IV INTEROGAREA DATELOR 89

 RANK( ) OVER (< order_by_clause > )


Atribuie un rang fiecărei înregistrări în funcţie de un criteriu de ordonare.
Înregistrările cu aceleaşi valori pe câmpul după care se face ordonarea vor avea
acelaşi rang.
 NTILE (N) OVER (order_by_clause > )
Permite distribuirea înregistrărilor dintr-un set de rezultate pe N intervale funcţie
de un criteriu de ordonare.
Exemple:
 Sa se numeroteze angajaţii în ordine descrescătoare a salariilor.
 Utilizând funcţia RANK să se atribuie un rang angajaţilor după salariu.
 Să se împartă angajaţii în trei grupe salariale, în ordine descrescătoare a
salariilor.

SELECT Nume, Salariu,


ROW_NUMBER() OVER (ORDER BY SALARIU DESC) AS Ex1,
RANK() OVER (ORDER BY SALARIU DESC) AS Ex2,
NTILE(3) OVER (ORDER BY SALARIU DESC) AS Ex3
FROM Colaboratori
ORDER BY SALARIU DESC

Observaţie: Clauza ORDER BY din instrucţiunea SELECT poate să difere de cea


din clauza OVER utilizată în cadrul funcţiilor ROW_NUMBER, RANK sau NTILE.

Alte funcţii
 ISNULL ( expresie , valoare de inlocuit )
90 Baze de date – Introducere in SQL Server 2008

Funcţia ISNULL evaluează dacă o expresie conţine valoarea NULL şi, în cazul în
care condiţia este îndeplinită, înlocuieşte expresia cu valoarea specificată ca al
doilea argument .
 ISDATE (expresie )
Funcţia ISDATE verifică dacă o expresie poate fi evaluată ca o dată calendaristică
validă. În cazul în care condiţia este îndeplinită returnează valoarea 1, în caz
contrar 0.
 ISNUMERIC(expresie)

Funcţia ISNUMERIC evaluează dacă o expresie poate fi considerată valoare


numerică. În cazul în care condiţia este îndeplinită returnează valoarea 1, în caz
contrar 0.
 Funcţia NEWID()
Returnează o valoare de tip UNIQUEIDENTIFIER
Se poate utiliza în orice frază SQL sau în cadrul proprietăţii Default Value or
Binding, pentru atribute de tipul unique identifier în scopul alocării de valori în
mod automat.

Exemplul 1
Utilizarea din SQL:

SELECT NEWID ()

Exemplul 2
Utilizarea în cadrul proprietăţii Default Value or Binding, într-un tabel unde cheia
primară CodModul, este de tip uniqueidentifier:
Capitolul IV INTEROGAREA DATELOR 91

Figura 1 Utilizarea funcţiei newid()

 Funcţia CASE

Sintaxa varianta 1:
CASE expresieA
WHEN expresieB1 THEN expresie_Rezultat
[WHEN expresieB2 THEN expresie_Rezultat]

[ELSE expresie_Rezultat_F]
END
În această variantă funcţia evaluează expresieA şi în cazul în care rezultatul
acesteia verifică rezultatul evaluării pentru expresieB1 (sau expresieB2, etc.), se
execută expresie_Rezultat. În cazul în care nu se verifică niciuna din expresiile
expresieB1, expresieB2, … se va executa expresie_Rezultat_F.
92 Baze de date – Introducere in SQL Server 2008

Sintaxa varianta 2:
CASE
WHEN expresie_logica1 THEN expresie_Rezultat
[WHEN expresie_logica2] THEN expresie_Rezultat

[ELSE expresie_Rezultat_F]
END

În această variantă se execută prima expresie_Rezultat luată în ordinea scrierii,


unde expresie_logică este evaluată cu valoarea “adevărat” (TRUE). În cazul în
care niciuna din expresiile logice nu este adevărată se execută
expresie_Rezultat_F.
Exemplul 1
Să se calculeze o primă contractuală pentru fiecare angajat care a lucrat la un
contract. Prima se va calcula astfel:
 Pentru angajaţii care au lucrat toată perioada unui contract, se acordă o
primă de 10% din tariful negociat al contractului;
 Pentru angajaţii care nu au lucrat toată perioada unui contract, se acordă o
primă de 8% din tariful negociat al contractului, proporţional cu numărul de
zile lucrate la contract;

SELECT Angajat.CNPAngajat, NumeAng, PrenumeAng,


Contract.NrContract, TarifNegociat,
DateDIFF(DAY, DataInceput, DataSfarsit) as
NrZileLucrate,
DateDiff(DAY, DataContract, DataFinContract) As
NrZileContract,
CASE DateDIFF(DAY, DataInceput, DataSfarsit)
WHEN DateDiff(DAY, DataContract, DataFinContract) THEN
TarifNegociat * 0.1
ELSE
TarifNegociat * 0.08 * DateDIFF(DAY, DataInceput,
DataSfarsit)/DateDiff(DAY, DataContract,
DataFinContract)
END As Prima2008
FROM Angajat INNER JOIN Lucreaza ON
Angajat.CNPAngajat=Lucreaza.CNPAngajat
INNER JOIN Contract ON
Lucreaza.NrContract=Contract.NrContract
Capitolul IV INTEROGAREA DATELOR 93

Exemplul 2
Să se calculeze o primă anuală acordată fiecărui angajat, ca procent din tariful
negociat al fiecărui contract la care a lucrat, în funcţie de numărul de zile pe care
le-a lucrat. Procentul din tariful negociat este:
 5% pentru cei care au lucrat sub 50 zile la un contract;
 7% pentru cei care au lucrat între 50 şi 100 zile la un contract;
 9% pentru cei care au lucrat între 100 şi 150 zile la un contract;
 11% pentru cei care au lucrat între 150 şi 300 zile la un contract;
 12% pentru cei care au lucrat peste 300 zile la un contract;

SELECT Angajat.CNPAngajat, NumeAng, PrenumeAng,


Contract.NrContract, TarifNegociat,
DateDIFF(DAY, DataInceput, DataSfarsit) as NrZile,
CASE
WHEN DateDIFF(DAY, DataInceput, DataSfarsit) <50 THEN
TarifNegociat * 0.05
WHEN DateDIFF(DAY, DataInceput, DataSfarsit) <100 THEN
TarifNegociat * 0.07
WHEN DateDIFF(DAY, DataInceput, DataSfarsit) <150 THEN
TarifNegociat * 0.09
WHEN DateDIFF(DAY, DataInceput, DataSfarsit) <300 THEN
TarifNegociat * 0.1
ELSE
TarifNegociat * 0.12
END As Prima2008

FROM Angajat INNER JOIN Lucreaza ON


Angajat.CNPAngajat=Lucreaza.CNPAngajat
INNER JOIN Contract ON Lucreaza.NrContract=Contract.NrContract
94 Baze de date – Introducere in SQL Server 2008

4.2. Obiecte de tip View


Un obiect de tip View reprezintă un tabel virtual al cărui conţinut se obţine în
urma execuţiei unei interogări. Datele obţinute cu un View nu sunt stocate separat
în baza de date împreuna cu View-ul. Nu sunt memorate nici măcar câmpurile
obiectului View. Atât datele cât şi câmpurile (structura tabelară) din View sunt
obţinute în mod dinamic la fiecare execuţie a interogării care stă la bază. După
crearea unui obiect View, având în vedere că din punct de vedere funcţional este
similar unui tabel (virtual) din baza de date, obiectul View poate fi utilizat în mod
asemănător acestora.
Categorii de obiecte View:
 Standard – utilizează interogări uzuale de selecţie pe unul sau mai multe
tabele ale bazei de date;
 Indexate – utilizează un index pentru a creşte performanţele accesului la date
prin intermediul unui View. Performanţa este foarte ridicată, doar în cazul în
care datele accesate nu suferă modificări dese în timp;
 Partiţionate – se utilizează pentru a accesa seturi de date cu structură
identică, distribuite pe unul sau mai multe servere. Pot fi utilizate cu succes
pentru implementarea bazelor de date federative.
Gestiunea obiectelor View, poate fi făcută:
 utilizând interfaţa grafică dedicată pusă la dispoziţie prin SQL Server
Management Studio;
 direct dintr-o fraza SQL (de exemplu dintr-o frază SQL executată într-o
fereastră Query);
Capitolul IV INTEROGAREA DATELOR 95

Crearea unui View standard folosind interfaţa grafică dedicată din


SQL Server Management Studio
Pentru a exemplifica crearea unui obiect View, în acest mod, se parcurg următorii
paşi (pentru exemplu definim un obiect View pentru selectarea în ordine
cronologică a tuturor contractelor din perioada 1/1/2008-2/2/2008 şi ofertelor pe
baza cărora s-au încheiat):
a. Se acţionează clic dreapta pe
secţiunea Views a bazei de date
şi apoi New View…:

b. Se aleg obiectele participante în View (tabele, view-uri, funcţii, sinonime):

Figura 2
96 Baze de date – Introducere in SQL Server 2008

c. Se stabilesc câmpurile selectate în View, criteriile de selecţie (Filter),


criteriile de grupare şi cele de ordonare a datelor (Sort Type şi Sort Order):

Figura 3

Se observă că pe măsură ce se defineşte obiectul View, în partea de jos a ferestrei


se generează în mod automat codul SQL. Utilizatorul poate defini obiectul View
simultan în mod grafic, cât şi în mod SQL.
Executarea View-ului se realizează din butonul (Execute SQL) din bara de
instrumente, rezultatele fiind afişate în partea de jos a ecranului.
În fereastra următoare este exemplificat modul de definire a unui View cu grupări,
pentru calcularea numărului de angajaţi care a lucrat la fiecare contract. Activarea
grupărilor într-un View se realizează din butonul (Add Group By) din bara de
instrumente.
Capitolul IV INTEROGAREA DATELOR 97

Figura 4

Crearea/modificarea/ştergerea unui View standard în SQL


Crearea unui obiect View din SQL se realizează folosind sintaxa:

CREATE VIEW numeView As


fraza_SQL_SELECTIE
Modificarea unui obiect View din SQL se realizează folosind sintaxa:

ALTER VIEW numeView As


fraza_SQL_SELECTIE

Ştergerea unui obiect View din SQL se realizează folosind sintaxa:

DROP VIEW numeView


98 Baze de date – Introducere in SQL Server 2008

Nota: accesarea unui View dintr-o fraza SQL se realizează respectând sintaxa
unei fraze SELECT:

SELECT lista_atribute
FROM NumeView
…….
Exemplul 1
Definirea unui View pentru calcularea numărului de angajaţi care a lucrat la
fiecare contract.

CREATE VIEW ExempluView


AS
SELECT Contract.NrContract, DataContract,
COUNT(Lucreaza.CNPAngajat) AS NrAng
FROM Contract INNER JOIN Lucreaza ON Contract.NrContract =
Lucreaza.NrContract
GROUP BY Contract.NrContract, Contract.DataContract
Exemplul 2
Ştergerea obiectului View creat anterior:

DROP VIEW ExempluView

Crearea unui View indexat in limbajul SQL


Sintaxa simplificată:

CREATE VIEW numeView WITH SCHEMABINDING As


fraza_SQL_SELECTIE

CREATE UNIQUE CLUSTERED INDEX numeINDEX ON


numeView(numecâmp)

Exemplul 3
Crearea unui View indexat pentru afişarea contractelor din 2008:

CREATE VIEW Exemplu WITH SCHEMABINDING As


SELECT NrContract, DataContract, TarifNegociat
FROM dbo.Contract
WHERE YEAR(DataContract) = 2008
şi apoi:
Capitolul IV INTEROGAREA DATELOR 99

CREATE UNIQUE CLUSTERED INDEX numeIDX ON


Exemplu(DataContract)

Crearea unui View partiţionat în SQL


Se implementează cu ajutorul interogărilor de tip UNION, fiind de fapt o reuniune
a seturilor de date cu structură identica/asemănătoare distribuite la nivel local sau
pe servere diferite.
Exemplul 1
Definirea unui View distribuit pentru afişarea contractelor încheiate, din două baze
de date diferite (CarteBDA şi CarteBDA_bis), dar care sunt stocate pe acelaşi
server.
SELECT NrContract, DataContract, TarifNegociat, CUICl
FROM CarteBDA.dbo.Contract
UNION ALL
SELECT NrContract, DataContract, TarifNegociat, CUICl
FROM CarteBDA_bis.dbo.Contract
ORDER BY DataContract

Exemplul 2
Definirea unui View distribuit pentru afişarea contractelor încheiate, din două baze
de date diferite (CarteBDA şi CarteBDA_server), care sunt stocate pe servere
diferite ([10.1.3.3\CIGTOTAL] şi [FM-PC]) aflate în locaţii geografice diferite.

SELECT NrContract, DataContract, TarifNegociat, CUICl


FROM [10.1.3.3\CIGTOTAL].CarteBDA.dbo.Contract
UNION ALL
SELECT NrContract, DataContract, TarifNegociat, CUICl
100 Baze de date – Introducere in SQL Server 2008

FROM [FM-PC].CarteBDA_server.dbo.Contract
ORDER BY DataContract

Notă: pentru a putea executa o frază SQL care foloseşte date de pe mai multe
servere se execută în prealabil procedura sistem (o singura dată pentru fiecare
server): sp_addlinkedserver nume_server

4.3. Proceduri stocate


O procedură stocată este un pachet de instrucţiuni SQL memorat pe server şi
compilat la utilizare.
Avantaje:
 Facilitează actualizarea datelor prin faptul că permit ca toate aplicaţiile ce
modifică datele să acţioneze în acelaşi mod;
 Acceptă definirea de parametri şi astfel permit executarea aceloraşi
instrucţiuni SQL cu seturi diferite de parametri;
 Utilizarea procedurilor stocate permite diminuarea fluxului de date în reţea
micşorând secvenţele de cod SQL ce sunt transmise serverului;
 Deoarece planurile de execuţie sunt păstrate de server, performanţele
aplicaţiilor pot fi îmbunătăţite în mod semnificativ.
Utilizarea procedurilor stocate presupune parcurgerea următoarelor etape:
1. Crearea procedurii (prin intermediul comenzii CREATE PROCEDURE)
2. Executarea de către utilizator (prin intermediul unei comenzi EXEC)
3. Compilarea (în timpul unei comenzi EXEC serverul va compila şi optimiza
procedura)
4. Executarea de către server (conform planului de execuţie compilat al
procedurii)
Pentru a crea o procedură stocată se poate iniţia o nouă interogare în baza de date
(New Query) şi se va utiliza comanda:
CREATE PROCEDURE nume_procedura AS instrucţiuni_SQL
Pentru a modifica o procedură stocată se va utiliza comanda:
ALTER PROCEDURE nume_procedura AS instrucţiuni_SQL
O procedură stocată poate conţine orice instrucţiuni SQL valide cu câteva excepţii
dintre care amintim: CREATE PROCEDURE, CREATE VIEW si CREATE TRIGGER.
Sunt însă permise comenzi de tip CREATE TABLE sau chiar CREATE DATABASE)
Capitolul IV INTEROGAREA DATELOR 101

Procedurile stocate pot fi create prin intermediul interfeţei oferite de Microsoft


SQL Server Management Studio. În cadrul ferestrei Object Explorer, procedurile
stocate pot fi vizualizate în cadrul colecţiei Programmabilty .

Exemplul: Crearea si lansarea în execuţie a unei proceduri stocate.


Să se realizeze o procedură stocată pentru a obţine lista salariaţilor angajaţi pe
parcursul anului 2008.
CREATE PROC ListaAngajati2008 AS
SELECT NumeAng, PrenumeAng, DataAng FROM Angajati
WHERE DataAng BETWEEN ‘1/1/2008’ AND ‘12/31/2008’
Observaţie:
Lansarea în execuţie a procedurii stocate se poate realiza prin simpla specificare a
numelui acesteia, sau prin plasarea instrucţiunii EXEC înaintea numelui
procedurii:
EXEC ListaAngajati2008

4.3.1. Proceduri de sistem

Înafara procedurilor definite de utilizatori, SQL server pune la dispoziţia


programatorilor o serie de proceduri predefinite ce sunt memorate în baza de date
Master. Aceste proceduri permit executarea unor rutine utile şi pot fi identificate
prin prefixul sp_ ce apare în cadrul denumirii procedurii.

Câteva exemple de astfel de proceduri:

sp_databases permite afisarea listei bazelor de


date de pe server
102 Baze de date – Introducere in SQL Server 2008

sp_columns permite afişarea informaţiilor


privind coloanele unui tabel
specificat ca parametru
sp_executesql permite executarea unor
instrucţiuni SQL specificate ca
parametru
sp_help afiseaza toate informaţiile
disponibile privind un anumit
obiect din baza de date
sp_rename permite redenumirea obiectelor din
baza de date
sp_spaceused afişează numărul de înregistrări şi
spaţiul utilizat de un anumit tabel
sau view pe server

4.3.2. Declararea variabilelor


În cadrul procedurilor stocate se pot utiliza variabile pentru a facilita prelucrarea
datelor. Variabilele se declară în cadrul instrucţiunilor ce urmează după cuvântul
cheie AS din definiţia procedurii stocate prin intermediul instrucţiunii
DECLARE.
Numele de variabile sunt precedate de simbolul @ . Atribuirea unei valori se
poate realiza prin instrucţiunile SET sau SELECT.
Exemplu – declararea variabilelor
În procedura următoare este utilizată o variabilă de tip numeric @CotaDiscount
pentru a calcula reduceri în procent de 12% la tarifele negociate pentru toate
contractele ce au ca obiect ofertele cu codurile 1 sau 3.

CREATE PROC ReduceriTarife AS


DECLARE @CotaDiscount as numeric(3,2)
SET @CotaDiscount = 0.12
SELECT NrContract, TarifNegociat*@CotaDiscount As Reducere
FROM Contract
WHERE CodOferta=1 OR CodOferta=3
Capitolul IV INTEROGAREA DATELOR 103

4.3.3. Parametrizarea procedurilor stocate

Parametrii procedurilor SQL Server sunt de tot tipuri:


 Parametri de intrare (Input)
 Parametri de ieşire (Output)
Parametrii de intrare permit preluarea în cadrul procedurilor stocare a unuia sau
mai multor elemente variabile ce pot fi utilizate în cadrul expresiilor.
Parametri de ieşire sunt utilizaţi pentru returnarea de rezultate în urma
prelucrărilor efectuate de procedura stocată.
O sintaxă simplificată a comenzii CREATE PROCEDURE, care permite şi
adăugarea de parametri este prezentată în continuare:
CREATE PROC nume_procedura
[ [ @parametru tip_de_date] [OUTPUT] [ , ….n] ]
AS
<instrucţiuni SQL>
Procedurile pot utiliza mai mulţi parametri de tip input. Tipul implicit de
parametru este cel de intrare. Doar parametrii însoţiţi de opţiunea OUTPUT, sunt
trataţi ca parametri de ieşire şi sunt utilizaţi pentru a returna valori.
Exemplul - Parametrizarea procedurilor
Să se realizeze o procedură stocată pentru a afişa lista cu numele clienţilor,
numerele contractelor şi tarifele negociate pe o perioadă de timp cuprinsă între
două date calendaristice specificate prin parametri. Se va ordona lista alfabetic,
după numele clienţilor.

CREATE PROC ListaContracte


@Data1 as datetime,
@Data2 as datetime
AS
SELECT DenumireCL, NrContract, TarifNegociat
FROM Contract INNER JOIN Client
ON Contract.CUICl=Client.CUICl
WHERE DataContract BETWEEN @Data1 And @Data2
ORDER BY DenumireCL

Pentru a lansa în execuţie această procedură trebuie atribuite valori parametrilor.


Atribuirea de valori se poate realiza prin enumerarea valorilor parametrilor in
aceeaşi ordine în care au fost declaraţi în procedură sau prin specificarea exactă a
numelui parametrului în faţa fiecărei valori:

EXEC ListaContracte ‘1/1/2007’, ‘1/1/2008’


104 Baze de date – Introducere in SQL Server 2008

sau
EXEC ListaContracte @Data1=’1/1/2007’, @Data2=, ‘1/1/2008’
In exemplul precedent, nespecificarea valorii pentru unul dintre cei doi parametri
va genera o eroare şi imposibilitatea de a executa procedura. Pentru a
preîntâmpina astfel de situaţii, parametrilor de intrare li se pot asocia valori
implicite, care vor fi utilizate automat atunci când nu se precizează o altă valoare.
Aceste valori se specifică imediat după declararea fiecărui parametru.
Pentru a atribui valoarea implicită ‘1/1/2000’ parametrului @Data1 şi ‘1/1/2009’
parametrului @Data2 se va modifica procedura astfel:

ALTER PROC ListaContracte


@Data1 as datetime = '1/1/2000',
@Data2 as datetime= '1/1/2009'
AS
SELECT DenumireCL, NrContract, TarifNegociat
FROM Contract INNER JOIN Client ON
Contract.CUICl=Client.CUICl
WHERE DataContract BETWEEN @Data1 And @Data2
ORDER BY DenumireCL
După cum se poate observa, întrucât procedura ListaContracte, a fost deja creată
şi se dorea doar modificarea acesteia, s-a utilizat instrucţiunea ALTER în loc de
instrucţiunea CREATE.

Exemplu – parametri de tip Output


Pentru a exemplifica utilizarea parametrilor de ieşire vom lua în considerare
următoarea situaţie: Se doreşte calculul unui discount pentru toate contractele
nefinalizate. Discountul va fi egal cu 5% din tariful negociat + 1% din valoarea
celui mai mare contract încheiat cu respectivul client.
A. Vom crea o procedura stocată pentru a determina valoarea maximă a
contractelor negociate cu un client. Procedura va conţine un parametru de tip
OUTPUT care va prelua valoarea salariului maxim calculat şi un parametru de tip
INPUT prin care se va preciza codul clientului.

CREATE PROCEDURE AflaMaximPeClient


@ValMax Money OUTPUT,
@Client as varchar(50)
AS
SELECT @ValMax=MAX(TarifNegociat)
FROM Contract
WHERE CUICl=@Client
Capitolul IV INTEROGAREA DATELOR 105

B. Pentru a calcula reducerea aferentă fiecărui contract conform algoritmului


propus, este necesar să executăm procedura anterior creată. Valoarea parametrului
de tip Output va fi preluată într-o variabilă de memorie. Pentru parametrul de tip
Input (@client) am ales, pentru exemplificare, valoarea ‘RO1001’, cod aferent
unuia dintre clienţi.

DECLARE @Variabila AS money

EXECUTE AflaMaximPeClient
@ValMax=@Variabila OUTPUT, @Client='RO1001'

SELECT NrContract, TarifNegociat*0.05 + @Variabila*0.01 AS


Discount
FROM CONTRACT
WHERE CUICl='RO1001'

4.3.4. Structuri de control al fluxului


 Instrucţiunea RETURN
Prin intermediul comenzii RETURN, se poate forţa întreruperea execuţiei unei
proceduri stocate. Comenzile ce urmează după instrucţiunea RETURN nu vor mai
fi executate.
Sintaxa instrucţiunii este: RETURN [ expresie de tip întreg ]
După cum se poate observa, opţional, după instrucţiunea RETURN se poate
preciza un număr întreg ce poate fi utilizat ulterior în cadrul blocului de
instrucţiuni ce a lansat în execuţie procedura.
Exemplu:
Să se creeze o procedură stocată pentru a afişa datele unui angajat al cărui CNP
este specificat ca parametru. În cazul în care parametrul nu este specificat (rămâne
NULL) se va afişa un mesaj de eroare.

CREATE PROCEDURE AfisezDateAngajat


@cnp char(13) = NULL
AS
IF @cnp IS NULL
BEGIN
SELECT 'NU ATI FURNIZAT UN CNP !'
RETURN
END
ELSE
SELECT * FROM ANGAJATI WHERE CNP = @cnp
106 Baze de date – Introducere in SQL Server 2008

Observaţie:
Orice procedură stocată care se execută cu succes va returna valoarea zero.
Procedurile stocate care provoacă la execuţie o eroare vor returna un cod
negativ (de la -1 la -14)

 Structuri alternative de tip IF


Sintaxa:

IF expresie_logica
Bloc_insctructiuni_SQL_1
[ELSE
Bloc_instructiuni_SQL_2]

Un bloc de instructiuni este format dintr-una sau mai multe instrucţiuni:


BEGIN
instructiune_1
instructiune_2
….
END
Notă. În cazul în care se execută o singură instrucţiune, cuvintele BEGIN şi END
pot să lipsească.
Instrucţiunea IF execută Bloc_insctructiuni_SQL_1 în cazul în care
expresie_logica este adevărată sau Bloc_instructiuni_SQL_2] în cazul în care
expresie_logica este falsă.

 Structuri repetitive de tip WHILE


Sintaxa:
WHILE expresie_logica
Bloc_instructiuni_SQL_1
[BREAK]
Bloc_instructiuni_SQL_2
[CONTINUE]
Bloc_instructiuni_SQL_3
Capitolul IV INTEROGAREA DATELOR 107

Se execută în mod repetat Bloc_instructiuni_SQL_1 atâta timp cât expresie_logica


este adevărată. Când expresie_logica devine falsă, se iese din structura WHILE.
Notă. În cazul în care expresie_logica conţine fraze SQL de tip SELECT, acestea
se vor scrie între paranteze rotunde.
 BREAK realizează ieşirea forţată din WHILE.
 CONTINUE abandonează iteraţia curentă, toate instrucţiunile de după
CONTINUE fiind ignorate.

 Instrucţiunea WAITFOR
Sintaxa:
WAITFOR DELAY ‘timp_aşteptare’ | TIME ‘ora_execuţie’
Stabileşte un timp de întârziere (timp_aşteptare) până la momentul lansării în
execuţie a unei fraze SQL sau stabileşte ora (ora_execuţie) la care se va lansa în
execuţie o frază SQL. Este utilă mai ales la programarea unor operaţii în perioade
timp în care nu se lucrează (de exemplu o operaţie complexă care nu presupune
intervenţia unui utilizator şi se poate executa noaptea).
Exemplul 1
Afişarea peste un minut a tuturor contractelor:
WAITFOR DELAY '00:01'
SELECT * FROM Contract

Exemplul 2
Afişarea tuturor angajaţilor la ora 22:48
WAITFOR TIME '22:48'
SELECT * FROM Angajat

Exemplul 3
Se doreşte majorarea treptată cu câte 10% a tarifelor de bază pentru fiecare ofertă,
până când media generală a tarifelor de bază depăşeşte media generală a tarifelor
negociate din contracte.
În acest sens, s-a definit codul SQL următor:

--se declara o variabila in care se va memora media


--tarifului negociat din contracte

DECLARE @MedieTarifNegociat as money


108 Baze de date – Introducere in SQL Server 2008

--se afiseaza inainte de efectuarea modificarilor


--valorile pentru media tarifului de baza de la oferte
--si media tarifelor negociate ale contractelor

SELECT AVG(TarifBaza) as MedieTarifBazaInitial FROM Oferta


SELECT @MedieTarifNegociat = AVG(TarifNegociat) FROM
Contract
SELECT @MedieTarifNegociat as MedieTarfiNegociat

--Atata timp cat media tarifelor din oferte


--este mai mica decat media tarifelor negociate din
--contracte se va executa structura repetitiva WHILE

WHILE (SELECT AVG(TarifBaza)


FROM Oferta)<@MedieTarifNegociat
BEGIN
--se majoreaza cu 10% doar tarifele de baza din oferte
--care inca sunt sub media tarifelor negociate din
--contracte
UPDATE Oferta
SET TarifBaza = TarifBaza * 1.1
WHERE TarifBaza < @MedieTarifNegociat

--se afiseaza la fiecare iteratie media tarifelor


-- de baza din oferte
SELECT AVG(TarifBaza) as MedieTarifBaza FROM Oferta
END

Rezultatul execuţiei codului de mai sus, este:

Se observă afişarea iniţială a mediei tarifelor de bază (3300) şi a mediei tarifelor


negociate (3675,6139). Apoi se execută două iteraţii, până când media tarifelor de
bază din oferte (3730,50) depăşeşte media tarifelor negociate din contracte
(3675,6139).
Capitolul IV INTEROGAREA DATELOR 109

4.4. Funcţii definite de utilizator


Înafara de setul de funcţii predefinite care au fost prezentate anterior, SQL server
permite utilizatorilor să definească propriile funcţii prin instrucţiuni TRANSACT
SQL.
O funcţie SQL, definită de utilizator, grupează în cadrul unui obiect SQL de sine
stătător o secvenţă de instrucţiuni SQL încapsulată, ce poate fi reutilizată ori de
câte ori este nevoie.
Funcţiile definite de utilizatori au rolul de a returna un rezultat, calculat conform
unui algoritm descris de utilizator şi pot utiliza parametri în cadrul calculelor.
În funcţie de tipul de rezultat returnat, funcţiile definite de utilizatori pot fi
clasificate în două categorii:
 Funcţii de tip scalar (scalar functions) – care returnează o valoare ce poate
fi încadrată în unul dintre tipurile de date SQL Server (de exemplu char,
varchar, numeric, datetime, etc.)
 Funcţii de tip tabelar (table valued functions) – care returnează un set de
înregistrări ce poate fi asimilat unui tabel virtual.
Dintre avantajele aduse de utilizarea funcţiilor putem menţiona:
 Funcţiile definite de utilizator oferă posibilitatea de a structura într-o manieră
eficientă codul SQL şi pot simplifica prelucrările de date ce implică realizarea
unor structuri complexe de programare.
 O dată create, funcţiile pot fi utilizate în cadrul obiectelor de tip View sau al
procedurilor stocate, diminuând considerabil dimensiunea codului SQL şi
facilitând o mai bună structurare a acestuia.
 Funcţiile de tip table valued functions pot fi utilizate pentru simularea unor
tabele virtuale extrem de utile în programarea procedurilor.

Funcţii de tip scalar (scalar functions)


O funcţie de tip scalar poate accepta zero sau mai mulţi parametri şi va returna o
singură valoare. Pentru a crea o astfel de funcţii se va utiliza comanda CREATE
FUNCTION a cărei sintaxă simplificată este următoarea:
110 Baze de date – Introducere in SQL Server 2008

CREATE FUNCTION [numeproprietar.] nume_funcţie


([ @parametru_1 AS tip_de_date],
…. [ @parametrul_n AS tip_de_date] )
RETURNS tip_de date
AS
BEGIN
INSTRUCŢIUNI SQL
RETURN valoare de returnat
END

Pentru modificarea unei funcţii se utilizează sintaxa:


ALTER FUNCTION

Pentru ştergerea unei funcţii se utilizează sintaxa:


DROP FUNCTION

Observaţii:
 Prin intermediul instrucţiunii RETURNS se precizează tipul de date al valorii
returnate de funcţie.
 Prin intermediul instrucţiunii RETURN se specifică efectiv valoare de returnat
rezultată în urma calculelor din comenzile SQL precizate în corpul funcţiei
(între BEGIN şi END). În corpul funcţiei, între BEGIN şi END pot fi incluse
structuri repetitive, declaraţii de variabile, etc. Valoarea returnată nu poate fi
de tip tabel, motiv pentru care nu pot fi utilizate în clauza FROM a unei
instrucţiuni SELECT, însă pot fi utilizate în SELECT, WHERE, GROUP BY,
HAVING şi ORDER BY.
 Numele complet al unei funcţii este dat de următorul specificator:
NumeBazaDate.NumeProprietarFunctie.NumeFuncţie

La apelarea unei funcţii definite de utilizator dintr-o frază SQL, trebuie precizat în
mod obligatoriu proprietarul funcţiei (de exemplu dbo). Numele bazei de date se
precizează doar dacă funcţia executată provine din altă bază decât cea unde se
face apelul către ea. În cazul în care funcţia trebuie executată de alt utilizator decât
proprietarul, acesta trebuie să aibă dreptul de EXECUTE asupra acesteia.
Informaţii despre o funcţie se pot obţine folosind următoarea procedură de sistem:
sp_help funcţie
Capitolul IV INTEROGAREA DATELOR 111

Exemplul 1
Se dau de la două dintre tabelele din modelul proiectat în primul capitiol al cărţii.
CLIENTI (CUICl, DenumireCL, AdresaCL, LocalitateCl, TaraCL )
CONTRACTE(NrContract, DataContract, DataFinContract, TarifNegociat , CodClient)
Se doreşte realizarea unei funcţii pentru calculul unui discount ce se aplica la
valoarea contractelor (tariful negociat) după următoarele criterii:
 Pentru contractele de la clienţii din afara României cu valoare sub 1000 se
aplica 5 % la valoare contract
 Pentru contractele de la clienţii din afara României cu valoare peste 1000 se
aplica 7% la valoare contract
 Pentru contractele de la clienţii din România se aplica 10% la valoare contract
CREATE FUNCTION DISCOUNT(@VALC AS MONEY,
@TARA AS VARCHAR(50))
RETURNS money
AS
BEGIN

RETURN
CASE
WHEN @TARA<>'Romania' AND @VALC<1000 THEN @VALC*0.05
WHEN @TARA<>'Romania' AND @VALC>=1000 THEN @VALC*0.07
ELSE @VALC*0.1
END
END
Ulterior putem utiliza funcţia în cadrul unui View după cum se poate observa în
figura următoare:

Figura 5 Utilizarea funcţiei definite în cadrul unui View


112 Baze de date – Introducere in SQL Server 2008

Observaţie: Funcţiile de tip Scalar se pot utiliza şi în cadrul procedurilor stocate,


cel mai uzual în cadrul clauzei SELECT pentru definirea expresiilor, dar şi în
cadrul clauzei WHERE pentru a impune restricţii.

Exemplul 2
Se doreşte să se realizeze o funcţie pentru a calcula pentru fiecare dintre clienţii cu
care nu mai sunt contracte în derulare în prezent câte săptămâni au trecut de la
finalizarea ultimului contract.
Pentru clienţii la care încă se mai lucrează la ultimul contract (nu s-a ajuns la data
de finalizare) funcţia va returna valoarea 0.
CREATE FUNCTION CalculSaptamani (@CodC as char(30))
RETURNS int
AS
BEGIN
DECLARE @UltimaData as datetime
SET @UltimaData = (SELECT max(DataFinContract)
FROM CONTRACT
WHERE CUICl=@CodC)
RETURN
CASE
WHEN @UltimaData>=GETDATE() THEN 0
ELSE Datediff(week, @UltimaData, getdate())
END
END

Ulterior putem utiliza funcţia în cadrul unei proceduri stocate. În exemplul


următor se vor selecta clienţii români cu care nu s-a mai colaborat de mai mult de
10 saptamani

SELECT CUICl, DenumireCl, LocalitateCl, TaraCl ,


dbo.Ex2(CUICl) as [saptamani de la ultimul contract]
FROM CLIENT
WHERE dbo.Ex2(CUICl)>10 And TaraCl='Romania'

Exemplul 3
Se doreşte alocarea unor coduri contractelor pentru realizarea unor clasificări şi
verificări ulterioare. Codurile vor fi alcătuite din: primele 3 caractere din
denumirea ţării din care provine clientul, urmate de caracterul de pe poziţia a doua
din numele clientului, apoi, ultimele doua cifre din anul în care s-a semnat
contractul şi numărul zilei din an în care s-a finalizat contractul.
Capitolul IV INTEROGAREA DATELOR 113

CREATE FUNCTION Codificare(@Tara as varchar(50), @NumeC As


varchar(50), @DataC as datetime, @DataF as datetime)
RETURNS char(9)
AS
BEGIN
--- OBSERVATIE:
--- ESTE NECESAR CA DATELE DE TIP NUMERIC SI CALENDARISTIC
--- SA FIE CONVERITE IN TIMP SIR DE CARACTERE

DECLARE @Cod1 as char(3) --prima parte din cod (tara)


DECLARE @Cod2 as char(1) --a doua parte din cod (client)
DECLARE @Cod3 as char(2) --a treia parte din cod (anul)
DECLARE @Cod4 as char(3)--a patra parte din cod (ziua)

SET @Cod1= LEFT(@TARA,3)


SET @Cod2 = SUBSTRING( @NumeC,2,1)
SET @Cod3= RIGHT( CONVERT(CHAR(4), YEAR(@DataC)) , 2)
SET @Cod4 = CONVERT (CHAR(3), DATEPART(dayofyear, @DataF))

RETURN @COD1+@COD2+@COD3+@COD4

END
Utilizarea funcţiei pentru a afişa lista codurilor aferente contractelor este
exemplificată în interogarea următoare:

SELECT NrContract, DenumireCl, TaraCl,


DataContract,DataFinContract, dbo.Codificare(TaraCl,
DenumireCl, DataContract, DataFinContract) As Cod
FROM Client INNER Join Contract on
Client.Codclient=Contract.CodClient

Funcţii de tip tabelar (table valued functions)


Funcţiile de tip tabelar se diferenţiază de cele de tip scalar prin faptul că
returnează un set de date sub forma unui tabel bidimensional conform modelului
relaţional.
Având în vedere faptul că sunt asimilate tabelelor (virtuale), pot fi utilizate în
cadrul unei fraze SQL, similar modului cum sunt utilizate tabelele bazei de date.
Datele rezultate nu sunt memorate sub forma unui tabel în baza de date, ci sunt
obţinute dinamic la fiecare apel al funcţiei – similar obiectelor de tip View.
Diferenţa majoră faţă de acestea constă în posibilitatea utilizării parametrilor în
cadrul funcţiilor, conferindu-le un grad ridicat de flexibilitate.
114 Baze de date – Introducere in SQL Server 2008

Aceste funcţii sunt utilizate în clauza FROM a interogărilor.


Sintaxa simplificată pentru definirea unei funcţii de tip tabelar este următoarea:
A). (varianta INLINE) funcţia returnează un tabel ca rezultat, fără a fi nevoie să
se definească structura acestuia. În această varianta nu sunt permise BEGIN
….END, ci doar o frază SQL de tip SELECT, rezultatele fiind furnizate sub forma
unui tabel. Fiecare câmp din interogarea sursă trebuie să aibă un nume, ceea ce
înseamnă că expresiile cu mai mulţi operanzi trebuie sa aibă neapărat un alias. În
această variantă de utilizare, utilizarea clauzei ORDER BY este permisă numai
dacă se utilizează împreună cu TOP în instrucţiunea SELECT. Nu sunt permise
prelucrări complexe.
CREATE FUNCTION [numeproprietar.] nume_funcţie
([ @parametru_1 AS tip_de_date],
… [ @parametrul_n AS tip_de_date] )
RETURNS TABLE
As
RETURN (fraza_SQL_SELECT)

Exemplu
Realizaţi o funcţie prin intermediul căreia să se determine care sunt primii 10
clienţi cu cele mai multe contracte. Funcţia va returna codurile clienţilor şi
numărul total de contracte.

CREATE FUNCTION CLIENTI_IMPORTANTI()


RETURNS TABLE
AS
RETURN (SELECT top 10 CUICl, COUNT(NrContract)
FROM CONTRACT
GROUP BY CUICl
ORDER BY COUNT(NrContract) DESC)

B). varianta (MULTIINSTRUCŢIUNE). Funcţia va returna un tabel rezultat,


fiind necesară şi definirea structurii acestuia. Faţă de varianta A, această categorie
poate să includă prelucrări complexe.
CREATE FUNCTION [numeproprietar.] nume_funcţie ([
@parametru_1 AS tip_de_date],
…. [ @parametrul_n AS tip_de_date] )
RETURNS @variabila_output TABLE
(câmp_1 tip de date, …
câmp_N tip de date)
AS
BEGIN
Capitolul IV INTEROGAREA DATELOR 115

INSTRUCŢIUNI SQL
RETURN
END

Exemplu
Realizaţi o funcţie prin intermediul căreia să se determine care sunt primii 10
clienţi cu cele mai multe contracte. Funcţia va returna codurile clienţilor şi
numărul total de contracte.

CREATE FUNCTION CLIENTI_IMPORTANTI()


RETURNS @Tabel_10 TABLE
(CodC AS varchar(30), TotalContracte AS int)
AS
BEGIN
INSERT @Tabel_10
SELECT top 10 CUICl, COUNT(NrContract)
FROM CONTRACT
GROUP BY CUICl
ORDER BY COUNT(NrContract) DESC

RETURN
END

Putem utiliza funcţia în cadrul unei proceduri stocate ce afişează lista contractelor
cu clienţii importanţi, contractate de la începutul anului 2008 astfel:
SELECT NrContract, DataContract, TarifNegociat,
DenumireCl, AdresaCl
FROM CONTRACT INNER JOIN CLIENT ON
Contracte.CodClient=Clienti.Codclient
WHERE CLIENTI.CodClient IN (SELECT CodClient FROM
dbo.CLIENTI_IMPORTANTI() )
AND DataContract>'1/1/2008'

După cum se poate observa, funcţia precedentă nu a necesitat parametri.


Pentru exemplificarea unei funcţii cu parametri vom rezolva exemplul următor:

Exemplu
Se doreşte realizarea unei funcţii care să returneze lista contractelor finalizate într-
o anumită perioadă şi, pentru fiecare dintre acestea, 50% din valoarea contractata
(tariful negociat).
116 Baze de date – Introducere in SQL Server 2008

Ulterior se va realiza o procedură stocată pentru a calcula totalul încasărilor din


finalizarea contractelor de la un anumit client pe o perioadă de timp (presupunem
că la finalizarea contractelor se încasează 50% din valoarea contractată)
1. Realizarea funcţiei

CREATE FUNCTION ListaFinalizari (@Data1 as datetime,


@Data2 As Datetime)
RETURNS @ListaF TABLE
(NrContract int,
DataFinalizare datetime,
CodClient varchar(30),
Incasari money)
AS
BEGIN
INSERT @ListaF
SELECT NrContract, DataFinContract,
CUICL, TarifNegociat/2
FROM CONTRACT
WHERE DataFinContract BETWEEN @Data1 AND @Data2

RETURN
END

2. Realizarea procedurii stocate:


CREATE PROC Incasari_Final_Contract
@CodC AS varchar(30),
@DataStart AS DateTime,
@DataSfarsit AS DateTime
AS
SELECT CodClient, SUM(Incasari)
FROM ListaFinalizari(@DataStart, @DataSfarsit)
WHERE CodClient=@CodC
GROUP BY Codclient

Lansarea în execuţie a procedurii stocate pentru a determina încasările de la


clientul cu codul RO1000 în intervalul 2 iunie 2007 – 3 mai 2008:

EXEC Incasari_Final_Contract ‘RO1000’, '2/6/2007', '3/5/2008'


Capitolul IV INTEROGAREA DATELOR 117

4.5. Utilizarea cursoarelor


Cursoarele SQL reprezintă o modalitate de parcurgere secvenţială a unui set de
înregistrări returnat de o frază SQL de tip SELECT.

Instrucţiuni pentru declararea şi utilizarea cursoarelor:


a. Declararea cursoarelor se realizează cu instrucţiunea:

DECLARE nume_cursor CURSOR


[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
FOR instructiune_SQL_SELECT
[ FOR UPDATE [ OF nume_coloana [ ,...n ] ] ]

 FORWARD_ONLY – permite deplasarea numai în modul “înainte” în cadrul


înregistrărilor cursorului.
 SCROLL – permite operaţiile FIRST, LAST, PRIOR, NEXT, RELATIVE,
ABSOLUTE pentru deplasarea în cadrul înregistrărilor cursorului.
 STATIC – determină realizarea unei copii temporare a setului de înregistrări
pe care se lucrează. În cazul în care se fac modificări în tabelele folosite ca
sursă de către cursor, acestea nu se reflectă în cadrul setului de înregistrări
virtual, asociat cursorului. Cursorul nu este actualizabil.
 DYNAMIC – permite ca orice modificare efectuată în tabelele sursă ale
cursorului să se reflecte automat în setul de înregistrări asociat acestuia. În
acest tip de cursor nu se poate utiliza deplasarea prin instrucţiunea
ABSOLUTE.
 KEYSET – specifică faptul că înregistrările din cursor sunt deschise într-o
anumită ordine.
 FAST_FORWARD – se utilizează pentru a optimiza accesul la înregistrările
cursorului (numai “înainte”).
 SCROLL_LOCKS – blochează înregistrările care se citesc (pentru ceilalţi
utilizatori), în aşa fel încât orice actualizare încercată pe înregistrările citite să
se efectueze cu succes.
 OPTIMISTIC – nu blochează înregistrările citite, însă în cazul în care alţi
utilizatori efectuează actualizări pe aceleaşi înregistrări cu cele din cursor, iar
cel care utilizează cursorul încearcă şi el modificări, se citeşte valoarea
coloanei timestamp, iar în cazul în care se detectează că s-au efectuat
118 Baze de date – Introducere in SQL Server 2008

actualizări de către alţi utilizatori, atunci actualizarea iniţiată în cursor va eşua.


 READ ONLY – nu sunt permise modificările/ştergerile în cadrul înregistrărilor
cursorului.
 UPDATE [OF nume_coloana [ ,...n ] ] ] – stabileşte o coloană sau mai multe
coloane pe care se pot face actualizări în cadrul înregistrărilor cursorului.
Observaţie: În fraza SQL de selecţie nu sunt permise clauzele COMPUTE,
COMPUTE BY şi INTO.
b. OPEN nume_cursor
Execută instrucţiunile T-SQL declarate în cursor şi încarcă în memorie setul de
înregistrări rezultat.
c. CLOSE nume_cursor
Închide cursorul, orice blocaje induse de acesta înregistrărilor din tabelele folosite
de cursor fiind şterse.
d. DEALLOCATE nume_cursor
Eliberează memoria ocupata de cursor.
e. Instrucţiunea FETCH
Accesează o înregistrare din cursor. Sintaxa:

FETCH
[ [ NEXT | PRIOR | FIRST | LAST
| ABSOLUTE { n | @nvar }
| RELATIVE { n | @nvar } ]
FROM
]
Nume_cursor
[ INTO @variable_name [ ,...n ] ]

 ABSOLUTE { n | @nvar}
 dacă n este un număr pozitiv se returnează înregistrarea cu numărul n,
calculată în raport cu începutul cursorului.
 dacă n este un număr negativ se returnează înregistrarea cu numărul n,
calculată în raport cu sfârşitul cursorului.

 [ INTO @variable_name [ ,...n ] ]


Capitolul IV INTEROGAREA DATELOR 119

Se specifică un set de variabile în care se vor încărca valorile din coloanele


rândului accesat din cursor. Variabilele se vor preciza în aceeaşi ordine ca şi
câmpurile din cadrul cursorului.

f. Variabila @@FETCH_STATUS
Determina daca mai exista înregistrări de accesat in cursor. Valori posibile:
 0 – instrucţiunea FETCH pentru accesarea unei înregistrări s-a executat cu
succes.
 -1 – instrucţiunea FETCH pentru accesarea unei înregistrări a eşuat.
 -2 – instrucţiunea FETCH pentru accesarea unei înregistrări nu a returnat
niciun rezultat.

Exemplu
Sa se definească o procedură stocată care să folosească un cursor pentru
următoarele operaţii:
 parcurgerea unui set de înregistrări virtuale, secvenţial, înainte, cu media
tarifelor negociate ale fiecărei oferte folosite în contracte.
 daca media tarifelor negociate a unei oferte din cursor este mai mare decât
o valoarea transmisă printr-un parametru procedurii stocate se va majora cu
10% tariful de bază al ofertei (în tabelul Oferta).

CREATE PROCEDURE TestCursor


@pParametru as money
AS

--se declara cursorul


--Atentie ! Nu se executa inca fraza SQL asociata

DECLARE cursorExemplu CURSOR STATIC SCROLL


FOR SELECT CodOferta, AVG(TarifNegociat) as Medie
FROM Contract
GROUP BY CodOferta

--se declara doua variabile in care se vor incarca


--cele doua coloane ale cursorului
declare @pCodOferta as bigint, @pMedie as money

--urmatoarea fraza SQL este numai pentru control/test


--pentru a urmari ce se executa in fereastra Results
SELECT CodOferta, AVG(TarifNegociat) as Medie
120 Baze de date – Introducere in SQL Server 2008

FROM Contract
GROUP BY CodOferta

--urmatoarea fraza este tot pentru test pentru


--a urmari valorile initiale ale preturilor de catalog
--si sa putem observa la sfarsit daca s-au produs
--modificari in baza de date
SELECT * FROM Oferta

--se executa fraza SQL din cursor


OPEN cursorExemplu

--se pozitioneaza pe prima inregistrare din cursor


FETCH NEXT FROM cursorExemplu

--se incarca cele doua variabile cu


--valorile din coloanele cursorului
INTO @pCodOferta, @pMedie

--in cazul in care exista cel putin o inregistrare


--se incearca parcurgerea intregului cursor atata timp
--cat o intructiune FETCH returneaza in variabila
--@@FETCH_STATUS valoarea zero
--Prima data instructiunea WHILE de mai jos verifica
--starea instructiunii FETCH executata anterior
WHILE @@FETCH_STATUS = 0
BEGIN
IF @pMedie>@pParametru
BEGIN
UPDATE Oferta SET TarifBaza = TarifBaza * 1.1
WHERE CodOferta = @pCodOferta
END

--se pozitioneaza pe urmatoarea inregistrare din cursor


FETCH NEXT FROM cursorExemplu
INTO @pCodOferta, @pMedie
END

--urmatoarea fraza SQL este pentru test


--pentru a urmari daca s-au efectuat modificari
SELECT * FROM Oferta

--se inchide cursorul


CLOSE cursorExemplu

--se elibereaza memoria


DEALLOCATE cursorExemplu
Capitolul IV INTEROGAREA DATELOR 121

Apelul procedurii stocate cu parametrul 4000 (se vor lua în considerare din cursor
doar mediile tarifelor de bază mai mari de 4000):

EXEC TestCursor 4000

În captura de mai jos se vede rezultatul execuţiei prin urmarirea înregistrărilor


returnate de cele trei fraze SQL de test, incluse în procedura stocată:
 În primul set de rezultate se observă ofertele care au media tarifelor
negociate mai mari de 4000 (ofertele cu codul 2 şi 4).
 În al doilea set de rezultate se observă valorile iniţiale ale tarifelor de bază
pentru cele două oferte (ofertele cu codul 2 şi 4).
 În al treilea set de rezultate se observă valorile finale ale tarifelor de bază
pentru cele două oferte (ofertele cu codul 2 şi 4).
122 Baze de date – Introducere in SQL Server 2008

4.6. Exerciţii propuse şi rezolvate


Se vor lua în considerare două dintre tabelele bazei de date pentru evidenţa
clienţilor şi contractelor proiectată în primul capitol al cărţii la care a fost adăugat
şi un tabel pentru evidenţa încasărilor (pentru fiecare contract pot exista mai multe
ordine de plată memorate în tabelul Incasari).
Se dau de la două dintre tabelele din modelul proiectat în primul capitiol al cărţii.
CLIENT(CUICl, DenumireCL, AdresaCL, LocalitateCl, TaraCL )
CONTRACT(NrContract, DataContract, DataFinContract, TarifNegociat , CUICl)
INCASARI (CodIncasare, NrOP, DataOP, Suma, NrContract)

1. Realizaţi un trigger pe tabelul Încasări, pentru operaţia de ştergere, astfel încât


să nu se poată şterge încasările ce provin din contracte finalizate in anul 2007.
Rezolvare
CREATE TRIGGER NuSterge2007 ON Incasari for DELETE
AS
IF EXISTS(SELECT NrContract
FROM DELETED
WHERE NrCONTRACT IN (SELECT NrContract
FROM CONTRACT
WHERE YEAR(DataFinContract)=2007))
BEGIN
RAISERROR ('Nu se poate şterge', 18,1)
ROLLBACK TRANSACTION
END

2. Realizaţi un View pentru a calcula totalul încasărilor din anul 2007 pe fiecare
client. Pentru clienţii la care totalul încasărilor depăşeşte 40000 se va calcula un
BONUS de 2% din total încasări.
Capitolul IV INTEROGAREA DATELOR 123

Figura 6 View

3. Realizaţi o funcţie de tip scalar ce va calcula pe fiecare contract un coeficient


de importanţa în funcţie de valoarea contractului şi de anul in care a fost incheiat
contractul astfel:
 pentru contractele din anul 2007 cu valoare peste 60.000  coeficient 1
 pentru contractele din anul 2007 cu valoare sub 60.000  coeficient 2
 pentru contractele de dinainte de 2007  coeficient 3
Rezolvare
CREATE FUNCTION Coeficient(@datac as datetime, @Valoare
as money)
RETURNS INTEGER
AS
BEGIN
RETURN
CASE
WHEN YEAR(@DATAC)= 2007 AND @VALOARE >=60000
THEN 1
WHEN YEAR(@DATAC)= 2007 AND @VALOARE <60000
THEN 2
ELSE 3
END
END
Exemplu de utilizare:
SELECT NrContract, DataContract, TarifNegociat,
dbo.Cerinta4(DataContract, TarifNegociat)
FROM CONTRACT
124 Baze de date – Introducere in SQL Server 2008

4. Realizaţi o funcţie de tip tabelar care sa returneze numerele de contracte şi


numele clientilor pentru contractele dintr-un anumit an (anul va fi specifcat ca
argument al functiei).
Rezolvare
CREATE FUNCTION ListaPeAn (@An as integer)
RETURNS @Tabel TABLE
(NrContract Integer, Client varchar(50) )
AS
BEGIN
INSERT @Tabel
SELECT NrContract, DenumireCl
FROM CLIENT INNER JOIN CONTRACT ON
CLIENT.CUICl=CONTRACT.CUICl
WHERE YEAR(DataContract)= @an

RETURN
END
Exemplu de utilizare:
SELECT * FROM ListaPeAn(2006)

5. Realizaţi o procedura stocată cu parametri pentru a realiza un clasament al


clienţilor in funcţie de valoarea totală a contractelor. Se va utiliza funcţia RANK
sau ROW_NUMBER. Procedura va fi parametrizată pentru nu a afişa decât
clienţii a căror valoare totala a contractelor depăşeşte o sumă specificată prin
parametru.

CREATE PROC Clasament


@Suma as money
AS
SELECT DenumireCl, SUM(TarifNegociat) AS TOTAL,
RANK() OVER ( ORDER BY SUM(TarifNegociat) DESC)
FROM CLIENT INNER JOIN CONTRACT
ON CLIENT.CUICL= CONTRACT.CUICL
GROUP BY DenumireCl
HAVING SUM(TarifNegociat)>@Suma

Exemplu de utilizare:
EXEC Clasament 6000
Capitolul IV INTEROGAREA DATELOR 125

6. Realizaţi o procedură stocată pentru a a afişa o listă a contractelor încheiate


între două date calendaristice specificate prin parametri. Lista va conţine:
NrContract, DataContract, DenumireCl şi LocalitateCL. Se va calcula câte
contracte a încheiat firma, în respectiva perioadă, în fiecare localitate.
Rezolvare
CREATE PROC ListaContractelor
@DataInceput AS Date,
@DataSfarsit AS Date
AS
SELECT NrContract, DataContract, DenumireCl,
LocalitateCL
FROM CLIENT INNER JOIN CONTRACT
ON CLIENT.CUICL= CONTRACT.CUICL
WHERE DataContract BETWEEN @DataInceput AND
@DataSfarsit AS Date
ORDER BY LocalitateCL
COMPUTE COUNT(NrContract) ON LocalitateCl

7. Realizaţi o procedură stocată pentru a majora tarifele negociate cu un anumit


procent precizat ca parametru. Doar pentru contractele nefinalizate până la data
curentă.
Rezolvare
CREATE PROC Majorare
@Procent AS numeric(3,2)
AS
UPDATE CONTRACT
SET TarifNegociat= TarifNegociat*(1+@Procent)
WHERE DataFinContract>GETDATE()

8. Sa se parcurga prin intermediul unui cursor tabelul Incasari in ordine


cronologica a datelor de pe ordinele de plată şi să se determine la ce dată s-a atins
plafonul de 50.000 de RON încasări totale.

DECLARE CRS CURSOR FORWARD_ONLY STATIC


FOR
SELECT DataOP, Suma
FROM Incasari
ORDER BY DataOP
126 Baze de date – Introducere in SQL Server 2008

DECLARE @DataPlafon AS DateTime


DECLARE @incasare AS money
DECLARE @Cumulat As money

OPEN CRS

-- citim prima inregistrare din cursor


FETCH NEXT From CursorEx1
INTO @DataPlafon, @incasare
SET @CUMULAT=@incasare

-- se aduna sume in variabila cumulat pana se atinge –


-- plafonul sau pana se ajunge la sfarsitul setului de inregistari
WHILE @@FETCH_STATUS=0
BEGIN
IF @Cumulat>10000 BREAK
FETCH NEXT From CursorEx1
INTO @DataPlafon, @Suma
SET @CUMULAT=@CUMULAT+@incasat
END

--se afiseaza data la care s+a atins plafonul


--si suma cumulata
SELECT @DataPlafon As [Data plafon], @Cumulat AS [suma
la aceasta data]

CLOSE CRS
DEALLOCATE CRS

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