Sunteți pe pagina 1din 80

1.

INTRODUCERE - SISTEME DE BAZE DE DATE


Sistemele de baze de date sunt o componentă esenţială a vieţii de zi cu zi în societatea modernă. În cursul unei
zile, majoritatea persoanelor desfăşoară activităţi care implică interacţiunea cu o bază de date: depunerea sau extragerea
unor sume de bani din bancă, rezervarea biletelor de tren sau avion, căutarea unei referinţe într-o bibliotecă
computerizată, cumpărarea unor produse etc.
Bazele de date pot avea dimensiuni (număr de înregistrări) extrem de variate, de la câteva zeci de înregistrări
(de exemplu, baza de date pentru o agendă cu numere de telefon) sau pot ajunge la zeci de milioane de înregistrări (de
exemplu, baza de date de plată pentru plata taxelor şi a impozitelor).
Utilizatorii unei baze de date au posibilitatea să efectueze mai multe categorii de operaţii asupra datelor
memorate:
• Introducerea de noi date (insert);
• Ştergerea unora din datele existente (delete);
• Actualizarea datelor memorate (update);
• Interogarea bazei de date (query) pentru a regăsi anumite informaţii, selectate după un criteriu ales.
În sensul cel mai larg, o bază de date (database) este o colecţie de date corelate din punct de vedere logic, care
reflectă un anumit aspect al lumii reale şi este destinată unui anumit grup de utilizatori. În acest sens, bazele de date pot
fi create şi menţinute manual (de exemplu, fişele de evidenţă a cărţilor dintr-o bibliotecă, aşa cum erau folosite cu ani în
urmă) sau computerizat, aşa cum este majoritatea bazelor de date folosite în momentul de faţă. O definiţie într-un sens
mai restrâns a unei baze de date este următoarea:
O bază de date (database) este o colecţie de date creată şi menţinută computerizat, care permite operaţii de
introducere, ştergere, actualizare şi interogare a datelor.
Simple colecţii de fişe (documente pe hârtie) sau fişiere de date, care conţin înregistrări de date, dar nu permit
operaţii de interogare, nu sunt considerate baze de date. De exempu, datele memorate în fişiere pe disc de un instrument
de calcul tabelar (ca Microsoft Excel) sau documentele memorate de un editor de text (ca Microsoft Word) nu sunt
considerate baze de date.

1.1 COMPONENTELE UNUI SISTEM DE BAZE DE DATE


Un sistem de baze de date (Database System) este un sistem computerizat de menţinere a evidenţei unei
anumite activităţi, folosind baze de date. Componentele unui sistem de baze de date sunt: hardware, software,
utilizatori, date persistente.
Hardware. Sistemele de baze de date sunt instalate, de regulă, pe calculatoare de uz general, de la calculatoare
PC standard, până la staţii multiprocesor puternice. Bineînţeles, performanţele generale de operare ale calculatorului
(numărul şi viteza procesoarelor, dimensiunea şi viteza de operare a memoriei principale etc.) influenţează în mod
corespunzător performanţele sistemului de baze de date. Dar, ceea ce interesează în mod deosebit în utilizarea unui
calculator pentru un sistem de baze de date, este volumul (capacitatea) memoriei secundare, utilizată pentru memorarea
colecţiei de date persistente ale bazei de date.
Dat fiind că într-un sistem de baze de date este necesar accesul rapid la oricare din înregistrările de date, pentru
memorarea acestora se folosesc discurile magnetice (hard-discuri). Benzile magnetice (care oferă acces secvenţial la
înregistrările de date) sunt utilizate numai pentru duplicarea (back-up) şi salvarea/restaurarea datelor.
Software. Între baza de date (colecţia de date memorate fizic în fişiere pe hard-discuri) şi utilizatorii sistemului
există un nivel software, numit Sistem de Gestiune a Bazei de Date (SGBD) - (Database Management System -DBMS)
- (fig. 1.1).
Baza de date

Date Date
Utilizator Program
final aplicaţie SGBD

Date Date

Fig. 1.1. Componente ale unui sistem de baze de date.

Sistemul de gestiune a bazei de date - SGBD - (Database Management System - DBMS) recepţionează
cererile utilizatorilor de acces la baza de date (pentru operaţii de introducere, ştergere, modificare sau interogare), le
interpretează, execută operaţiile corespunzătoare şi returnează rezultatul către utilizatori.
Sistemul SGBD oferă utilizatorilor o viziune (vedere - view) a bazei de date la un nivel înalt şi îi eliberează de
necesitatea de a cunoaşte organizarea particulară a sistemului (driverele de disc, structura înregistrărilor de date, etc.).

1
Mai mult, sistemul de gestiune asigură protecţia datelor faţă de accese neautorizate sau defecte de funcţionare,
asigurând integritatea bazei de date.
Pe lângă SGBD, care este cea mai importantă componentă software a unui sistem de baze de date, mai există şi
alte componente: sistemul de operare, care asigură controlul execuţiei programelor, biblioteci şi instrumente software
(toolset-uri) pentru proiectarea, dezvoltarea sau exploatarea sistemelor de baze de date şi a aplicaţiilor de baze de date.
O aplicaţie de baze de date (Database Application) este un program care oferă o anumită utilizare a unei baze
de date.
De exemplu, programul care permite menţinerea şi urmărirea activităţii angajaţilor unei întreprinderi
(încadrare, calificare, salarizare, etc.) folosind informaţiile despre angajaţi memorate într-o bază de date reprezintă o
aplicaţie de baze de date.
Utilizatorii unui sistem de baze de date se pot împărţi în câteva categorii: programatorii de aplicaţii, utilizatorii
finali şi administratorul bazei de date.
Programatorii de aplicaţii sunt cei care scriu (dezvoltă) aplicaţiile de baze de date, folosind limbaje de
programare de nivel înalt (Cobol, PL/1, Fortran, C, C++, Java, Basic) şi biblioteci care permit încorporarea operaţiilor
de acces la baza de date. Aplicaţiile rezultate pot fi aplicaţii cu execuţie independentă (batch-processing) sau pot fi
aplicaţii interactive (on-line) folosite de utilizatorii finali ai sistemului pentru a accesa (într-un mod mai eficient şi mai
sigur) baza de date.
Utilizatorii finali sunt acei utilizatori care accesează baza de date prin intermediul unui program de aplicaţie
care le conferă drepturi limitate de acces la date pentru anumite operaţii de prelucrare. Utilizatorii finali sunt persoane
cu pregătire tehnică minimală, care efectuează un volum mare de operaţii asupra bazei de date, dar nu trebuie să
cunoască mai mult decât posibilităţile oferite de programul pe care îl utilizează. De exemplu, utilizatorii finali ai unui
sistem de rezervare a bietelor de avion sunt agenţii de vânzări, care folosesc programul adecvat (scris de programatorii
de aplicaţii), fără a fi necesar să cunoască întreaga structură a bazei de date.
Administratorul bazei de date (Database Administrator) este o persoană (sau un grup de persoane) cu înaltă
calificare tehnică care are ca sarcină menţinerea funcţionalităţii bazei de date prin stabilirea drepturilor de acces ale
diferitelor categorii de utilizatori, prin efectuarea operaţiilor periodice de salvare a datelor (backup), prin
monitorizarea performanţelor sistemului şi refacerea datelor atunci când este necesar.
Datele memorate într-o bază de date sunt date persistente, adică date care rămân memorate pe suport
magnetic, independent de execuţia programelor de aplicaţii. Datele persistente ale unei baze de date se introduc, se şterg
sau se actualizează folosind date de intrare (provenite de la tastatură, din citirea unor fişiere de date sau din
recepţionarea unor mesaje). Datele de intrare sunt, în general, date nepersistente; ele sunt generate de utilizatori şi sunt
memorate (devenind date persistente) numai după ce au fost validate (acceptate) de către SGBD. Datele de ieşire ale
unui sistem de baze de date sunt, de asemenea, date nepersistente; ele provin din operaţii de interogare a bazei de date şi
sunt puse la dispoziţia utilizatorului (sub formă de afişări, rapoarte tipărite, etc).
Aceste tipuri de utilizatori asigură exploatarea unei baze de date după ce aceasta a fost proiectată şi realizată.
Activitatea de proiectare a unei baze de date implică şi alte categorii de personal cu înaltă calificare tehnică (proiectanţi,
programatori) sau administrativă (administrator de date). Proiectanţii bazelor de date au responsabilitatea de a analiza
realitatea reprezentată (modelată) de baza de date respectivă, de a identifica datele ce necesită să fie memorate, pentru a
asigura menţinerea evidenţei activităţii dorite. Aspecte privind proiectarea bazelor de date vor fi studiate în capitolele
următoare.
Orice SGBD suportă două categorii de limbaje conceptuale: limbaje de descriere a datelor şi limbaje de
manipulare a datelor.
Limbajele de descriere a datelor - LDD - (Data Description Languages - DDL) permit definirea conceptuală
a datelor, fără referire la modul de memorare fizică a acestora.
Limbajele de manipulare a datelor - LMD - (Data Manipulation Languages - DML) permit specificarea
operaţiilor de introducere, actualizare, ştergere şi interogare a datelor.

1.2 ARHITECTURA INTERNĂ A SISTEMELOR DE BAZE DE DATE


Arhitectura internă a unui sistem de baze de date propusă prin standardul ANSI/X3/SPARC (1975) conţine trei
niveluri funcţionale: nivelul extern, nivelul conceptual şi nivelul intern (fig. 1.2). Nivelul extern este o colecţie de
scheme externe, care sunt vederi ale diferitelor grupuri de utilizatori, existând câte o vedere individuală a datelor pentru
fiecare grup; nivelul conceptual conţine schema conceptuală (logică) a bazei de date, iar nivelul intern conţine schema
internă (fizică) a bazei de date.
O schemă externă (vedere utilizator) (external schema, user’s view) conţine o subschemă conceptuală a bazei
de date, mai precis descrierea datelor care sunt folosite de acel grup de utilizatori.
Schema conceptuală a bazei de date (conceptual schema) corespunde unei reprezentări unice (pentru toţi
utilizatorii) şi abstracte a datelor, descriind ce date sunt stocate în baza de date şi care sunt asocierile dintre acestea.
Schema internă (fizică) a bazei de date (internal schema) specifică modul de reprezentare a datelor pe
suportul fizic.
Un sistem de baze de date suportă o schemă internă, o schemă conceptuală şi mai multe scheme externe; toate
aceste scheme sunt descrieri diferite ale aceleiaşi colecţii de date, care există doar în nivelul intern.

2
Vedere Vedere Vedere
Nivelul extern utilizator #1 utilizator #2 utilizator #n

Nivelul conceptual Schema conceptuală


SGBD

Nivelul intern Schema internă

Date
memorate

Fig. 1.2. Arhitectura internă a unui sistem de baze de date.

Toate aceste reprezentări ale datelor sunt gestionate de către SGBD care asigură, de asemenea, şi cele două
corespondenţe (mappings): între schemele externe şi schema conceptuală şi între schema conceptuală şi schema internă.
Unele sisteme SGBD nu separă complet cele trei niveluri funcţionale ale bazelor de date, existând posibilitatea
de a specifica detalii ale schemei interne sau ale schemelor externe în cadrul schemei conceptuale.

1.3 AVANTAJELE OFERITE DE SISTEMELE DE BAZE DE DATE


Faţă de vechile metode de înregistrare a datelor privind diferite activităţi pe fişe (documente scrise) sau chiar în
fişiere pe disc, sistemele de baze de date oferă avantaje considerabile, ceea ce explică extinsa utilizare a acestora.
Câteva dintre avantajele oferite sunt prezentate în continuare.
• Compactitate ridicată: volumul ocupat de sistemele de baze de date este mult mai redus decât volumul
ocupat de documente scrise sau de fişiere necorelate.
• Viteză mare de regăsire şi actualizare a informaţiilor.
• Redundanţă scăzută a datelor memorate, care se obţine prin partajarea datelor între mai mulţi utilizatori
şi aplicaţii. În stocarea pe fişe sau în fişiere a datelor, fiecare aplicaţie conţinea propriile seturi de date. În
sistemele de baze de date, mai multe aplicaţii pot folosi date comune, memorate o singură dată. De
exemplu, o aplicaţie de personal şi o aplicaţie de rezultate la examene dintr-o universitate care exploatează
o singură bază de date, pot folosi aceleaşi informaţii referitoare la structurarea facultăţilor şi a secţiilor.
• Posibilitatea de introducere a standardelor privind modul de stocare a datelor, ceea ce permite
interschimbul informaţiilor între diferite organizaţii.
• Menţinerea integrităţii datelor prin politica de securitate (drepturi de acces diferenţiate în funcţie de rolul
utilizatorilor), prin gestionarea tranzacţiilor şi prin refacerea datelor în caz de funcţionare defectuoasă a
diferitelor componente hardware sau software.
• Independenţa datelor faţă de suportul hardware utilizat. Sistemele de gestiune a bazelor de date oferă o
vedere (view) externă a datelor, care nu se modifică atunci când se schimbă suportul de memorare fizic,
ceea ce asigură imunitatea structurii bazei de date şi a aplicaţiilor la modificări ale sistemului hardware
utilizat.

1.4 CLASIFICAREA SISTEMELOR DE BAZE DE DATE


Se pot lua în consideraţie mai multe criterii de clasificare ale sistemelor de baze de date.
Clasificare după modelul de date. Majoritatea sistemelor de baze de date actuale sunt realizate în modelul de
date relaţional sau în modelul de date obiect. Dezvoltarea continuă a acestor modele a condus către o nouă categorie de
baze de date, numite obiect-relaţionale, care combină caracteristicile modelului relaţional cu cele ale modelului obiect.
De asemenea, mai sunt încă în funcţiune baze de date în modele mai vechi (modelul ierarhic sau modelul reţea).
Modelele de date utilizate de sistemele SGBD vor fi prezentate în secţiunea următoare.
Clasificare după numărul de utilizatori. Majoritatea sistemelor de baze de date sunt sisteme multiutilizator,
adică permit accesul concurent (în acelaşi timp) a mai multor utilizatori la aceeaşi bază de date. Un număr redus de
sisteme de baze de date sunt de tip monoutilizator, adică suportă accesul doar al unui singur utilizator (la un moment
dat).
Clasificare după numărul de staţii pe care este stocată baza de date. Există două categorii de sisteme de baze
de date: centralizate şi distribuite.
Un sistem de baze de date centralizat (Centralized Database System) este un sistem de baze de date în care
datele şi sistemul de gestiune sunt stocate pe o singură staţie (calculator).

3
Un sistem centralizat poate suporta unul sau mai mulţi utilizatori, dar, în orice situaţie, datele şi sistemul de
gestiune rezidă în întregime pe o singură staţie.
Un sistem de baze de date distribuit (Distributed Database System) poate avea atât datele, cât şi sistemul de
gestiune, distribuite în mai multe staţii interconectate printr-o reţea de comunicaţie.
Sistemele de baze de date pot fi reprezentate din punct de vedere al funcţionării lor printr-o arhitectură de tip
client-server.
Într-un sistem centralizat (fig. 1.3) există un singur server, care este chiar sistemul SGBD, care răspunde
cererilor unui singur client (în sistemele mono-utilizator, fig. 1.3, a) sau mai multor clienţi (în sistemele multi-utilizator,
fig. 1.3, b), care accesează baza de date respectivă. Clienţii sunt programe de aplicaţii oferite de furnizorul sistemului de
gestiune sau dezvoltate de programatori.
Aplicaţiile client pot fi executate pe staţii diferite, conectate printr-o reţea de comunicaţie cu staţia pe care
rulează serverul. Această arhitectură permite o prelucrare distribuită a datelor şi, mai mult, o configurare a sistemului
adaptată cerinţelor de calcul particulare. Astfel, serverul bazei de date poate fi un sistem puternic, echipat corespunzător
(cu volum mare de memorie secundară), în timp ce fiecare client este o staţie personală, cu putere de calcul adecvată
aplicaţiei executate.
Aplicaţie Aplicaţie Aplicaţie Aplicaţie
Client Client Client Client

Reţea
de comunicaţie

Server Server
SGBD SGBD

BD BD
a b
Fig. 1.3. Sisteme de baze de date centralizate: a- monoutilizator; b- multiutilizator.
Sistemele de baze de date distribuite pot fi reprezentate într-un mod asemănător din perspectiva structurării
client-server (fig. 1.4).
O bază de date distribuită este o colecţie de date care aparţin din punct de vedere logic aceluiaşi sistem, dar
care pot să fie, din punct de vedere fizic, memorate în mai multe staţii de calcul (locaţii - sites) conectate printr-o reţea
de comunicaţie. Sistemul software care gestionează o astfel de bază de date se numeşte Sistem de Gestiune a Bazei de
Date Distribuite - SGBDD - (Distributed Database Management System - DDBMS). Aplicaţiile client rulează pe alte
staţii din reţea şi solicită servicii de la sistemul de gestiune distribuit.

Aplicaţie Aplicaţie Aplicaţie


Client Client Client

Reţea
de comunicaţie

Server Server
SGBD SGBD

BD BD

Fig. 1.4. Sistem de baze de date distribuit.


Există numeroase avantaje ale sistemelor de baze de date distribuite (creşterea capacităţii de stocare şi
prelucrare a datelor, creşterea disponibilităţii şi a partajării datelor, etc.), dar şi o creştere considerabilă a complexităţii
acestora.
Cea mai importantă cerinţă pe care trebuie să o îndeplinească sistemele de gestiune a bazelor de date distribuite
este de a asigura administrarea transparentă a datelor. Transparenţa se referă la capacitatea unui sistem distribuit de a
ascunde detaliile de implementare, astfel încât utilizatorii să poată accesa datele pe baza unui model de nivel înalt, fără a
fi necesară cunoaşterea exactă a modului de amplasare, replicare sau comunicare a datelor.
Sistemele de gestiune a bazelor de date distribuite comerciale nu oferă în momentul de faţă un nivel suficient
de transparenţă a localizării datelor, dar dezvoltarea continuă a acestora va putea să asigure în viitor această cerinţă.

4
1.5 MODELAREA DATELOR
Un model este o abstractizare a unui sistem, care captează cele mai importante trăsături caracteristice ale
sistemului (concepte), relevante din punct de vedere al scopului pentru care se defineşte modelul respectiv. Tehnica de
identificare a trăsăturilor caracteristice esenţiale ale unui sistem se numeşte abstractizare.
Un model de date stabileşte regulile de organizare şi interpretare a unei colecţii de date. În proiectarea bazelor
de date se folosesc, de regulă, mai multe modele de date, care se pot clasifica în două categorii: modele conceptuale de
nivel înalt şi modele specializate.
Un model conceptual de nivel înalt al datelor conţine o descriere concisă a colecţiilor de date care modelează
activitatea dorită (numită schemă conceptuală de nivel înalt), fără să detalieze modul de reprezentare sau de prelucrare a
datelor.
Modelele specializate de date (cum sunt: modelul ierarhic, modelul reţea, modelul relaţional, etc.) impun
anumite structuri speciale de reprezentare a mulţimilor de entităţi şi a asocierilor dintre acestea, structuri pe baza cărora
sunt dezvoltate sistemele de gestiune a bazelor de date. Într-un astfel de model de date, o bază de date este reprezentată
printr-o schemă conceptuală (logică) specifică. Trecerea de la modelul conceptual de nivel înalt la un model de date
specific reprezintă etapa de proiectare logică a bazei de date care asigură corespondenţa dintre schema conceptuală de
nivel înalt a bazei de date şi schema conceptuală specifică modelului de date respectiv.

1.5.1 Modele conceptuale de nivel înalt


Cel mai utilizat model conceptual de nivel înalt este modelul Entitate-Asociere (E-A) care reprezintă schema
conceptuală de nivel înalt a bazei de date prin mulţimi de entităţi şi asocieri dintre acestea. Dezvoltarea acestui model,
astfel încât să permită extinderea tipurilor de entităţi, este cunosută sub numele de model Entitate-Asociere Extins (E-
AE). Proiectarea modelului E-A sau al modelului E-AE este, de regulă, una din primele etape în proiectarea bazelor de
date, etapă numită proiectarea schemei conceptuale (care va fi prezentată în Capitolul 7).

1.5.1.1 Modelul Entitate-Asociere


Modelul Entitate-Asociere (Entity-Relationship Model), introdus în 1976 de P.S. Chen [Chen76], este un
model conceptual de nivel înalt al unei baze de date, care defineşte mulţimile de entităţi şi asocierile dintre ele, dar nu
impune nici un mod specific de structurare şi prelucrare (gestiune) a datelor.
Elementele esenţiale ale modelului Entitate-Asociere folosit în proiectarea bazelor de date sunt entităţile
(entities) şi asocierile dintre acestea (relationships).
O entitate (entity) este „orice poate fi identificat în mod distinctiv"; o entitate se referă la un aspect al
realităţii obiective care poate fi deosebit de restul universului şi poate reprezenta un obiect fizic, o activitate, un
concept, etc. Orice entitate este descrisă prin atributele sale.
Un atribut (attribute) este o proprietate care descrie un anumit aspect al unei entităţi.
Atributele prin care este descrisă o entitate se aleg pe baza criteriului relevanţei relativ la domeniul de interes
pentru care se defineşte modelul respectiv, astfel încât să asigure diferenţierea acelei entităţi faţă de restul universului.
Toate entităţile similare, care pot fi descrise prin aceleaşi atribute, aparţin unui acelaşi tip de entitate (entity
type), iar colecţia tuturor entităţilor de acelaşi tip dintr-o bază de date constituie o mulţime de entităţi (entities set). În
general, în modelul E-A se foloseşte aceeaşi denumire atât pentru un tip de entitate cât şi pentru mulţimea entităţilor de
acel tip.
De exemplu, tipul de entitate „angajat” (al unei instituţii) reprezintă orice persoană angajată a instituţiei, care
are o anumită funcţie şi primeşte un anumit salariu. Acest tip de entitate poate fi descris prin mai multe atribute, dintre
care o parte sunt atribute de identificare a persoanei (Nume,Prenume,DataNasterii,Adresa), iar altele sunt atribute
legate de activitatea acesteia în instituţia respectivă (Functie,Salariu).
Prin analogie cu modelul obiect, se poate spune că un tip de entitate corespunde unei clase, o entitate este o
instanţă a unui tip de entitate şi corespunde unui obiect, iar mulţimea entităţilor de un tip dat corespunde mulţimii
obiectelor (instanţelor) unei clase.
În proiectarea bazelor de date se consideră două categorii de entităţi: entităţi normale (puternice, obişnuite -
regular entities) şi entităţi slabe (dependente - weak entities).
Entităţile normale au o existenţă proprie în cadrul modelului, în timp ce entităţile slabe nu pot exista decât dacă
există o entitate normală (puternică) cu care sunt asociate. De exemplu, o entitate „dependent” poate să reprezinte o
persoană care depinde de un angajat al unei instituţii (adică se află în întreţinerea acestuia). O entitate „angajat” este o
entitate puternică, deoarece ea există în mod normal în modelul activităţii instituţiei, în timp ce o entitate “dependent”
este o entitate slabă: nu se va înregistra o astfel de persoană decât dacă părintele (susţinătorul) acesteia este angajat în
acea instituţie.
În proiectarea bazelor de date se definesc asocieri între mulţimile de entităţi componente, pentru a reprezenta
anumite aspecte ale realităţii pe care baza de date o modelează.
O asociere (relationship) este o corespondenţă între entităţi din două sau mai multe mulţimi de entităţi.

5
Gradul unei asocieri este dat de numărul de mulţimi de entităţi asociate. Asocierile pot fi binare (de gradul 2,
între 2 mulţimi de entităţi) sau multiple (între k mulţimi de entităţi, k > 2).
Asocierile binare sunt, la rândul lor, de trei categorii, după numărul elementelor din fiecare dintre cele două
mulţimi puse în corespondenţă de asocierea respectivă (fig. 1.5). Fiind date două mulţimi de entităţi, E1 şi E2, se
definesc următoarele categorii de asocieri binare:
• Asocierea “unul-la-unul” (one-to-one) este asocierea prin care unui element (entitate) din mulţimea E1 îi
corespunde un singur element din mulţimea E2, şi reciproc; se notează cu 1:1.
• Asocierea „unul-la-multe” (one-to-many) este asocierea prin care unui element din mulţimea E1 îi
corespund unul sau mai multe elemente din mulţimea E2, dar unui element din E2 îi corespunde un singur
element în mulţimea E1; se notează cu 1:N.
• Asocierea „multe-la-multe” (many-to-many) este asocierea prin care unui element din mulţimea E1 îi
corespund unul sau mai multe elemente din mulţimea E2 şi reciproc; se notează cu M:N.
Cardinalitatea (multiplicitatea) unei asocieri faţă de o mulţime de entităţi (cardinality, multiplicity) este
numărul maxim de elemente din acea mulţime care pot fi asociate cu un element din altă mulţime a asocierii.

Aa Ab Ac
r1 e21 r1 e21
e11 r1 e21 e11 r2 e22 e22
e11 r2
r2 r3 e23 r3 e23
e12 e22 e12
e12 r4 e24 r4 e24
e13 r3 e23 r5 r5
e25 e13 r6 e25
r4 e24 e13 r6 e26 e14 r7 e26
e14 r7 e27

E1 E2 E1 E2 E1 E2
a b c
Fig. 1.5. Categorii de asocieri între două mulţimi de entităţi: a - asociere 1:1; b - asociere 1:N; c- asociere M:N.

De exemplu, asocierea 1:N dintre mulţimile E1 şi E2 prezintă multiplicitatea 1 faţă de mulţimea E1 şi


multiplicitatea N (se înţelege o valoare oarecare N > 1) faţă de mulţimea E2. Raportul dintre valorile cardinalităţilor unei
asocieri binare faţă de cele două mulţimi de entităţi se numeşte raport de cardinalitate (cardinality ratio). Se poate
observa că cele trei categorii de asocieri descrise mai sus diferă între ele prin raportul de cardinalitate.
Asocierile multiple (k-are, k > 2) prezintă câte un raport de cardinalitate pentru fiecare pereche de mulţimi de
entităţi pe care le asociază.
O asociere între două sau mai multe mulţimi de entităţi este, în acelaşi timp, o asociere între tipurile de entităţi
corespunzătoare.
Diagrama Entitate-Asociere (Entity-Relationship Diagram) reprezintă modelul Entitate-Asociere prin
mulţimile de entităţi şi asocierile dintre acestea.
Există numeroase variante de notaţii pentru redarea diagramei E-A. Una dintre cele mai folosite notaţii
reprezintă un tip de entitate (precum şi mulţimea de entităţi de acel tip) printr-un dreptunghi, iar atributele tipului de
entitate prin elipse conectate printr-o linie continuă la acesta (fig. 1.6). Pentru entităţile puternice se utilizează un
dreptunghi încadrat cu o linie simplă, iar pentru entităţile slabe se utilizează un dreptunghi încadrat cu linie dublă.

Tip entitate Tip de entitate puternică

Tip entitate Tip de entitate slabă

Nume
Atribut
atribut

N
Asociere binară 1:N
1 A
E1 E2 între 2 tipuri de entităţi

Fig. 1.6. Notaţiile diagramei Entitate-Asociere (E-A).

O asociere (tip de asociere) dintre două sau mai multe tipuri de entităţi se reprezintă printr-un romb conectat
prin link-uri (linii continue, formate din unul sau mai multe segmente) la tipurile de entităţi asociate. O asociere poate

6
să aibă sau nu un nume; dacă are un nume, acesta poate fi înscris în rombul respectiv sau în vecinătatea acestuia.
Categoria asocierii se notează prin înscrierea multiplicităţii pe fiecare link care conduce la un tip de entitate. Este posibil
ca o asociere să prezinte ea însăşi atribute, şi aceste atribute se reprezintă prin elipse conectate la asocierea respectivă.
Exemplul 1.1. În continuare se exemplifică dezvoltarea modelului conceptual de nivel înalt al unei baze de
date a unei instituţii şi diagrama E-A corespunzatoare, definind câteva tipuri de entităţi şi asocierile între acestea.
Diagrama E-A a acestui mic model de bază de date este prezentată în figura. 1.7.

Număr Buget Nume Salariu

1 N
SECTIE ANGAJAT

Cuprinde 1 M
Intretine Lucreaza

N N

DEPENDENT PROIECT

Nume GradRudenie Nume Buget

Fig. 1.7. Exemplu de diagramă E-A.

Tipul de entitate „secţie” reprezintă o unitate de organizare a instituţiei şi este un tip de entitate puternică (de
sine stătătoare). Acest tip se caracterizează prin mai multe atribute, de exemplu, un număr al secţiei, numele secţiei şi
bugetul alocat. Mulţimea de entităţi care grupează toate entităţile de acest tip se poate denumi SECTIE sau SECTII
(oricare variantă poate fi folosită) şi este caracterizată prin aceleaşi atribute care caracterizează tipul entităţii:
SECTIE(Numar,Nume,Buget)
Tipul de entitate „angajat” reprezintă o persoană angajată în instituţie şi este de asemenea un tip de entitate
puternică. Acest tip de entitate, ca şi mulţimea de entităţi care grupează toate entităţile de acest tip, se poate defini astfel
ANGAJAT(Nume,Prenume,DataNasterii,Adresa,Functie,Salariu)
Tipul de entitate „proiect” reprezintă o activitate a instituţiei, şi este de asemenea un tip de entitate puternică,
care poate fi caracterizat prin numele proiectului, data începerii, termen de realizare, bugetul proiectului:
PROIECT(Nume,DataInceperii,Termen,Buget)
Tipul de entitate „dependent” reprezintă o persoană care depinde de un angajat al instituţiei (adică se află în
întreţinerea acestuia). Acest tip de entitate este un tip de entitate slabă: nu se va înregistra o astfel de persoană decât
dacă întreţinătorul acesteia este angajat în acea instituţie. Acest tip se poate defini astfel:
DEPENDENT(Nume,Prenume,DataNasterii,GradRudenie)

Asocierea SECTIE-ANGAJAT este o asociere 1:N, dacă se consideră că o secţie cuprinde mai mulţi angajaţi,
iar un angajat aparţine unei singure secţii.
Asocierea ANGAJAT-PROIECT este o asociere M:N, dacă se consideră că la fiecare proiect lucrează mai
mulţi angajaţi, şi fiecare angajat poate lucra la mai multe proiecte.
Asocierea ANGAJAT-DEPENDENT este o asociere de tipul 1:N, deoarece un angajat poate întreţine mai multe
persoane (fii, părinţi etc.), iar o persoană dependentă este în întreţinerea unui singur susţinător.
Raportul de cardinalitate al unei asocieri este stabilit de proiectant astfel încât să reflecte cât mai corect modul
de organizare a activităţii modelate. De exemplu, asocierea ANGAJATI-PROIECTE are raportul de cardinalitate M:N
dacă în instituţia respectivă se admite ca un angajat să lucreze la mai multe proiecte; dacă s-ar impune ca un angajat să
lucreze la un singur proiect, atunci asocierea respectivă ar avea raportul de cardinalitate N:1. În ambele situaţii se
admite că la un proiect lucrează mai mulţi angajaţi.
Sunt de remarcat câteva caracteristici generale ale modelului E-A:
a) Modul de stabilire a tipurilor de entităţi şi a asocierilor dintre acestea nu este unic, deoarece graniţa dintre
entităţi şi asocieri nu este, în general, una bine precizată. Aceeaşi funcţionalitate se poate obţine printr-o varietate de
diagrame E-A, depinzând de felul în care proiectantul dezvoltă modelul conceptual. O asociere poate fi considerată şi
ca un tip de entitate. De exemplu, pentru baza de date a unei facultăţi (şcoli) se definesc tipurile (mulţimile) de entităţi:

7
STUDENTI(Nume,Prenume,Adresa,...)
DISCIPLINE(Denumire,Credite,...)

Între aceste mulţimi de entităţi se poate defini asocierea STUDENTI-DISCIPLINE, cu raportul de


cardinalitate M:N. Această asociere reprezintă (în general) studierea unor discipline de către studenţi, cu atribute ca:
Nota (examenului la disciplina respectivă), DataExamen, etc. Dar, la fel de bine, este posibil să se definească tipul
de entitate NOTE, aflat în asociere N:1 cu fiecare din tipurile de entităţi STUDENTI şi DISCIPLINE (fig. 1.8).
Studiaza
STUDENTI M N
DISCIPLINE (a)

1 N N 1
STUDENTI NOTE DISCIPLINE (b)

Fig. 1.8. Diferite moduri de definire a tipurilor de entităţi şi a asocierilor:


a- asociere M:N între mulţimile de entităţi STUDENTI şi DISCIPLINE;
b - mulţimea de entităţi EXAMENE este asociată cu raportul de cardinalitate N:1
cu fiecare din mulţimile de entităţi STUDENTI şi DISCIPLINE.

b) În modelul E-A, tipul de entitate (şi mulţimea de entităţi corespunzătoare) semnifică un substantiv, în timp
ce o asociere semnifică un verb. Bineînţeles, nu este obligatoriu ca numele dat unei asocieri să fie un verb (şi, de cele
mai multe ori, nici nu este), dar o asociere reprezintă o interacţiune între tipurile de entităţi (şi mulţimile de entităţi
corespunzătoare), care poate fi exprimată printr-un verb. De exemplu, în diagrama E-A din figura 1.7, asocierea
SECTIE-ANGAJAT poate fi exprimată prin verbul cuprinde, asocierea ANGAJATI-DEPENDENTI poate fi exprimată
prin verbul întreţine, asocierea ANGAJATI-PROIECTE poate fi exprimată prin verbul lucrează etc.
c) Modelul E-A nu precizează modul cum sunt realizate asocierile între mulţimile de entităţi. Acest aspect
depinde de modelul de date specializat utilizat pentru definirea bazei de date. De exemplu, în modelele ierarhic şi reţea,
asocierile sunt realizate explicit, prin pointeri de la o entitate la entităţile asociate, în timp ce în modelul relaţional
asocierea se realizează prin egalitatea valorilor unor atribute comune ale entităţilor (chei).

1.5.1.2 Modelul Entitate-Asociere Extins

Modelul Entitate-Asociere Extins (Enhanced Entity-Relationship Model) permite definirea de subtipuri ale
unui tip de entităţi, care moştenesc atribute de la tipul de entitate pe care il extind (şi care, în acest context, se numeşte
supertip) şi au în plus atribute specifice semnificaţiei lor. Prin definirea tipurilor şi a subtipurilor de entităţi se pot crea
ierarhii de tipuri de entităţi pe mai multe niveluri.
Modelul E-A prezentat în capitolul precedent este suficient pentru modelarea aplicaţiilor de baze de date
„tradiţionale”, adică bazele de date utilizate pentru activităţi financiare şi industriale, în care se folosesc tipuri de date
simple. Odată cu dezvoltarea sistemelor de baze de date, domeniile în care acestea se folosesc au devenit tot mai
numeroase, ca, de exemplu: telecomunicaţiile, proiectarea tehnologică, sistemele de informaţii geografice, seviciul
Web, etc. Tipurile de entităţi definite în astfel de baze de date sunt mult mai complexe şi pentru reprezentarea lor cât
mai intuitivă şi mai compactă au fost propuse mai multe concepte noi, care au fost introduse în modelul E-A extins.
Modelul E-A extins se reprezintă printr-o diagramă E-A extinsă. Ierarhiile de tipuri se pot crea prin specializare
sau generalizare.
Specializarea (specialization) este un proces de abstractizare a datelor prin care, pornind de la un tip de
entitate dat, se definesc unul sau mai multe subtipuri, diferenţiate între ele în funcţie de rolul specific pe care îl au în
modelul de date.
De exemplu, pornind de la tipul de entitate ANGAJAT se definesc prin specializare subtipurile SECRETARA,
TEHNICIAN, INGINER, pentru a diferenţia funcţiile pe care angajaţii le pot avea în întreprinderea respectivă (fig. 1.9).
Litera “d” din marcajul de specializare a tipurilor indică o constrângere de disjuncţie impusă specializării, care va fi
descrisă mai jos.
Subtipurile de entităţi moştenesc atribute ale tipului iniţial şi fiecare dintre ele are atribute suplimentare,
specifice rolului lor. De exemplu, atributele (Nume,Prenume,DataNasterii,Adresa,Salariu) ale tipului de
entitate ANGAJAT sunt moştenite de fiecare din subtipurile acestuia. Atributul Functie nu este moştenit, deoarece
specializarea subtipurilor s-a efectuat după acest atribut. Ca atribute specifice, subtipul SECRETARA are atributul
VitezaRedactare, care este o măsură a calificării, subtipul TEHNICIAN are atributul Calificare, care reprezintă
gradul de calificare, iar subtipul INGINER are atributul Specialitate, care este o precizare a domeniului in care
lucrează (mecanic, electric, etc.).
Generalizarea (generalization) este procesul de abstractizare invers specializării, prin care se crează un
supertip de entitate pornind de la mai multe tipuri de entităţi.
Pentru definirea unei generalizări, se identifică atributele comune ale mai multor tipuri de entităţi şi aceste
atribute vor caracteriza supertipul de entitate, iar atributele care diferă de acestea rămân specifice fiecărui tip.

8
De exemplu, dacă au fost definite tipurile de entităţi: AUTOMOBIL (NrInregistrare, Marca,
VitezaMaxima, Pret, NumarPasageri) şi CAMION(NrInregistrare,Marca,VitezaMaxima,Pret,Tonaj),
se poate defini un supertip al acestor tipuri: VEHICUL(NrInregistrare,Marca, VitezaMaxima,Pret). Acest tip
va cuprinde toate atributele comune, iar tipurile iniţiale, AUTOMOBIL şi CAMION, devin subtipuri ale tipului VEHICUL,
fiecare conţinând atributele specifice (NumarPasageri pentru tipul AUTOMOBIL şi Tonaj pentru tipul CAMION).
Rezultatul obţinut prin generalizare este, ca şi în cazul specializării, o ierarhie de tipuri de entităţi; ceea ce
diferă este modul în care se definesc nivelurile ierarhiei.

Nume Prenume DataNasterii Adresa Salariu

ANGAJAT

SECRETARA TEHNICIAN INGINER

VitezaRedactare Calificare Specialitate

Fig.1.9. Diagrama E-A extinsă cu ierarhie de tipuri de entităţi.

Moştenirea atributelor. Proprietatea principală a ierarhiilor de tipuri de entităţi create prin specializare sau
generalizare este moştenirea atributelor: atributele tipurilor de entităţi de nivel ridicat (supertipuri) sunt moştenite de
tipurile de entităţi de nivel scăzut (subtipuri).
Moştenirea dintre un subtip de entităţi şi supertipul acestuia se reprezintă în diagrama E-A extinsă printr-o
legătură (link) între subtip şi supertipul de entitate corespunzător pe care este plasat un semicerc orientat către subtip
(aşa cum se poate vedea în figura 1.9).
Este evidentă asemănarea dintre ierarhiile de tipuri de entităţi din modelul E-A extins şi ierarhiile de clase din
modelul obiect-orientat, dar modelul E-A extins este un model de date mult mai general (de nivel inalt), care poate fi
transpus în diferite modele de date specializate, inclusiv modelul obiect-orientat. Aceste transpuneri se fac în funcţie de
suportul oferit de modelul specializat respectiv pentru reprezentarea entităţilor, asocierilor, moştenirilor, etc.

1.5.1.3 Modelul de date ierarhic

În modelul ierarhic (Hierarchical Model) o bază de date se reprezintă printr-o structură ierarhică de
înregistrări de date (records) conectate prin legături (links).
Modelul de date ierarhic a fost primul model folosit pentru dezvoltarea bazelor de date. Cea mai cunoscută
realizare de SGBD ierarhic este sistemul IMS (Information Management System) dezvoltat de firma IBM în cadrul
programului de cercetări Apollo, în perioada anilor 1960.
O înregistrare de date în modelul ierarhic este o instanţă a unui tip de înregistrare (record type) şi constă dintr-
o colecţie de câmpuri (fields), fiecare câmp conţinând valoarea unui atribut. Un tip de înregistrare corespunde unui tip
de entitate, iar o înregistrare corespunde unei entităţi din modelul E-A.
Un tip de legătură în modelul ierarhic este un tip de asociere cu raportul de cardinalitate 1:N (de tip părinte-
fiu) între două tipuri de înregistrări. Tipul de înregistrare din partea cu multiplicitatea 1 a asocierii este numit tip de
înregistrare părinte, iar tipul din partea cu multiplicitatea N a asocierii este numit tip de înregistrare fiu.
Schema conceptuală a unei baze de date în modelul ierarhic se reprezintă printr-un număr oarecare de scheme
ierarhice (fig. 1.12). O schemă ierarhică este un arbore direcţionat, reprezentat pe mai multe niveluri, în care nodurile
sunt tipurile de înregistrări, iar arcele sunt tipurile de legături. Fiecare nod (cu excepţia nodului rădăcină) are o singură
legătură către un nod de pe un nivel superior (nodul părinte) şi fiecare nod (cu excepţia nodurilor frunză) are una sau
mai multe legături către noduri de pe nivelul imediat inferior (noduri fii).
Se poate stabili o corespondenţă între o schemă conceptuală ierarhică şi o diagramă E-A: tipurile de înregistrări
corespund tipurilor de entităţi, iar tipurile de legături corespund tipurilor de asocieri. (fig. 1.12, a şi b).
În modelul ierarhic nu sunt admise decât legături de tipul părinte-fiu, care corespund asocierilor 1:1 şi
asocierilor 1:N din modelul E-A. Asocierile M:N din modelul E-A nu se pot reprezenta în mod direct în modelul
ierarhic, ci numai prin multiplicarea înregistrărilor de tip fiu, atunci când sunt referite de mai multe înregistrări de tip
părinte. Acest lucru conduce la o mare redundanţă a datelor.
Corespunzător schemei ierarhice a unei baze de date se pot reprezenta mai mulţi arbori de instanţiere a datelor,
care sunt, de asemenea, arbori direcţionaţi (fig. 1.12, c). Fiecare arbore de instanţiere contine ierarhii pe mai multe
niveluri de înregistrări între care există legături de tipul părinte-fiu.

9
FACULTATE
FACULTATE FACULTATE
f1 f2 f3
1

N PROFESOR
PROFESOR PROFESOR
p1 p2 p3
M
STUDENT
N
STUDENT STUDENT s1 s2 s1
s3 s2 s4

a b c
Fig. 1.12. Bază de date ierarhică: a - diagrama E-A;
b - schema conceptuală a bazei de date ierarhice; c - arbori de instanţiere a datelor.

Corespunzător schemei ierarhice a unei baze de date se pot reprezenta mai mulţi arbori de instanţiere a datelor,
care sunt, de asemenea, arbori direcţionaţi (fig. 1.12, c). Fiecare arbore de instanţiere contine ierarhii pe mai multe
niveluri de înregistrări între care există legături de tipul părinte-fiu.
Avantajele modelul ierarhic sunt simplitatea şi eficienţa de calcul, dar în momentul de faţă, pentru realizarea
bazelor de date sunt preferate modele de date mai puternice (modelul relaţional, modelul obiect-orientat).

1.5.1.4 Modelul de date reţea

Modelul reţea (Network Model) foloseşte o structură de graf pentru definirea schemei conceptuale a bazei de
date; nodurile grafului sunt tipuri de entităţi (înregistrări - records), iar muchiile grafului reprezintă în mod explicit
asocierile (legăturile-links) dintre tipurile de entităţi.
Apărut după modelul ierarhic, modelul reţea de reprezentare a bazelor de date a fost standardizat în 1971, de o
comisie DBTG (Database Task Group). Modelul reţea a avut mai multe implementări ca sisteme de gestiune
comerciale: IDS II (Honeywell), UNISYS (Burroughs), IDMS (Computer Associates).
Deosebirea faţă de modelul ierarhic constă în aceea că în modelul reţea asocierile M:N se reprezintă fără
duplicarea înregistrărilor, fiecare înregistrare putând fi referită de mai multe înregistrări, ceea ce elimină redundanţa.
La fel ca şi la modelul ierarhic, dezavantajul principal al modelului reţea este acela că fiecare interogare trebuie
să fie prevăzută încă din faza de proiectare, prin memorarea explicită a legăturilor între tipurile de entităţi. În plus,
complexitatea reprezentării datelor în modelul reţea este deosebit de ridicată, iar programatorii trebuie să o cunoască
pentru a putea realiza aplicaţiile necesare.
În momentul de faţă modelul de date reţea este foarte rar utilizat pentru baze de date de uz general (care
implică operaţii de interogare), dar există unele domenii în care structurarea ca graf a datelor permite o parcurgere
eficientă a acestora. De exemplu, majoritatea bazelor de date grafice folosite în modelarea scenelor tridimensionale din
realitatea virtuală sunt baze de date reţea [Ion96a].

1.5.1.5 Modelul de date relaţional

Modelul relaţional (Relational Model) se bazează pe noţiunea de relaţie (relation) din matematică, care
corespunde unei mulţimi de entităţi de acelaşi tip.
Modelul de date relaţional a fost propus de cercetătorul E.F. Codd de la compania IBM, care a publicat în anul
1970 lucrarea "Un model Relaţional de Date pentru Bănci Mari de Date Partajate" [Codd70]. Alte lucrări ale lui Codd,
ca şi ale altor cercetători (C.J. Date, P. Chen, R. Boyce, J.D. Ullman, R. Fagin, W.W. Armstrong, M. Stonebraker, etc.)
au perfecţionat modelul de date relaţional şi au permis dezvoltarea fără precedent a sistemelor de gestiune a bazelor de
date, datorită simplităţii şi a fundamentării matematice a modelului.
Primul Sistem de Gestiune a Bazelor de Date Relaţionale (SGBDR) a fost prototipul System R, dezvoltat la
compania IBM în anii 1970, după care numeroase companii au realizat sisteme de gestiune relaţionale (Oracle,
Microsoft, Ingres, Sybase, etc.) iar aplicaţiile de baze de date relaţionale au căpătat o amploare deosebită.
Pe lângă avantajul unui model de date precis şi simplu, sistemele de baze de date relaţionale mai beneficiază şi
de un limbaj de programare unanim recunoscut şi acceptat, limbajul SQL (Structured Query Language), pentru care au
fost emise mai multe standarde de către ISO (International Standardization Office). Majoritatea SGBD-urilor
relaţionale actuale implementează versiunea SQL92 (sau SQL2).

1.5.1.6 Modelul de date obiect-orientat

Modelul obiect (Object Model) este un concept unificator în ştiinţa calculatoarelor, fiind aplicabil în
programare, în proiectarea hardware-ului, a interfeţelor, a bazelor de date, etc. Sistemele de baze de date obiect-

10
orientate se bazează pe limbaje de programare obiect-orientate cu capacităţi de persistenţă, în care datele sunt
independente de timpul de viaţă al programelor care le creează, prin memorare pe suport magnetic (disc).
Oricât de folositor este modelul de date relaţional pentru realizarea bazelor de date, există unele domenii (în
special acele domenii în care se manevrează tipuri de date complexe), în care modelul relaţional s-a dovedit a fi
insuficient de expresiv şi cu performanţe de execuţie reduse. Domenii ca: proiectarea asistată de calculator, sisteme de
informaţii geografice, medicină (şi altele) au impulsionat cercetări pentru găsirea unor modele mai performante, dintre
care modelul obiect-orientat şi modelul obiect-relaţional au cunoscut şi cunosc în continuare o dezvoltare semnificativă.
Caracteristicile importante ale modelului obiect (abstractizarea, moştenirea, încapsularea, modularitatea) sunt
intens dezbătute şi analizate mai ales din perspectiva proiectării şi programării obiect-orientate [Booch87], [Con00].
Din perspectiva realizării bazelor de date, o altă proprietate a modelul obiect, persistenţa, este aceea care
asigură memorarea transparentă pe suport magnetic a obiectelor care alcătuiesc o bază de date obiect-orientată.
Pentru dezvoltarea unui sistem de gestiune a bazelor de date obiect- orientate (SGBDOO) se poate aborda una
din următoarele strategii:
• Extinderea unui limbaj de programare obiect-orientat cu capacităţi de administrare a obiectelor persistente.
Sistemul GemStone este un astfel de SGBDOO, dezvoltat prin extinderea limbajelor C++ şi Java.
• Extinderea unui limbaj de programare relaţional cu capacităţi de orientare spre obiecte. Un astfel de limbaj
este limbajul ODL (Object Query Language) (sau Object SQL), specificat prin standardul propus de
consorţiul Object Database Management Group, din care fac parte principalii producători de sisteme de
baze de date obiect-orientate. Există mai multe astfel de sisteme, cum sunt: Ontos, Versant, O2.
• Dezvoltarea unui limbaj obiect-orientat pentru baze de date complet nou, care să asigure crearea şi
interogarea obiectelor persistente. Există şi astfel de produse, ca de exemplu sistemul SIM (Semantic
Information Manager).
Dintre avantajele cele mai importante ale sistemelor de baze de date dezvoltate în modelul obiect se
evidenţiază capacitatea acestora de a defini şi manevra tipuri de date complexe (clase), care se pot extinde prin
mecanismul de moştenire, ceea ce contribuie la creşterea performanţelor în aplicaţiile de baze de date avansate.
Există, bineînţeles, şi dezavantaje ale sistemelor de baze de date obiect-orientate, care le fac să aibă o utilizare
limitată, mult mai redusă decât cea a sistemelor de baze de date relaţionale (sub 5% din piaţa sistemelor de baze de
date). Principalul dezavantaj îl constitue complexitatea de dezvoltare a bazei de date şi a aplicaţiilor, datorită faptului că
proiectanţii şi programatorii trebuie să prevadă în structura obiectelor toate asocierile (legăturile) necesare tuturor
interogărilor. Cu cât interogările sunt mai complexe, cu atât sunt necesare mai multe asocieri între obiecte şi deci se
complică structura acestora. La acest dezavantaj se adaugă şi altele, cum ar fi lipsa unui standard de limbaj de interogare
care să fie unanim (sau cât mai larg) acceptat.

1.5.1.7 Modelul de date obiect-relaţional

Modelul obiect-relaţional (Object-Relational Model) reprezintă extinderea modelului relaţional cu


caracteristici ale modelului obiect, extindere necesară pentru realizarea bazelor de date care definesc şi prelucrează
tipuri de date complexe.
În esenţă, modelul obiect-relaţional păstrează structurarea datelor în relaţii (reprezentate ca tabele), dar adaugă
posibilitatea definirii unor noi tipuri de date, pentru domeniile de valori ale atributelor. Tipurile de date definite de
utilizator pot fi extinse prin mecanismul de moştenire şi pentru fiecare tip sau subtip se pot defini metode pe care le pot
executa obiectele de acel tip.
În general, dezvoltarea sistemelor de gestiune a bazelor de date obiect-relaţionale (SGBDOR) se realizează
prin extinderea sistemelor relaţionale, de cele mai multe ori în mod gradat, adăugându-se de la o versiune la alta cât mai
multe caracteristici posibile ale modelului obiect şi păstrând în continuare toate caracteristicile modelului relaţional.
O astfel de abordare asigură rularea în continuare a aplicaţiilor relaţionale existente în noile versiuni de sisteme
SGBDOR, ceea ce permite producătorilor să-şi păstreze clienţii şi domeniile de utilizare. Mai mulţi dintre principalii
producători de sisteme de gestiune (Oracle, Informix şi IBM) au extins în acest mod sistemele lor relaţionale pentru a
deveni sisteme obiect-relaţionale. Este o tendinţă firească, dat fiind că prin aceasta se păstrează toată experienţa şi
rezultatele obţinute cu sistemele relaţionale şi se pot dezvolta şi aplicaţii complexe, obiect-relaţionale.
Standardele limbajelor de programare pentru sistemele de gestiune obiect-relaţionale sunt extensii ale
standardului SQL (ca de exemplu, versiunea din anul 1999, denumită SQL3).

1.5.1.8 Complexitatea datelor şi a interogărilor

M. Stonebraker a oferit o reprezentare în patru cadrane a universului bazelor de date (fig. 1.13) deosebit de
simplă şi de interesantă, bazată numai pe complexitatea datelor şi a interogărilor [Ston96]. Propusă în anul 1996,
această clasificare nu include modelele prerelaţionale (modelul ierarhic şi modelul reţea), considerate depăşite în
această fază de dezvoltare a bazelor de date.
Pe abscisa diagramei este reprezentată capacitatea de definire a tipurilor de date complexe, iar pe ordonată este
reprezentată capacitatea de interogare a bazelor de date.

11
În cadranul din stânga jos sunt acele aplicaţii care prelucrează tipuri de date simple şi nu necesită interogarea
datelor. Astfel de tipuri de aplicaţii (cum sunt procesoarele de texte – Word, Framemaker) folosesc direct sistemul de
fişiere al sistemului de operare pentru memorarea datelor persistente.

Complexitatea
interogărilor
SGBDR SGBDOR

Sisteme de fişiere SGBDOO

Complexitatea datelor
Fig. 1.13. Clasificarea sistemelor de gestiune a bazelor de date.

În cadranul din stânga sus sunt sistemele de gestiune a bazelor de date relaţionale (SGBDR), care prelucrează
tipuri simple de date, dar permit interogări complexe.
În cadranul din dreapta jos sunt sistemele de gestiune a bazelor de date obiect-orientate (SGBDOO), care
prelucrează tipuri de date complexe, dar în care rezolvarea interogărilor este destul de dificilă, dat fiind că pentru
fiecare interogare trebuie să fie prevăzute legăturile necesare în structura obiectelor.
În cadranul din dreapta sus sunt reprezentate sistemele obiect-relaţionale (SGBDOR), care permit prelucrarea
datelor complexe şi rezolvarea interogărilor complexe. Modelul obiect-relaţional este, evident, cel mai complet,
deoarece admite atât tipuri de date definite de utilizator cât şi interogări complexe. În aceeaşi lucrare, Stonebraker
denumeşte sistemele de gestiune a bazelor de date obiect-relaţionale ca fiind sisteme de baze de date universale.
În momentul de faţă este evidentă tendinţa producătorilor de sisteme de gestiune a bazelor de date de a trece la
sisteme obiect-relaţionale şi, în general, această trecere se realizează prin adăugarea treptată a caracteristicilor
modelului obiect în sistemele de gestiune relaţionale. Oferta de sisteme de gestiune a bazelor de date este deosebit de
generoasă, pe o scară extinsă de performanţe şi costuri, de la sisteme care se pot folosi gratuit (fără licenţă sau cu licenţă
publică), până la sisteme cu înalte performanţe, a căror utilizare necesită plata licenţelor respective. Chiar şi pentru
astfel de sisteme există versiuni de test (trial versions) care pot fi obţinute gratuit prin Internet (de la adrese care sunt
indicate în Bibliografie), astfel încât pot fi folosite pentru a înţelege şi a executa exemplele propuse în această lucrare.
Sistemul Oracle este un sistem de gestiune a bazelor de date multi-utilizator puternic, cu implementări pe toate
platformele (Windows, Unix, Linux), care oferă atât performanţe de execuţie ridicate, cât şi un grad înalt de protecţie şi
securitate a datelor. În toate versiunile, Oracle oferă implementarea completă a caracteristicilor modelului relaţional
(conform standardului SQL2), iar ultimele versiuni (Oracle8i, Oracle9i şi Oracle 10g) sunt sisteme de gestiune obiect-
relaţionale distribuite, implementând extensiile obiect-orientate prevăzute în standardul SQL3 şi oferind posibilitatea de
dezvoltare a bazelor de date distribuite. Sistemele de gestiune Oracle, ca şi diferite instrumente de dezvoltare a
aplicaţiilor de baze de date (Oracle Application Server, JDeveloper, Oracle Forms etc.), se pot obţine de la adresa
http://www.oracle.com şi termenii licenţei permit utilizarea acestor sisteme în scopuri necomerciale pe o perioadă
nelimitată; pentru utilizarea în scopuri comerciale trebuie să fie plătite licenţele corespunzătoare
SQL Server este sistemul de gestiune a bazelor de date relaţionale dezvoltat de firma Microsoft pentru
sistemele de operare Windows. Au existat mai multe versiuni, versiunea actuală (2007) fiind SQL Server 2005. În toate
versiunile sistemul SQL Server suportă complet standardul SQL2, cu implementarea performantă a trăsăturilor avansate
de stocare şi prelucrare a datelor (integritate referenţială, subinterogări, triggere, gestiunea tranzacţiilor, etc). De la
adresa http://www.microsoft.com/sql se poate obţine gratuit o versiune de test a sistemului SQL Server sau se poate
cumpăra o versiune completă. În plus, pachetul de dezvoltare .NET SDK (.NET Software Development Kit), care se
poate obţine gratuit de la adresa http://msdn.microsoft.com/downloads conţine o versiune mai simplă de server de baze
de date numit Microsoft SQL Server 2000 Desktop Engine (MSDE 2000) care poate fi folosită pentru dezvoltarea şi
execuţia exemplelor prezentate în lucrare.
Microsoft Access este unul din cele mai cunoscute sisteme de gestiune a bazelor de date relaţionale pe
platforme de calculatoare personale. MS Access dispune de un sistem de control al bazei de date (database engine) şi o
interfaţă grafică pentru interacţiunea cu utilizatorul. Aplicaţiile de baze de date în MS Access se pot dezvolta cu multă
uşurinţă datorită generatoarelor de aplicaţii (Wizards) care permit proiectarea vizuală a bazelor de date şi a formularelor
(forms) pentru interfeţele grafice. MS Access este folosit în special pentru aplicaţii personale sau pentru mici afaceri şi
licenţa acestuia se poate cumpăra odată cu licenţa produsului Microsoft Office.
MySQL este un sistem de gestiune a bazelor de date relaţionale cu implementări pentru sistemele de operare
Windows, Linux, Unix. La adresa http://www.mysql.com se găseşte ultima versiune şi documentaţia sistemului de
gestiune a bazelor de date MySQL care se poate utiliza gratuit (este open source). Acest sistem este compatibil cu
standardul SQL2, dar unele prevederi ale standardului sunt implementate parţial. Versiunea actuală 2007) este versiunea
5.0 care ofera vederi, proceduri stocate, triggere (caracteristici care lipseau in versiunile precedente).

12
2 MODELUL RELAŢIONAL
Modelul relaţional a avut, încă de la apariţia sa în anul 1970, o popularitate deosebit de mare, astfel
încât majoritatea sistemelor de baze de date care există şi se dezvoltă la ora actuală sunt sisteme relaţionale. Pe
lângă simplitatea şi precizia modului de definire a elementelor de bază ale modelului (relaţii, atribute, domenii),
există un suport teoretic substanţial pentru realizarea bazelor de date relaţionale (constrângerile de integritate,
normalizarea relaţiilor, controlul concurenţei), care permite crearea structurii datelor şi a prelucrării lor într-un
mod consistent şi asigură integritatea şi protecţia acestora.

2.1 RELAŢII
O bază de date relaţională este compusă dintr-o mulţime finită de relaţii, fiecare relaţie reprezentând
un tip de entitate sau o asociere dintre două sau mai multe tipuri (mulţimi) de entităţi.
Din această definiţie rezultă că într-o bază de date fiecare relaţie este unică (nu există două sau mai
multe relaţii de acelaşi fel), dat fiind că o bază de date este o mulţime de relaţii. O relaţie se defineşte prin
intermediul atributelor sale.
Atributele unei relaţii sunt atributele tipului de entitate sau de asociere pe care îl reprezintă relaţia
respectivă.
Fiecare atribut al unei relaţii are un domeniu de definiţie şi poate lua o singură valoare (din domeniul
său de definiţie) pentru fiecare tuplu al relaţiei, ceea ce înseamnă că atributele au numai valori scalare.
Un domeniu de definiţie (domain) este o mulţime cu nume de valori atomice de acelaşi tip, având o
anumită semnificaţie, din care îşi iau valori atributele relaţiilor.
Nu trebuie confundată atomicitatea din punct de vedere semantic a valorii unui atribut cu atomicitatea
structurii de date prin care este reprezentată valoarea acelui atribut în memoria calculatorului. De exemplu, un
atribut care desemnează un nume (de persoană, de instituţie, de produs, de componentă, etc.) se reprezintă
printr-un şir (vector) de caractere, care, bineînţeles, poate fi descompus în elementele componente (caracterele),
dar sistemul de gestiune nu atribuie nici o semnificaţie unui element separat, ci numai întregii valori (şirul de
caractere).
Schema relaţiei (relation schema), notată R(A1,A2,...Ai,...An), este compusă din numele
relaţiei (R) şi din lista ordonată a atributelor sale A1,A2,...Ai,..An, fiecare atribut Ai definit pe
domeniul său de definiţie, D(Ai).
Schema relaţiei este folosită pentru a descrie relaţia respectivă şi se mai numeşte şi tipul sau intensiunea
relaţiei. Numărul de atribute ale schemei unei relaţii se numeşte gradul relaţiei.
O relaţie (relation) R definită de schema R(A1,A2,...Ai,...An) este o mulţime de n-tupluri t,
fiecare tuplu fiind o listă ordonată de n valori t = <v1,v2,...vi,...vn>, unde 1 ≤ i ≤ n şi vi este
valoarea atributului Ai, aparţinând domeniului său de definiţie D(Ai).
Din această definiţie rezultă imediat că într-o relaţie nu există tupluri duplicat (două sau mai multe
tupluri identice), relaţia fiind o mulţime (în sens matematic) de tupluri. De asemenea, rezultă că în fiecare tuplu
un atribut nu poate lua decât o valoare din domeniul său de definiţie, deci un scalar, nu un vector (tablou) de
valori. Numărul de atribute ale relaţiei se numeşte gradul relaţiei (degree), iar numărul de tupluri se numeşte
cardinalitatea relaţiei (cardinality).
O relaţie corespunde, în general, unei mulţimi de entităţi din diagrama E-A a unei baze de date, iar un
tuplu reprezintă o entitate din mulţimea de entităţi. Atributele tipului de entitate din modelul conceptual de nivel
înalt devin atributele relaţiei în modelul relaţional.
Relaţia, ca mulţime de tupluri, fiecare tuplu fiind compus din valori ale atributelor sale, reprezintă o
variabilă - variabila relaţie - care se modifică în cursul existenţei sale, prin inserarea sau ştergerea tuplurilor, sau
prin modificarea (actualizarea) valorilor atributelor tuplurilor. Tipul acestei variabile este dat de schema relaţiei,
care nu se modifică o dată ce a fost definită (sau se modifică foarte rar). Valoarea la un moment dat a relaţiei se
mai numeşte şi starea sau extensiunea relaţiei. În general, pentru o exprimare mai concisă, nu se detaliază de
fiecare dată aceste aspecte şi se foloseşte termenul simplu de relaţie, atât pentru variabila relaţie cât şi pentru
valoarea relaţiei. De asemenea, în multe documentaţii, se foloseşte numai numele relaţiei (de exemplu, R), fără
lista de atribute, pentru a desemna schema relaţiei corespunzătoare.
Ca exemplu de relaţie se defineşte relaţia cu schema ANGAJATI (sau ANGAJAT) corespunzătoare
tipului de entitate “angajat” (descris în exemplele prezentate anterior) astfel:
ANGAJATI(Nume,Prenume,DataNasterii,Adresa,Functie,Salariu)
Domeniile de definiţie ale atributelor se specifică prin numele domeniului, semnificaţia acestuia, tipul
de date şi restricţii asupra valorilor în cadrul acelui tip de date. Exemple de domenii relaţionale ale atributelor:

1
• D_Nume: domeniul numelor de persoane reprezentate printr-un şir de maximum 20 caractere, în
care primul caracter este literă mare, iar celelalte pot fi litere mari sau mici.
• D_Data: domeniul datelor calendaristice, reprezentate prin 3 numere întregi, separate prin linii de
despărţire(-); primul număr reprezintă anul şi este format din patru cifre; al doilea număr reprezintă
luna şi are valori cuprinse între 01 şi 12; al treilea număr reprezintă ziua şi are valori cuprinse între
01 şi 31.
• D_Salariu: domeniul salariului, reprezentat printr-un număr zecimal.
Domeniile de definiţie a atributelor, D(A1),D(A2),...D(An), nu trebuie să fie neapărat distincte.
Atributele Nume şi Prenume din relaţia ANGAJATI se pot defini pe acelaşi domeniu, domeniul D_Nume;
atributul DataNasterii se defineşte pe domeniul D_Data, etc.
Cel mai frecvent însă, în implementarea modelului relaţional în cadrul diferitelor sisteme de gestiune a
bazelor de date, ca domenii de definiţie ale atributelor se folosesc direct tipurile de date predefinite ale limbajului
de definiţie a datelor (LDD) specific acelui SGBD. Acest mod simplificat de tratare a domeniilor de definiţie a
atributelor va fi evidenţiat în continuare, în secţiunea dedicată limbajului SQL.

2.1.1 Reprezentarea relaţiilor prin tabele


Un tabel (table) este o reprezentare a unei relaţii şi este compus din următoarele părţi:
• Numele tabelului, care este identic cu numele relaţiei pe care o reprezintă.
• Un număr de coloane egal cu numărul de atribute ale relaţiei, fiecare coloană reprezentând un
atribut.
• Capul tabelului, în care se înscriu numele atributelor relaţiei, fiecare atribut fiind înscris în
coloana corespunzătoare.
• O mulţime de linii, fiecare linie corespunzând unui tuplu (deci unei entităţi); în fiecare element al
unei linii se înregistrează valoarea atributului corespunzător coloanei în care se află elementul
respectiv.
De exemplu, tabelul corespunzător stării relaţiei ANGAJATI la un moment dat este prezentat în fig. 2.1.
Deşi în numeroase documentaţii se foloseşte termenul de tabel pentru a desemna relaţia pe care o
reprezintă, cele două noţiuni nu sunt identice: relaţia este o noţiune abstractă (o mulţime în sens matematic), în
timp ce tabelul este o reprezentare a relaţiei. Interpretarea tabelului ca relaţie este posibilă numai prin respectarea
unor reguli bine precizate: fiecare linie desemnează un tuplu; valorile unui atribut sunt înscrise pe aceeaşi
coloană pentru toate liniile; numele atributului corespunzător unei coloane este înscris în capul tabelului.

Numele relaţiei Atributele relaţiei

ANGAJATI
Nume Prenume DataNasterii Adresa Functie Salariu

Ionescu Ion 1945.01.05 Bucureşti Inginer 4000


Popescu Petre 1972.06.21 Bucuresti Tehnician 3500
Vasilescu Ana 1966.04.02 Bucuresti Secretara 3000

Fig. 2.1. Tabelul de reprezentare a relaţiei ANGAJATI.

Reprezentarea unei noţiuni abstracte (relaţia) într-o formă atât de uşor de interpretat (tabelul) este o
proprietate remarcabilă a modelului relaţional, deşi această reprezentare poate sugera unele aspecte ale
modelului relaţional care nu sunt reale.
De exemplu, tabelul sugerează o ordonare a tuplurilor (de sus în jos), ordonare care nu este impusă de
noţiunea de relaţie, definită ca o mulţime de tupluri. Chiar dacă, în implementările reale, o relaţie este memorată
ca un fişier pe disc, iar tuplurile relaţiei sunt înregistrări (records) ale fişierului memorate într-o anumită ordine
(există prima înregistrare, a doua înregistrare, etc.), definiţia relaţiei evidenţiază nivelul logic de organizare a
datelor, în care tuplurile nu sunt ordonate.

2
2.1.2 Ordonarea valorilor atributelor în tupluri
În definiţia relaţiei din secţiunea precedentă, tuplul este considerat o listă ordonată de n valori ale
atributelor (A1,A2,...Ai,...An) şi, în acest sens, reprezentarea printr-un tabel, în care coloanele sunt
ordonate, corespunde definiţiei relaţiei.
Aceasta este o definiţie mai simplă şi mai intuitivă a relaţiei; totuşi, din punct de vedere logic, ordinea
valorilor atributelor într-un tuplu nu are nici o importanţă, dacă se poate face o corespondenţă între un atribut şi
valoarea acestuia. Această structurare logică a relaţiei, în care atributele unui tuplu nu sunt ordonate, poate fi
exprimată prin următoarea definiţie a relaţiei:
Fie schema relaţiei definită ca o mulţime de atribute: R = {A1,A2, ...Ai,...An}. Relaţia R este o
mulţime de n-tupluri t, fiecare tuplu compus dintr-o mulţime de n perechi ordonate t =
{<A1,v1>,<A2,v2>,...<Ai,vi>, ...<An,vn>}, unde vi este valoarea atributului Ai, din domeniul
său de definiţie.
În această definiţie ordinea atributelor nu mai contează, dat fiind că numele atributului şi valoarea lui
sunt grupate împreună şi, de aceea, această definiţie a relaţiei este mult mai generală.
În implementările reale, valorile atributelor sunt câmpuri (fields) memorate într-o anumită ordine în
cadrul înregistrării (record) corespunzătoare unui tuplu, deci există o anumită ordine a valorilor atributelor, dar
acesta nu este relevantă din punct de vedere logic şi de aceea se poate considera că valorile atributelor nu sunt
ordonate. Totuşi, prima definiţie a relaţiei (în care tuplul este definit ca o listă ordonată de valori ale atributelor)
simplifică notaţiile şi corespunde reprezentării prin tabel a relaţiei (cu coloanele corespunzătoare atributelor
aşezate într-o anumită ordine) şi de aceea în continuare va fi folosită destul de frecvent.

2.1.3 Catalogul sistemului SGBD


Catalogul sistemului (system catalog) este partea centrală a oricărui SGBD, în care se memorează
descrierea bazelor de date pe care acesta le administrează.
Catalogul sistemului este o mică bază de date, în care sunt memorate date despre datele stocate; astfel
de informaţii sunt denumite meta-date.
În sistemele de gestiune a bazelor de date relaţionale catalogul este compus din relaţii, care pot fi
actualizate sau interogate folosind chiar comenzi ale sistemului SGBD. În catalog sunt descrise relaţiile (numele
relaţiei, numele şi domeniul atributelor, cheile candidate şi străine), vederile, indexurile, precum şi procedurile
stocate, triggerele, funcţiile definite de utilizatori. De asemenea se memorează utilizatorii bazelor de date şi
drepturile acestora.

2.2 Limbajul SQL


Limbajul SQL (Structured Query Language) este limbajul utilizat de majoritatea sistemelor de baze de
date relationale pentru definirea şi manipularea datelor.
Limbajul SQL a fost dezvoltat într-un prototip de sistem de gestiune a bazelor de date relaţionale -
System R - la IBM, la mijlocul anilor 1970. In 1979 Corporatia Oracle a introdus prima implementare a
limbajului SQL în varianta comercială. În anul 1987 Institutul National American de Standarde - ANSI
(American National Standardization Institute) a elaborat standardul limbajului SQL. Ulterior au avut loc mai
multe revizii ale acestui standard. În 1992 Organizaţia International de Standarde - ISO (International
Standardization Office) a adoptat limbajul SQL ca limbaj standard pentru sistemele de gestiune a bazelor de date
relaţionale sub denumirea de SQL-92 (sau, mai simplu, SQL2) [ANSI92]. Un standard ulterior, SQL-99 (numit
şi SQL3) adaugă limbajului trăsături ale modelului obiect-relaţional.
Majoritatea sistemelor SGBD relaţionale actuale suportă standardul SQL2, dar fiecare implementează,
de fapt, un dialect specific al limbajului SQL. În diferitele implementări ale limbajului SQL pot să lipsească
unele comenzi prevăzute în standardul SQL2, dar pot exista extensii specifice, neprevăzute în standard, care
micşorează oarecum gradul de portabilitate a aplicaţiilor. Unele sisteme de gestiune (ca Oracle9i, PostgreSQL-
7.2) suportă şi o mare parte din specificaţiile obiect-relaţionale prevăzute în standardul SQL99 (SQL3).
Deoarece această lucrare este axată pe modelul relaţional se vor descrie în principal instrucţiunile standardului
SQL2.
Limbajul SQL foloseşte termenii de tabel (table), linie (row), coloană (column) pentru a desemna o
relaţie, un tuplu sau un atribut, deci este orientat spre reprezentarea prin tabele a relaţiilor, care este mai simplă şi
mai intuitivă pentru proiectanţi şi pentru programatori.
Limbajul SQL cuprinde atât componenta de descriere a datelor relaţionale (Limbaj de Descriere a
Datelor - LDD) cât şi componenta de manipulare a datelor (Limbaj de Manipulare a Datelor - LMD), ambele
fiind absolut necesare în gestiunea bazelor de date. Pe lângă aceste componente principale, standardul SQL2 mai
prevede şi alte componente ale limbajului:

3
• Controlul tranzacţiilor - conţine comenzi pentru specificarea tranzacţiilor. Unele implementări
adaugă comenzilor prevăzute în standard şi alte comenzi suplimentare de control al concurenţei şi
refacerea datelor.
• Controlul securităţii şi al protecţiei datelor - conţine comenzi de administrare a bazelor de date,
pentru definirea utilizatorilor şi a drepturilor acestora de acces la tabele. Această componentă este
puternic dependentă de sistemul de gestiune al bazei de date, iar pentru sistemele performante,
administrarea bazei de date este un capitol foarte extins, care face obiectul activităţii unei categorii
speciale de utilizatori ai bazei de date (administratori ai bazei de date).
Limbajul SQL este un limbaj neprocedural: o instrucţiune SQL specifică ce informaţii trebuie să fie
setate sau obţinute, nu modul (procedura) în care se operează. Limbajul SQL conţine numai instrucţiuni de
definire şi manipulare a datelor şi nu conţine instrucţiuni de control al fluxului execuţiei (instrucţiuni ca for,
while, if, etc). De aceea, pentru realizarea aplicaţiilor de baze de date, s-a dezvoltat o multitudine de
tehnologii, limbaje, biblioteci şi interfeţe de programare care integrează, printr-o tehnică oarecare, instrucţiunile
SQL de acces la date. Astfel de tehnologii vor fi prezentate în Capitolele 4 şi 7.
În continuare se vor prezenta cele mai importante instrucţiuni de descriere şi manipulare a
datelor definite în standardul SQL2.
Pentru a învăţa şi testa diferite instrucţiuni SQL trebuie să fie instalat un sistem de gestiune a
bazelor de date şi un program utilitar care primeşte instrucţiuni de la consolă şi le transmite sistemului
SGBD pentru a fi executate. Majoritatea sistemelor SGBD oferă astfel de instrumente soft (cu sau fără
interfaţă grafică). De exemplu, pentru sistemul Microsoft SQL Server există utilitarele isql şi osql care
permit executarea instrucţiunilor Transact-SQL (care sunt extensii ale limbajului SQL); pentru
sistemele Oracle există utilitarul SQL*Plus care permite execuţia de la consolă atât a instrucţiunilor
SQL, ca şi a blocurilor PL/SQL; pentru sistemul mySQL există utilitarul mySQL care execută
instrucţiuni SQL. Aceste programe utilitare sunt descrise în primul capitol din îndrumarul de aplicaţii.
Bineînţeles, atunci când se testează o anumită instrucţiune SQL trebuie să fie folosită exact varianta
implementată în sistemul folosit, care poate diferi într-o oarecare măsură de forma generală din
standard.

2.2.1 Structura lexicală a limbajului SQL


O instrucţiune SQL (statement) este o secvenţă de elemente componente (tokens) terminată cu
semnul punct şi virgulă (;). Fiecare instrucţiune SQL conţine o comandă SQL (command), care
specifică ce acţiune se efectuează, urmată de alte elemente componente, care specifică operaţii, clauze,
parametri, etc. De exemplu, instrucţiunea:
SELECT * FROM ANGAJATI;
conţine comanda SQL SELECT, urmată de alte elemente componente ale instrucţiunii.
Un element al unei instrucţiuni SQL poate fi: cuvânt cheie (key word), identificator
(identifier), constantă (literal) sau un caracter special. Elementele componente sunt, în general,
separate printr-unul sau mai multe spaţii albe (whitespaces): caracter spaţiu, caracter linie nouă sau
caracter tab. Separatorii pot lipsi dacă nu există ambiguităţi în secvenţa de elemente ale unei comenzi.
O comandă se poate scrie pe una sau mai multe linii, iar într-o linie se pot introduce una sau mai multe
comenzi.
Cuvinte cheie şi identificatori. Cuvintele cheie sunt elemente componente cu semnificaţie fixă
în limbajul SQL. Acestea pot fi comenzi (SELECT, UPDATE, INSERT, etc.), operatori (AND, OR, NOT,
LIKE), clauze (WHERE, SET, VALUES, etc.). De exemplu, în instrucţiunile următoare, toate cuvintele
scrise îngroşat (bold) sunt cuvinte cheie:
SELECT * FROM SECTII WHERE IdSectie = 1;
INSERT INTO SECTII VALUES (2,‘Productie’,500);
Identificatorii sunt elemente componente care denumesc tabele, coloane sau alte obiecte ale
bazei de date. În exemplul de mai sus, SECTII, IdSectie sunt identificatori.
În SQL cuvintele cheie şi identificatorii trebuie să înceapă cu o literă sau cu caracterul
subliniere ( _ ), iar caracterele următoare pot fi litere, cifre sau caracterul subliniere. Cuvintele cheie şi
identificatorii au aceeaşi structură lexicală şi nu pot fi diferenţiaţi fără a cunoaşte limbajul. În
cuvintele cheie şi identificatori nu se diferenţiază caracterele mici de cele mari (sunt case-insensitive),

4
deci comenzile INSERT, insert, sau Insert, etc. sunt identice. Totuşi, pentru evidenţierea
comenzilor SQL, în continuare majoritatea cuvintelor cheie vor fi scrise cu majuscule. La fel vor fi
scrise şi numele relaţiilor.
Pe lângă acest tip de identificatori simpli (formaţi dintr-o secvenţă de litere, cifre şi caracterul
subliniere), mai există şi un alt tip de identificatori, identificatorii delimitaţi (quoted identifiers),
constând dintr-o secvenţă de caractere încadrată (la început şi la sfârşit) de caracterul ghilimele ( “ ).
Un identificator delimitat este întotdeauna identificator (niciodată cuvânt cheie), iar literele mari sunt
diferite de literele mici (este case-sensitive). De exemplu, elementul “SELECT” este un identificator
delimitat şi poate denumi o tabelă sau o coloană (atribut), în timp ce elementul SELECT este un cuvânt
cheie şi utilizarea lui ca nume de tabel sau de atribut va produce o eroare de interpretare a comenzii.
Un identificator delimitat poate conţine orice caracter cu excepţia caracterulul ghilimele,
permiţând crearea unor nume (de tabele, coloane, etc.) mai complexe, care să conţină spaţii sau
caractere speciale ( &, %, etc.), ceea ce cu identificatori simpli (ne-delimitaţi) nu este posibil.
Constante. Constantele (literale) pot fi şiruri de caractere, numere întregi, numere reale sau
constanta NULL. Ele se reprezintă aproximativ la fel ca în alte limbaje de programare (de exemplu
C/C++).
Caractere speciale. Unele caractere care nu sunt litere sau cifre pot avea rol de operatori SQL
sau pot avea o semnificaţie specială în cadrul comenzilor SQL. De exemplu, caracterul punct şi
virgulă (;) este folosit pentru terminarea comenzilor; caracterul punct (.) este folosit ca punct zecimal
sau pentru calificarea numelor; caracterul asterisc (*) este folosit ca operator de înmulţire sau în
comanda SELECT, etc.

2.2.2 Expresii, operatori şi funcţii SQL2


O expresie SQL constă dintr-unul sau mai mulţi operanzi, operatori şi paranteze. Un operand
poate fi numele unei coloane (a unui tabel), o constantă (literal), sau valoarea returnată de o functie;
parantezele sunt folosite pentru a preciza o anumită ordine a operaţiilor, dacă aceasta este diferită de
cea implicită, dată de precedenţa operatorilor.
Un operator SQL este compus dintr-unul sau mai mai multe caractere speciale (care nu sunt
litere sau cifre), ca de exemplu: +,-,*,/,%,<,>,=,~,!,@ #,&,|,^,?,‘,$, sau este un cuvânt
cheie, ca de exemplu: AND, OR, NOT, LIKE, etc. Din punct de vedere al numărului de operanzi,
operatorii SQL sunt de două categorii, binari şi unari, cei unari putând fi operatori prefix sau operatori
postfix. Din punct de vedere al tipului de operaţie, operatorii SQL (din orice categorie) pot fi
aritmetici, logici, de comparaţie SQL, sau relationali.
Operatorii aritmetici ai limbajului SQL2 sunt compuşi din unul sau mai multe caractere
speciale şi au notaţie şi semnificaţie asemănătoare cu a celor definiţi în diferite limbaje de programare,
cu mici diferenţe care se pot remarca din lista de mai jos. Operatorii aritmetici binari sunt: +
(adunarea), - (scaderea), * (înmulţirea), / (împărţirea), % (modulo), ^ (ridicarea la putere), & (AND
orientat pe biti), | (OR orientat pe biti), # (XOR orientat pe biti) , << (deplasarea la stânga), >>
(deplasarea la dreapta). Tot operatori aritmetici sunt şi operatorii binari de comparatie: < (mai mic), >
(mai mare), <= (mai mic sau egal), >= (mai mare sau egal), = (egal), <> (sau !=) (diferit). Operatorii
aritmetici unari sunt: @ (valoarea absoluta), ! (factorial), !! (factorial, operator postfix), ~ (NOT
orientat pe biti).
Operatorii de comparaţie SQL sunt descrişi în tabelul următor.
Operator Operaţia efectuată
A BETWEEN min AND max compară A cu două valori, min şi max
A IN (v1,v2, ...vn) compară A cu o lista de valori (v1, v2,…vn )
A IS NULL compară A cu NULL
A IS NOT NULL compară A cu NOT NULL
A LIKE model_sir compară A cu un model de şir de caractere

Pentru operatorii de comparaţie, valoarea de comparat (A) este, de regulă, valoarea unui atribut
al unei relaţii, dat prin numele coloanei corespunzătoare a tabelului care reprezintă relaţia respectivă, o

5
constantă sau valoarea unei expresii. Operatorii de comparaţie (atât cei aritmetici, cât şi operatorii de
comparaţie specifici SQL), returnează (evaluează) valoarea logică TRUE (1), dacă condiţia de
comparaţie este îndeplinită şi FALSE (0) dacă condiţia de comparaţie nu este îndeplinită. Valoarea
NULL este returnată dacă ambii operanzi comparaţi au valoarea NULL.
Operatorii logici ai limbajului SQL sunt notaţi prin cuvinte cheie: AND, OR, NOT. Toţi aceşti
operatori se aplică unor variabile logice cu 3 valori (trivalente): TRUE (1), FALSE (0) şi NULL;
valoarea NULL semnifică lipsa de informaţie. Operatorii logici SQL returnează o valoare logică
trivalentă (TRUE, FALSE sau NULL), aşa cum se poate vedea din tabelele de adevăr de mai jos.
A B A AND B A OR B
TRUE TRUE TRUE TRUE
TRUE FALSE FALSE TRUE
TRUE NULL NULL TRUE
FALSE FALSE FALSE FALSE
FALSE NULL FALSE NULL
NULL NULL NULL NULL

A NOT A
TRUE FALSE
FALSE TRUE
NULL NULL
Operatorii relaţionali sunt notaţi prin cuvinte cheie: UNION (reuniune), INTERSECT (intersecţie),
MINUS (diferenţa). Ei vor fi studiaţi mai pe larg în Capitolul 3.
Funcţiile definite în SQL sunt de două categorii: funcţii agregat şi funcţii scalare.
Funcţiile agregat calculează un rezultat din mai multe linii ale unui tabel. Aceste funcţii vor fi detaliate
într-o secţiune următoare, la descrierea instrucţiunii SELECT.
Funcţiile scalare primesc unul sau mai multe argumente şi returnează valoarea calculată sau NULL în
caz de eroare. Argumentele funcţiilor pot fi constante (literale) sau valori ale atributelor specificate prin numele
coloanelor corespunzatoare. Există mai multe tipuri de funcţii scalare SQL:
• Funcţii numerice: Majoritatea versiunilor de SQL furnizează funcţii de calcul trigonometric (sin,
cos, etc.), funcţii de calcul al logaritmului (ln, log), al puterii (pow), funcţii de rotunjire
(floor, ceil), etc.
• Funcţii pentru manipularea şirurilor de caractere.
• Funcţii pentru data calendaristică şi timp.
• Funcţii de conversie.
Funcţiile scalare se folosesc în expresii, care pot să apară în diferite clauze ale instrucţiunilor SQL.

2.2.3 Tipuri de date şi domenii SQL2


În limbajul SQL (standardul SQL2) sunt definite mai multe tipuri de date: numeric, şir de caractere, şir
de biţi, data (calendaristică), timp. Denumirile tipurilor de date, ca şi limitele acestora (valoare minimă, valoare
maximă) prezintă diferite variaţii în funcţie de implementare (versiunea de SGBD), dar în general sunt destul de
asemănătoare.
Tipul numeric include numere întregi de diferite dimensiuni (integer sau int reprezentat pe 4
octeţi, smallint reprezentat pe 2 octeţi), numere reale reprezentate în virgulă flotantă, cu diferite precizii
(float, reprezentat pe 4 octeţi, real şi double [precision], reprezentate pe 8 octeţi) şi numere zecimale
reprezentate cu precizia dorită (tipul numeric sau decimal).
Formatul de reprezentare a numerelor zecimale cu precizia dorită este: numeric[(p,s)] (sau
decimal [(p,s)]), unde p (precizia) este numărul total de cifre afişate, iar s (scara) este numărul de cifre
după punctul zecimal. În reprezentarea acestui tip de date poate să lipsească parametrul s şi atunci se consideră
s = 0 (nici o cifră după punctul zecimal), sau pot lipsi ambii parametri şi, în acest caz, se pot reprezenta
numere cu orice precizie şi scară (până la limita admisă de implementarea sistemului). Pentru a păstra precizia
dorită, numerele de tip DECIMAL sau NUMERIC sunt memorate ca şir de caractere, fiecare caracter reprezentând
o cifră zecimală, punctul zecimal sau semnul.
Tipul şir de caractere permite definirea şirurilor de caractere de lungime fixă (character(n) sau,
prescurtat, char(n)), precum şi a şirurilor de caractere de lungime variabilă (character varying,
prescurtat varchar(n)). Ambele tipuri pot reprezenta şiruri de maximum n caractere, cu diferenţa că, pentru

6
şiruri de lungime mai mică decât n, la tipul char(n) se completează şirul cu spaţii albe până la n caractere, în
timp ce la tipul varchar(n) se memorează numai atâtea caractere câte are şirul dat.
Tipul şiruri de biţi defineşte secvenţe de cifre binare (care pot lua valoarea 0 sau 1) de lungime fixă n
(bit(n)) sau de lungime variabilă, cu limita maximă n (bit varying(n)).
Tipurile pentru data calendaristică şi timp sunt: date, time, timestamp, interval.
Tipul date permite memorarea datelor calendaristice prin utilizarea a trei câmpuri (year, month,
day), în formatul yyyy-mm-dd. Sunt admise numai date valide (valori pozitive diferite de zero şi mai mici
decât 12 pentru lună, etc.).
Tipul time este utilizat pentru memorarea timpului, folosind trei câmpuri (hour, minute, second)
în formatul HH:MM:SS şi, de asemenea, se admit numai valori valide. Un parametru opţional p (time(P))
permite stabilirea numărului de zecimale cu care se reprezintă câmpul second. Valoarea implicită a lui p este 0.
Tipul timestamp(p) este folosit pentru memorarea combinată a datei calendaristice şi a timpului, cu
precizia p pentru câmpul second. Valoarea implicită a lui p este 6.
Tipul interval este utilizat pentru memorarea intervalelor de timp.
În denumirile tipurilor de date nu se diferenţiază caracterele mici de cele mari (sunt case-insensitive),
deci se poate scrie: INT, VARCHAR, NUMERIC, etc.
Pe lângă aceste tipuri definite în standardul SQL2, implementările limbajului SQL în diferite sisteme
SGBD prezintă variante de tipuri de date specifice implementării respective. De exemplu, SQL implementat de
SQL Server adaugă tipul tinyint, ca număr întreg reprezentat pe 1 octet; în SGBD Oracle, tipul şir de
caractere de lungime variabilă este numit varchar2, etc.
Standardul SQL2 nu suportă tipuri de date şi operaţii definite de utilizator, dar aceste trăsături au fost
introduse în standardul SQL3 care defineşte, de fapt, modelul obiect-relaţional de date. În momentul de faţă se
poate observa că majoritatea producătorilor de sisteme de baze de date relaţionale introduc treptat, de la o
versiune la alta, diferite caracteristici ale modelului obiect-relaţional cuprinse în standardul SQL3.
Domeniile atributelor în SQL2 se specifică pe baza tipurilor de date predefinite ale limbajului SQL,
deci sunt destul de depărtate de noţiunea de domeniu relaţional, aşa cum a fost descrisă în secţiunea precedentă,
dat fiind că nu se face nici o precizare a semnificaţiei domeniului.
Standardul SQL2 prevede comanda CREATE DOMAIN, care atribuie un nume de domeniu şi unele
constrângeri unui tip predefinit SQL2, dar această comandă nu prea mai este implementată în sistemele de
gestiune actuale, care preferă alte soluţii de definire a domeniilor.
De exemplu, în SQL Server, se pot crea aşa-numitele “tipuri definite de utilizator” (user-defined types),
care sunt, de fapt, echivalente cu domeniile create cu comanda SQL CREATE DOMAIN. Pentru aceasta se
foloseşte o procedură stocată, scrisă în limbajul Transact-SQL care este specific sistemului SQL Server şi care
extinde limbajul SQL. În sistemele de baze de date Oracle 8i şi Oracle 9i, se pot crea cu adevărat tipuri de date
noi, folosind comanda CREATE TYPE, care permite gruparea sub un anumit nume a mai multor atribute, de
diferite tipuri (predefinite sau definite de utilizator), într-un mod asemănător cu definirea claselor din limbajele
obiect-orientate. Aceste tipuri definite de utilizator sunt folosite ca domenii ale atributelor (coloanelor) tabelelor.
Pentru fiecare tip de date nou definit, se pot prevedea metode, scrise într-unul din limbajele C, Java sau PL/SQL.
Aceste caracteristici reprezintă trăsăturile obiect-relaţionale ale sistemelor de baze de date Oracle 8i şi Oracle 9i.
În sistemul PostgreSQL se pot crea, de asemenea, tipuri de date noi, folosind comanda CREATE TYPE, iar
tipurile nou create pot fi folosite ca domenii ale atributelor.
Convenţiile sintactice care se vor folosi în acest capitol şi în următoarele pentru prezentarea limbajului
SQL şi a limbajelor de programare dezvoltate pe baza acestuia sunt prezentate în tabelul 2.3.
Convenţie sintactică Utilizare
Litere mari Cuvinte cheie ale limbajului sau denumiri de tabele.
[ ] (paranteze drepte) Element opţional al instrucţiunii.
{ } (acolade) Element obligatoriu al instrucţiunii.
| (bară verticală) Separă elementele din parantezele drepte sau acolade. Numai unul din elementele separate
cu bară verticală se pot introduce în instrucţiunea respectivă.
[,...n] Indică faptul că elementul precedent poate fi repetat de n ori. Elementele repetate sunt
separate prin virgulă.
element1, Listă de n elemente de acelaşi tip.
........ Elementele repetate sunt separate prin virgulă.
elementn
lista_elemente Listă de elemente de acelaşi tip (separate prin virgulă)

7
Caracterele folosite pentru a specifica o anumită convenţie sintactică (paranteze, bara verticală, virgula,
etc.) nu apar în instrucţiunile propriu-zise. Listele de elemente (compuse din mai multe elemente separate prin
virgulă) vor fi exprimate fie folosind una cele trei din construcţiile de mai sus, care se potriveşte cel mai bine
instrucţiunii respective. Pe lângă aceste convenţii, pentru o prezentare cât mai concisă şi mai uşor de înţeles a
instrucţiunilor, s-au mai adoptat şi alte notaţii: denumiri sugestive ale elementelor sintactice, renunţarea la detalii
minore pentru a se putea urmări caracteristicile de ansamblu, etc.

2.2.4 Instrucţiuni SQL de definire a datelor


Componenta de definire a datelor a limbajului SQL (numită Limbajul de Definire a Datelor - LDD)
permite crearea (CREATE), modificarea (ALTER) şi distrugerea (DROP) obiectelor bazei de date: tabele de bază
(TABLE), tabele vedere (VIEW), indexuri (INDEX), proceduri (PROCEDURE), etc. Majoritatea dialectelor
limbajului SQL conţin comenzile:
CREATE TABLE, CREATE VIEW, CREATE INDEX, CREATE USER
CREATE FUNCTION, CREATE PROCEDURE, CREATE TRIGGER
ALTER TABLE, ALTER VIEW, ALTER FUNCTION, ALTER PROCEDURE
DROP TABLE, DROP VIEW, DROP INDEX, DROP USER
DROP FUNCTION, DROP PROCEDURE, DROP TRIGGER
În continuare vor fi descrise cele mai importante instrucţiuni SQL de definire a datelor.

2.2.4.1 CREAREA TABELELOR ŞI A VEDERILOR


În limbajul SQL2 un tabel se creează folosind instrucţiunea CREATE TABLE, care are următoarea
sintaxă:
CREATE TABLE nume_tabel (
col1 dom1 [constrangeri_coloana],
col2 dom2 [constrangeri_coloana],
..........................................
coln domn [constrangeri_coloana],
[constrangeri_tabel] );
Constrângerile impuse fiecărei coloane (atribut), ca şi constrângerile de tabel, sunt opţionale şi vor fi
discutate în capitolul următor.
De exemplu, tabelul ANGAJATI corespunzător relaţiei ANGAJATI (descrisă în secţiunea precedentă)
se poate defini astfel:
CREATE TABLE ANGAJATI (
Nume varchar(20),
Prenume varchar(20),
DataNasterii date,
Adresa varchar(50),
Functie varchar(20),
Salariu numeric);
Instrucţiunea CREATE TABLE defineşte atât un tip de relaţie (cu atributele specificate) cât şi o
variabilă relaţie care iniţial este vidă (nu conţine nici un tuplu).
Tabelele create cu instrucţiunea CREATE TABLE sunt numite şi tabele de bază (base tables); ele sunt
memorate în fişierele bazei de date şi pot fi accesate pentru introducerea, modificarea şi regăsirea (interogarea)
datelor.

Vederi (views). Un tabel vedere este un tabel virtual, care nu este memorat fizic în fişiere, ci reprezintă
o selecţie (după un anumit criteriu) a datelor memorate în unul sau mai multe tabele de bază.
Datele (valorile atributelor) sunt memorate o singură dată, în tabelele de bază, dar pot fi accesate atât
prin tabelele de bază cât şi prin tabelele vederi. Instrucţiunea SQL de creare a unui tabel vedere este:
CREATE VIEW nume_vedere AS (SELECT...);
Formatul comenzii SELECT va fi descris în capitolul următor.
Un tabel vedere este întotdeauna actualizat ("la zi"), adică orice modificare efectuată în tabelele de bază
se regăseşte imediat în orice tabel vedere creat pe baza acestora.

8
2.2.4.2 MODIFICAREA ŞI ŞTERGEREA TABELELOR
Comanda de modificare a tabelelor (ALTER TABLE) permite adăugarea sau ştergerea unor atribute,
modificarea domeniilor unor atribute, precum şi adăugarea, modificarea sau ştergerea unor constrângeri ale
tabelului.
De exemplu, instrucţiunea de adăugare a atributului DataAngajarii în tabelul ANGAJATI se scrie
în felul următor:
ALTER TABLE ANGAJATI ADD DataAngajarii Date;
Pentru ştergerea unei coloane dintr-un tabel se foloseşte cuvântul cheie DROP în comanda ALTER
TABLE. De exemplu, pentru ştergerea coloanei DataAngajarii din tabelul ANGAJATI se introduce
instrucţiunea:
ALTER TABLE ANGAJATI DROP DataAngajarii;
Instrucţiunile de ştergere a tabelelor de bază şi a vederilor sunt:
DROP TABLE nume_tabel;
DROP VIEW nume_vedere;

2.2.5 INSTRUCŢIUNI SQL de manipulare a datelor


Instrucţiunile SQL de manipulare a datelor conţin una din comenzile: SELECT, INSERT, UPDATE sau
DELETE şi vor fi studiate în continuare.

2.2.5.1 INSTRUCŢIUNEA SELECT


Instrucţiunea SELECT este instrucţiunea de interogare în limbajul SQL, prin care se regăsesc
informaţiile dorite din unul sau mai multe tabele ale bazei de date. Instrucţiunea SELECT este foarte puternică şi
are următoarea sintaxă generală:
SELECT [DISTINCT] lista_coloane
[FROM lista_tabele]
[WHERE conditie]
[clauze_secundare];

Ca rezultat al instrucţiunii SELECT se obţine un tabel care conţine atributele (coloanele) din
lista_coloane ale acelor linii (tupluri) ale produsului cartezian al tabelelor din lista_tabele pentru
care expresia logică conditie este adevărată (are valoarea TRUE).
Se remarcă trei secţiuni (clauze) importante ale instrucţiunii de interogare: SELECT, FROM şi
WHERE. Clauza SELECT defineşte coloanele tabelului rezultat. Clauza FROM indică unul sau mai multe
tabele (o listă de tabele) din care se selectează liniile tabelului rezultat. Clauza WHERE defineşte
condiţia pe care trebuie să o îndeplinească fiecare linie a tabelului rezultat. În afara acestor clauze,
comanda SELECT mai poate conţine şi clauze secundare (ORDER BY, GROUP BY, HAVING), care
permit ordonări sau grupări ale tuplurilor (liniilor) rezultate, etc. Singura clauză obligatorie este
SELECT; restul clauzelor sunt opţionale.
Clauza SELECT introduce lista coloanelor unor tabele sau expresii care vor fi selectate şi
afişate. Coloanele din listă trebuie să aparţină unuia din tabelele specificate în clauza FROM. De
exemplu, comanda următoare va selecta numele şi prenumele tuturor angajaţilor din tabelul
ANGAJATI:
SELECT Nume,Prenume FROM ANGAJATI;
Ca rezultat al instrucţiunii de mai sus se pot obtine două sau mai multe linii identice, dacă exista
angajaţi cu acelaşi nume şi prenume, deci rezultatul operaţiei nu este o “relaţie” în sensul definiţiei din modelul
relaţional. Pentru eliminarea liniilor duplicat se introduce parametrul DISTINCT şi atunci se elimină liniile
duplicat iar rezultatul este o relaţie în sensul definiţiei din modelul relaţional. Deci instrucţiunea de mai sus se
poate scrie:
SELECT DISTINCT Nume,Prenume FROM ANGAJATI;

Dacă lista de atribute este un asterisc (*), atunci se selectează toate atributele produsului
cartezian al tabelelor indicate prin clauza FROM, care îndeplinesc condiţia din clauza WHERE. De
exemplu, instrucţiunea:

9
SELECT * FROM ANGAJATI;

permite selectarea tuturor coloanele şi a liniilor din tabelul ANGAJATI.


În clauza SELECT se pot introduce şi funcţii de totalizare (funcţii agregat). Funcţiile agregat definite în
limbajul SQL2 sunt date în tabelul următor.
Funcţia Valoarea returnata
COUNT numărul de linii ale rezultatului;
SUM suma tuturor valorilor dintr-o coloană
MAX valoarea cea mai mare dintr-o coloană
MIN valoarea cea mai mică dintr-o coloană
AVG media valorilor dintr-o coloană

De exemplu, comenzile următoare vor afişa numărul de linii ale tabelului ANGAJATI şi salariul
maxim, minim şi mediu al angajaţilor:
SELECT COUNT(*) FROM ANGAJATI;
SELECT MAX(Salariu) FROM ANGAJATI;
SELECT MIN(Salariu) FROM ANGAJATI;
SELECT AVG(Salariu) FROM ANGAJATI;

Instrucţiunea SELECT poate să conţină chiar şi numai clauza SELECT, deci fără să se refere la un tabel
(printr-o clauză FROM). În acest caz, comanda SELECT conţine o listă de expresii pe care le evaluează şi
rezultatele calculate sunt returnate ca o linie a unui tabel ale cărui coloane sunt chiar expresiile date.
În clauza SELECT se pot redenumi atributele (coloane ale tabelelor) sau se pot specifica nume pentru
expresii, folosind următoarea sintaxă:
SELECT nume1 [AS] noul_nume1 [,...n] FROM lista_tabele [alte_clauze];
Se observă că noul nume atribuit unei coloane (sau expresii) urmează vechiului nume (sau expresiei),
precedat (optional, depinzând de implementare) de cuvântul-cheie AS. De exemplu, comanda următoare va afisa
numele angajatului denumit NumeAngajat şi 80% din salariul acestuia, denumit SalariuNet:
SELECT Nume NumeAngajat,Salariu*0.8 SalariuNet FROM ANGAJATI;
Clauza FROM este obligatorie dacă într-una din clauzele SELECT, WHERE sau HAVING apar nume de
coloane ale unor tabele. În acest caz, lista de tabele care însoţeşte clauza FROM trebuie să conţină numele tuturor
tabelelor (separate prin virgulă) ale căror coloane se folosesc. Dacă lista conţine mai mult de un tabel, atunci
numele coloanelor din clauza SELECT trebuie să fie diferite şi, dacă nu sunt diferite, se califică cu numele
tabelului caruia îi aparţine, precedând numele atributului cu numele tabelului urmat de operatorul “punct” (.).
De exemplu:
SELECT ANGAJATI.Nume,SECTII.Nume FROM ANGAJATI,SECTII;
De retinut că, deşi limbajul SQL este case-insensitive, totuşi este necesar ca numele tabelului cu care se
califică numele unui atribut să fie identic (inclusiv tipul de caracter, majusculă sau nu) cu cel declarat în clauza
FROM.

Clauza WHERE restrictionează tuplurile returnate ca rezultat la acele tupluri care îndeplinesc condiţia
introdusă de această clauză sub forma unei expresii logice.
O expresie logică se construieste din valori logice, operatori logici (AND, OR, NOT) şi paranteze. O
valoare logică se obtine, în mod obişnuit, ca rezultat al comparaţiei între doi operanzi folosind un operator de
comparaţie. Un operand poate fi un atribut (nume de coloană dintr-unul din tabelele introduse prin clauza
FROM), o constantă, valoarea unei expresii aritmetice sau o valoare returnată de o funcţie. Operatorii de
comparaţie utilizaţi în clauza WHERE pot fi atât operatori aritmetici de comparaţie cât şi operatori SQL de
comparaţie, aşa cum se poate vedea în instrucţiunile următoare:
SELECT Nume,Prenume FROM ANGAJATI WHERE DataNasterii > 1968-01-01;
SELECT Nume,Prenume FROM ANGAJATI
WHERE Salariu BETWEEN 3000 AND 4000 AND Functie = ‘Inginer’;
Prima instrucţiune va afişa toţi angajaţii care s-au născut după data de 1 Ianuarie 1968; cea de-a doua
comandă va afişa toţi angajaţii cu funcţia Inginer şi salariul cuprins între 3000 şi 4000.

10
Clauza ORDER BY introduce numele atributului după care se face ordonarea liniilor tabelului rezultat.
Ordonarea se face în ordine crescătoare în mod implicit sau dacă numele atributului este urmat de cuvântul
cheie ASC; dacă numele atributului este urmat de cuvântul DESC, ordonarea liniilor se face în ordine
descrescătoare a valorilor acelui atribut. Ordonarea liniilor astfel obţinută este ordonare logică, foarte utilă în
prezentarea (afişarea) rezultatului şi nu înseamnă ordonarea înregistrărilor în fişierele relaţiilor. De exemplu,
pentru afişarea listei angajatilor ordonată după numele acestora, se introduce comanda:
SELECT * FROM ANGAJATI ORDER BY Nume;

Clauza GROUP BY se foloseşte pentru gruparea rezultatelor funcţiilor agregat (totalizatoare) în funcţie
de valoarea uneia sau mai multor coloane.
Pentru aceasta, în instrucţiunea SELECT se introduce clauza GROUP BY, urmată de numele coloanei
(sau al coloanelor) după valoarea cărora se doreşte gruparea rezultatelor funcţiei agregat. În acest caz, funcţia
agregat se aplică separat acelor linii care au aceeaşi valoare a atributelor specificate de clauza GROUP BY.
De exemplu, salariul mediu calculat separat pe grupuri de angajati, fiecare grup fiind compus din liniile
care au aceeaşi valoare a atributului Functie, se obţine cu următoarea instrucţiune SQL:
SELECT AVG (Salariu) FROM ANGAJATI GROUP BY Functie;
Clauza HAVING. Funcţiile agregat (totalizatoare) nu pot fi utilizate în clauza WHERE; de exemplu
instrucţiunea următoare (prin care se cere lista angajaţilor cu salariu mai mare decât salariul mediu) este eronată:
SELECT Nume,Prenume FROM ANGAJATI WHERE Salariu >= AVG(Salariu);

Pentru folosirea unei funcţii agregat într-o condiţie de selecţie se foloseşte clauza HAVING. Clauza
HAVING este asemănătoare clauzei WHERE, adică introduce o condiţie pe care trebuie să o îndeplineasca
tuplurile rezultat, şi, în plus, permite utilizarea funcţiilor agregat în expresia conditională. Exemplul de mai sus
se scrie corect astfel:
SELECT Nume,Prenume
FROM ANGAJATI HAVING Salariu >= AVG (Salariu);
Instrucţiuni SELECT imbricate. Subinterogări. Instrucţiunile SELECT se pot imbrica pe mai multe
niveluri, o instrucţiune având ca argument rezultatul unei altei instrucţiuni, numită subinterogare. Există mai
multe moduri de construire a subinterogărilor, una din formele cele mai frecvent folosite fiind următoarea:
SELECT lista_atribute FROM tabel1
WHERE colx IN (SELECT colx FROM tabel2 WHERE conditie);
Într-o astfel de construcţie valoarea de comparaţie (pentru operatorul de comparaţie IN) din clauza
WHERE a primei instrucţiuni SELECT se defineşte printr-o subinterogare care constă dintr-o altă instrucţiune
SELECT. Alte forme de construire a subinterogărilor vor fi prezentate în capitolele următoare.

2.2.5.2 INSTRUCŢIUNEA INSERT


Instrucţiunea INSERT se foloseşte pentru introducerea datelor în tabele şi are următoarea sintaxă:
INSERT INTO nume_tabel(col1,col2,...coln)VALUES(val1,val2,...valn);
Între valori şi numele de coloane trebuie să existe o corespondenţă unu la unu. Valorile din listă pot fi
constante (literale) sau expresii. De exemplu, introducerea unei linii în tabelul SECTII se poate face cu
instrucţiunea:
INSERT INTO SECTII (IdSectie,Nume,Buget)
VALUES (1,‘Productie’,4000000);
Lista de coloane poate să lipsească dacă se introduc valori în toate coloanele tabelului, dar în această
situatie ordinea valorilor introduse trebuie să respecte ordinea atributelor. Aceasta ordine provine din ordinea de
definire a atributelor prin instrucţiunea CREATE TABLE, precum şi din operaţiile ulterioare de alterare a
tabelului respectiv, şi se poate afla printr-o instrucţiune DESCRIBE nume_tabel. De exemplu, introducerea
unei linii în tabelul ANGAJATI(IdAngajat,Nume,Prenume,DataNasterii,Adresa,Salariu), se poate
face cu instrucţiunea:
INSERT INTO ANGAJATI VALUES(100,‘Mihailescu’,
‘Mihai’,‘1950-04-05’,‘Craiova’,3000);

11
2.2.5.3 INSTRUCŢIUNEA UPDATE
Instrucţiunea UPDATE permite actualizarea valorilor coloanelor (atributelor) din una sau mai multe linii
ale unui tabel. Aceasta are sintaxa:
UPDATE nume_tabel SET col1 = expr1 [,...n] [WHERE conditie];
Clauza WHERE impune ca actualizarea valorilor coloanelor să se efectueze numai asupra acelor linii
care îndeplinesc condiţia dată. Dacă este omisă clauza WHERE, vor fi modificate valorile coloanelor din toate
liniile tabelului.De exemplu, pentru a modifica linia introdusă mai sus în tabelul ANGAJATI, se poate introduce
instrucţiunea:
UPDATE ANGAJATI
SET Adresa = ‘Bucuresti,Str. Victoriei WHERE IdAngajat = 100;

2.2.5.4 INSTRUCŢIUNEA DELETE


Instrucţiunea DELETE permite ştergerea uneia sau mai multor linii dintr-un tabel şi are forma:
DELETE FROM nume_tabel [WHERE conditie];
Din tabel se şterg acele linii care îndeplinesc condiţia dată în clauza WHERE. Dacă este omisă clauza
WHERE, vor fi sterse toate liniile din tabel.
De exemplu, pentru a sterge din tabelul ANGAJATI toţi angajatii care au numele Ionescu, se
introduce instrucţiunea:
DELETE FROM ANGAJATI WHERE Nume =‘Ionescu’;
În această secţiune au fost prezentate consideraţii generale asupra comenzilor de definire şi manipulare
a datelor prevăzute în standardul SQL2. Alte comenzi SQL vor fi prezentate pe parcursul secţiunilor şi a
capitolelor următoare, o dată cu introducerea noţiunilor la care se referă.
In plus, există numeroase extensii, opţiuni şi detalii ale comenzilor, unele depinzând de implementarea
limbajului în fiecare tip şi versiune de SGBD. Pentru aceste aspecte este necesar să fie consultată documentaţia
sistemului respectiv.

2.3 CONSTRÂNGERI DE INTEGRITATE


Constrângerile de integritate (integrity constraints) sunt reguli care se definesc la proiectarea unei
bazei de date şi care trebuie să fie respectate de orice stare a acesteia.
Relaţiile unei baze de date reflectă realitatea modelată şi de aceea valorile pe care le conţin trebuie să
respecte anumite reguli, care să corespundă celor din realitate.
Constrângerile se pot clasifica după locul unde sunt definite şi după modul în care sunt definite. Din
punct de vedere al locului unde sunt definite, constrângerile pot fi constrângeri intra-relaţie şi constrângeri inter-
relaţii. Constrângerile intra-relaţie sunt reguli care se impun în cadrul unei singure relaţii şi asigură integritatea
datelor acesteia. Ele sunt, la rândul lor, de trei categorii:
• Constrângeri de domeniu, care sunt condiţii ce se impun valorilor atributelor şi asigură integritatea
domeniilor atributelor.
• Constrângeri de tuplu, care sunt condiţii ce se impun tuplurilor unei relaţii şi asigură identificarea
corectă a tuplurilor prin intermediul cheilor (primare sau candidate).
• Constrângeri impuse prin dependenţe de date (dependenţe funcţionale, multivalorice sau de
joncţiune); acestea sunt constrângeri prin care valorile unor atribute ale unei relaţii determină
valorile altor atribute ale aceleiaşi relaţii.
Constrângerile inter-relaţii sunt reguli care se impun între două sau mai multe relaţii. Cele mai
importante constrângeri inter-relaţii sunt constrângerile de integritarea referenţială, care se realizează prin
intermediul cheilor străine şi asigură asocierea corectă a relaţiilor.
Din punct de vedere al modului de definire, constrângerile unei baze date se pot clasifica în constrângeri
inerente, implicite şi explicite.
Constrângerile inerente sunt cele ale modelului de date însuşi, care nu trebuie să fie specificate la
definirea relaţiilor, dar sunt respectate prin modul în care se construiesc relaţiile. De exemplu, în modelul
relaţional constrângerea ca valoarea fiecărui atribut să fie atomică (indivizibilă) este o constrângere inerentă.
Constrângerile implicite sunt reguli care se definesc odată cu definirea schemelor relaţiilor (prin
intermediul instrucţiunile de definire a datelor), sunt memorate în baza de date şi sistemul de gestiune verifică şi
impune automat respectarea acestora. Connstrângerile de domeniu, constrângerile de tuplu şi constrângerile de
integritate referenţială sunt exemple de constrângeri implicite.

12
Constrângerile explicite sunt constrângeri suplimentare pe care trebuie să le respecte relaţiile unei baze
de date şi care nu sunt impuse automat de sistemul SGBD, ci necesită proceduri speciale de verificare şi
impunere a respectării lor. Ca exemple de constrângeri explicite sunt unele dependenţe de date, şi anume cele
care nu sunt determinate de cheile relaţiilor. Dependenţele de date sunt prezentate în Capitolul 5.

2.3.1 Constrângeri de domeniu


Constrângerile de domeniu sunt condiţii impuse valorilor atributelor, astfel încât acestea să corespundă
semnificaţiei pe care o au în realitatea modelată. Dat fiind că, în reprezentarea unei relaţiei printr-un tabel,
valorile atributelor sunt reprezentate pe coloane, constrângerile de domeniu se mai numesc şi constrângeri de
coloană. Dintre constrângerile de domeniu, constrângerea NOT NULL şi constrângerea de valoare implicită
(DEFAULT) sunt constrângeri cu caracter mai general, care se pot aplica oricărui atribut; constrângerea de
verificare (CHECK) se poate aplica unor anumite atribute, în funcţie de semnificaţia acestora.
Constrângerea NOT NULL. O valoare particulară pe care o poate lua un atribut este valoarea NULL,
care nu înseamnă zero, ci lipsă de informaţie. Valoarea NULL a unui atribut într-un tuplu semnifică faptul că
valoarea acelui atribut nu este cunoscută pentru acel tuplu sau că acel atribut nu este aplicabil acelui tuplu.
Lipsa de informaţie (deci valoarea NULL a unui atribut) poate să apară în diferite situaţii. De exemplu,
într-o relaţie de înregistrare a datelor despre personalităţi istorice, valoarea atributului DataNasterii poate să
nu fie cunoscută pentru unele personalităţi şi în aceste tupluri se trece valoarea NULL. Faptul că nu se cunoaşte
data de naştere a unor personalităţi nu trebuie să împiedice definirea acestui atribut care, pentru majoritatea
înregistrărilor, va avea valori cunoscute şi important să fie memorate.
O altă situaţie în care poate să fie folosită valoarea NULL a unui atribut este aceea în care la
înregistrarea unui tuplu nu se cunoaşte valoarea atributului, aceasta urmând să fie completată ulterior.
Nu orice atribut poate lua valori de NULL. De exemplu, în relaţia ANGAJATI(Nume,Prenume,
DataNasterii,Adresa,Functie,Salariu) nu are sens să se înregistreze un angajat al cărui nume nu se
cunoaşte, deci nu se vor admite valori de NULL pentru atributul Nume. În astfel de situaţii la definirea relaţiilor
se impune atributului constrângerea NOT NULL, însemnând că atributul respectiv nu poate lua valoare NULL în
nici un tuplu al relaţiei.
Constrângerea NOT NULL pentru un atribut se introduce la crearea unei relaţii (tabel) prin comanda
SQL CREATE TABLE. Opţiunea implicită (dacă nu se specifică nimic) este admiterea valorilor de NULL. De
exemplu, constrângerea NOT NULL pentru atributele Nume şi Prenume ale relaţiei ANGAJATI se introduce
astfel:
CREATE TABLE ANGAJATI(
Nume varchar(20) NOT NULL,
Prenume varchar(20) NOT NULL,...);

Valori implicite ale atributelor. În limbajul SQL2 se poate stabili o valoare implicită (DEFAULT)
pentru un atribut al unei relaţii. În cazul în care la inserarea unui tuplu nu se specifică valoarea unui atribut,
atunci atributul primeşte valoarea implicită (dacă a fost definită) sau valoarea NULL (dacă nu a fost definită o
valoare implicită pentru atributul respectiv, dar sunt admise valori NULL); dacă nu a fost definită o valoare
implicită şi nici nu sunt admise valori NULL, se generează o eroare.
În limbajul SQL2, valoarea implicită a unui atribut se poate specifica la crearea tabelului corespunzător,
ca o constrângere de coloană, împreună cu alte constrângeri. De exemplu, la crearea tabelului STUDENTI, se
poate specifica valoarea implicită pentru atributul Tara astfel:
CREATE TABLE STUDENTI(
IdStudent int PRIMARY KEY,
Nume varchar (20) NOT NULL,
Prenume varchar (20) NOT NULL,
Tara varchar (20) DEFAULT (‘Romania’) NULL ) ;
Constrângerea de verificare (CHECK). În limbajul SQL2, domeniile se pot stabili ca tipuri de date
predefinite (tipuri numerice, şiruri de caractere de lungime fixă sau variabilă, data calendaristică, timp). Pentru
fiecare atribut, definit pe un tip de date SQL, se pot adăuga constrângeri de verificare la definirea tabelului prin
comanda CREATE TABLE. Forma generală a unei constrângeri de verificare este:
[CONSTRAINT nume_constrangere] CHECK (conditie)

Numele constrângerii, precedat de cuvântul cheie CONSTRAINT este opţional. Rezultatul evaluării
expresiei care reprezintă condiţia specificată trebuie să fie o valoare logică (TRUE, FALSE sau NULL). La
introducerea (sau modificarea) valorilor atributelor se admit numai acele valori pentru care conditia de verificare

13
ia valoarea TRUE. De exemplu, în relaţia ANGAJATI se poate introduce condiţia ca atributul Salariu să aibă
valori mai mari sau egale cu o valoare dată (salariul minim pe economie). Atunci, comanda SQL de creare a
tabelului ANGAJATI se completează astfel:
CREATE TABLE ANGAJATI(
Nume varchar(20) NOT NULL,
Prenume varchar(20) NOT NULL,
DataNasterii Date,
Adresa varchar (50),
Functie varchar (20),
Salariu numeric,
CHECK (Salariu >= 1500));

2.3.2 Constrângeri de tuplu - Cheia primară şi cheile secundare


O relaţie este definită ca o mulţime de tupluri, deci tuplurile unei relaţii trebuie să fie distincte. Aceasta
înseamnă că într-o relaţie nu pot exista două (sau mai multe) tupluri care să conţină aceeaşi combinaţie de valori
ale tuturor atributelor.
De obicei, într-o schemă de relaţie există o submulţime de atribute SK cu proprietatea că nu există două
tupluri distincte ale relaţiei (în orice stare s-ar afla relaţia), ti şi tj care să aibă aceeaşi combinaţie de valori ale
atributelor submulţimii respective, adică:
ti[SK] ≠ tj[SK] dacă i ≠ j

O supercheie (superkey) a unei relaţii este o submulţime (SK) de atribute ale relaţiei care prezintă
proprietatea de unicitate, adică orice combinaţie de valori ale atributelor supercheii este unică pentru orice
stare a relaţiei.
Acest lucru înseamnă că, dacă se cunoaşte combinaţia de valori ale atributelor supercheii (valoarea
supercheii), atunci acel tuplu poate fi identificat în mod unic. Orice relaţie are cel puţin o supercheie: mulţimea
tuturor atributelor sale. Un concept mai util în dezvoltarea bazelor de date îl reprezintă conceptul de cheie
candidată (sau, mai simplu, cheie).
O cheie candidată (candidate key) este o supercheie ireductibilă.
Conform definiţiei de mai sus, o cheie candidată CK trebuie să prezinte următoarele două proprietăţi:
• Unicitate: nu există două tupluri diferite ale relaţiei care să conţină aceeaşi combinaţie de valori ale
atributelor cheii CK;
• Ireductibilitate: nu există nici o submulţime proprie, nevidă a cheii CK care să aibă proprietatea de
unicitate.
Acest lucru înseamnă că, dacă se elimină un atribut oarecare din submulţimea CK, noua submulţime de
atribute CK’ obtinută astfel nu mai are proprietatea de unicitate, adică vor putea exista două sau mai multe
tupluri care să prezinte aceeaşi combinaţie de valori ale atributelor din submulţimea CK’. Aşadar o cheie
candidată este o supercheie minimală, adică o supercheie din care nu se poate elimina nici un atribut fără a se
piarde proprietatea de unicitate.
Proprietatea cheii de a avea o valoare unică pentru fiecare tuplu (adică nu există două tupluri cu aceleaşi
valori ale atributelor cheii) este o constrângere de integritate a tuplurilor relaţiei respective, care trebuie să fie
respectată de orice stare a relaţiei (extensiune a relaţiei), în orice moment.
O cheie candidată poate să fie simplă (alcătuită dintr-un singur atribut), sau compusă (alcătuită din mai
multe atribute).
O cheie este determinată de înţelesul atributelor şi este o proprietate invariantă în timp; ea trebuie să se
menţină atunci când sunt adăugate tupluri noi în relaţie. De exemplu, în relaţia
ANGAJATI(Nume,Prenume,DataNaşterii,Adresa,Salariu) nu se poate desemna ca şi cheie
submulţimea de atribute {Nume,Prenume}, deoarece nu există nici o garanţie că nu vor exista niciodată (în
aceeaşi instituţie) două persoane cu acelaşi nume şi prenume, chiar dacă în momentul în care examinăm relaţia
această situaţie nu apare. În schimb, submulţimea de atribute {Nume,Prenume,DataNaşterii,Adresa}
poate fi cheie candidată dacă se consideră că nu pot exista două persoane cu acelaşi nume, prenume, data naşterii
şi adresă.
Atunci când există mai multe chei candidate, una dintre ele se alege ca şi cheie primară, celelalte chei
candidate fiind numite chei secundare, alternative sau unice. Aşadar:

14
O cheie primară (primary key) este o cheie candidată căreia proiectantul îi conferă un rol special de
accesare şi identificare a tuplurilor relaţiei.
Asupra cheii primare se impun următoarele restricţii:
• Nici o valoare a atributelor cheii primare nu poate fi modificată prin operaţii de actualizare.
• Nu se admit valori de NULL pentru nici unul dintre atributele cheii primare.
O cheie secundară (alternativă, unică) (secondary, alternate, unique key) este o cheie candidată care
nu a fost desemnată de proiectant ca şi cheie primară.
Cheile secundare admit valori NULL pentru unele din atributele lor dacă se respectă condiţia de
unicitate a valorilor.
Alegerea cheii primare dintre mai multe chei candidate este arbitrară, dar în mod obişnuit se alege acea
cheie care are cel mai mic număr de atribute. Deoarece condiţia de unicitate se verifică la fiecare tuplu nou
introdus şi toate operaţiile de identificare a tuplurilor se fac prin operaţii de comparaţie a valorilor atributelor
cheii primare, este evident că aceste operaţii sunt cu atât mai eficiente (cu timp de execuţie mai scăzut) cu cât
numărul de atribute ale cheii primare este mai mic.
O cheie primară compusă din atributele existente ale tipului de entitate se numeşte cheie naturală. În
general, cheile naturale sunt compuse din mai multe atribute (ceea ce produce scăderea eficienţei operaţiilor
relaţionale) şi de cele mai multe ori se folosesc chei artificiale. O cheie primară artificială este un atribut care se
adaugă în schema relaţiei pentru identificarea unică a tuplurilor. De exemplu, în relaţia ANGAJATI se adaugă
atributul IdAngajat, ca număr de identificare al fiecărui angajat al instituţiei:
ANGAJATI(IdAngajat,Nume,Prenume,DataNasterii,Adresa,Salariu)
Acest atribut este o cheie artificială a relaţiei şi poate identifica în mod unic un tuplu, deoarece (prin
convenţie) nu se atribuie acelaşi număr de identificare la mai mult de un angajat. Submulţimi de atribute care
includ atributul IdAngajat ca {IdAngajat,Nume}, {IdAngajat,Nume,Prenume}, etc., sunt
superchei ale relaţiei. Rămâne în grija programatorului sau a sistemului de gestiune a bazei de date să asigure
unicitatea acestui număr de identificare, dar acest lucru este întotdeauna posibil. În secţiunile care urmează cheia
primară se va marca prin subliniere sau scriere îngroşată (bold).
În limbajul SQL, constrângerea de cheie primară se introduce la definirea relaţiei (tabelului) prin
comanda CREATE TABLE. Dacă cheia primară este simplă (formată dintr-un singur atribut), constrângerea de
cheie primară se poate specifica la definirea atributului, ca o constrângere de atribut. Dacă cheia primară este
compusă (formată din mai multe atribute), atunci constrângerea de cheie primară se specifică după definirea
atributelor, ca o constrângere de tabel sub forma:
[CONSTRAINT nume_constr] PRIMARY KEY (lista_atribute)
Această formă de definire se poate folosi şi pentru o cheie primară simplă şi atunci lista are un singur
atribut. Cheile secundare se definesc în mod asemănător, folosind specificatorul UNIQUE în locul
specificatorului PRIMARY KEY. Cu aceste notaţii, definiţiile mai complete ale tabelelor ANGAJATI şi SECTII
pot arăta astfel:
CREATE TABLE ANGAJATI (
IdAngajat int PRIMARY KEY,
Nume varchar(20) NOT NULL,
Prenume varchar(20) NOT NULL,
DataNasterii Date,
Adresa varchar(50),
Salariu numeric,
CHECK (Salariu >= 1500),
CONSTRAINT UK UNIQUE(Nume,Prenume,DataNasterii,Adresa));
CREATE TABLE SECTII (
IdSectie int PRIMARY KEY,
Nume varchar(50) NOT NULL,
Buget numeric);
În fiecare dintre cele două tabele s-a definit cheia primară artificială printr-un atribut de identificare
(IdSectie, respectiv IdAngajat), care trebuie să ia valori unice în cadrul tabelului. În tabelul ANGAJATI s-
a mai definit o cheie unică şi o constrângere de verificare.
Modul de asigurare a unicităţii valorii cheii primare artificiale depinde de sistemul SGBD folosit. De exemplu, în
sistemul Microsoft SQL Server se pot obţine valori unice ale cheii primare folosind parametrul IDENTITY, care
asigură incrementarea valorii atributului cheii la introducerea fiecărei linii noi. .În sistemele Oracle se pot genera
chei artificiale folosind obiecte SEQUENCE. Un obiect SEQUENCE se creează pentru a genera cheia primară
artificială a unui tabel, este memorat în baza de date şi returnează un număr unic la fiecare apel al metodei
NEXTVAL.

15
2.3.3 Constrângeri între relaţii - Cheia străină
Asocierile (relationships) între tipurile de entităţi definite în modelul conceptual (prin diagrama
Entitate-Asociere) al unei baze de date se realizează în modelul relaţional prin intermediul cheilor străine.
Reluând exemplul din capitolul precedent, se consideră relaţiile SECTII şi ANGAJATI, aflate în
asociere 1:N, conform diagramei E-A şi a semnificaţiei celor două mulţimi de entităţi. Pentru a asigura asocierea
dintre relaţia ANGAJATI şi relaţia SECTII, se adaugă în relaţia ANGAJATI un atribut suplimentar,
IdSectie, care reprezintă identificatorul (numărul) secţiei în care lucrează angajatul respectiv:
ANGAJATI(IdAngajat,Nume,Prenume,DataNasterii,Adresa,Salariu,IdSectie)
Pentru orice tuplu din relaţia ANGAJATI, atributul IdSectie trebuie să aibă o valoare egală cu
valoarea atributului corespunzător (IdSectie) dintr-un tuplu oarecare din relaţia SECTII (sau poate avea
valoarea NULL, dacă nu este impusă constrângerea NOT NULL). Este evident că pentru starea la un moment dat
a relaţiilor prezentate în figura 2.2, nu poate să existe în relaţia ANGAJATI un tuplu cu valorile atributelor
(5,Marinescu,...5), dat fiind că în instituţia respectivă nu există o secţie cu identificatorul 5. Bineînţeles,
dacă ulterior se adaugă mai întâi în relaţia SECTII un tuplu care să aibă valoarea atributului IdSectie egală
cu 5 (de exemplu tuplul(5,Marketing,1000000)), atunci se poate admite în relaţia ANGAJATI
tuplul(5,Marinescu,... 5).
SECTII
IdSectie Nume Buget
1 Productie 4000000
2 Proiectare 3000000
3 Cercetare 2000000
4 Documentare 1000000
ANGAJATI
IdAngajat Nume Prenume DataNasterii Adresa Salariu IdSectie
1 Ionescu Ion 1945.01.05 Bucuresti 4000 1
2 Popescu Petre 1972.06.21 Bucuresti 3500 1
3 Vasilescu Ana 1966.04.02 Bucuresti 3000 2
4 Ionescu Ion 1970.11.12 Bucuresti 2500 3

Fig. 2.2. Tabelele SECTII şi ANGAJATI.

Din punct de vedere al notaţiilor, în instrucţiunile SQL de introducere, actualizare şi interogare valorile
atributelor definite ca şiruri de caractere se scriu între ghilimele simple, de exemplu 'Marinescu', etc. La
listarea sau afişarea rezultatelor însă, se va scrie numai valoarea atributului, fără acest marcaj, pentru
simplificarea prezentării exemplelor.

2.3.3.1 CHEIA STRĂINĂ


O cheie străină (foreign key) este o submulţime FK de atribute ale unei relaţiei R1 care referă relaţia
R2 şi satisface următoarele condiţii: (a) atributele cheii străine FK sunt definite pe domenii compatibile cu cele
ale atributelor unei cheii candidate CK a relaţiei R2 şi (b) combinaţia de valori ale atributelor FK într-un tuplu
din relaţia R1, fie este identică cu combinaţia de valori ale atributelor CK a unui tuplu oarecare din starea
curentă a relaţiei R2, fie ia valoarea NULL.
Relaţia care conţine cheia străină se numeşte relaţia care referă (R1 în definiţia de mai sus), iar relaţia
care conţine cheia candidată se numeşte relaţia referită (R2 în definiţia de mai sus).
Din punct de vedere relaţional, două domenii sunt compatibile dacă ele sunt comparabile din punct de
vedere semantic. De exemplu, are sens să se compare atributul IdSectie din relaţia SECTII cu atributul
IdSectie din relaţia ANGAJATI, deoarece aceste atribute reprezintă numere de identificare ale secţiilor, deci
domeniile celor două atribute sunt compatibile. În schimb, nu are sens să fie comparate numerele de identificare
ale secţiilor (atributul IdSecţie) cu numerele de identificare ale angajaţilor (atributul IdAngajat), deci
domeniile celor două atribute sunt incompatibile.
Cheia străină realizează asocierea N:1 între relaţiile R1 şi R2 (ceea ce este echivalent cu asocierea 1:N
între relaţiile R2 şi R1) şi reprezintă o constrângere între două relaţii, numită constrângere referenţială.
Din punct de vedere al sistemelor SGBD reale şi al limbajelor de definiţie şi manipulare a datelor,
noţiunea de compatibilitate semantică a domeniilor este mai puţin evidenţiată. În limbajul SQL2 verificarea de
compatibilitate a domeniilor se rezumă la verificarea tipurilor de date ale atributelor corespondente, iar

16
compatibiltatea semantică trebuie să fie asigurată de către proiectant. Limbajul SQL2 admite ca şi “compatibile”
domenii care sunt definite pe acelaşi tip de date sau pe tipuri de date din aceeaşi categorie (şir de caractere,
număr real, etc), chiar dacă sunt incompatibile din punct de vedere semantic. De exemplu, limbajul SQL permite
compararea valorilor atributului IdSectie cu valori ale atributului IdAngajat ale relaţiei ANGAJATI, dat
fiind că acestea sunt definite pe acelaşi tip de date (integer).
În limbajul SQL cheia străină se specifică la crearea relaţiei (tabelului) care referă printr-o constrângere
(CONSTRAINT) în care se specifică atributele cheii străine şi atributele cheii candidate (primare)
corespunzătoare din relaţia referită:
[CONSTRAINT nume_constr] FOREIGN KEY (cheie_straina)
REFERENCES relatia_referita (cheie_candidata)
Numele atributelor cheii străine nu trebuie să fie neapărat aceleaşi cu numele atributelor corespondente
din cheia candidată referită, deşi această notaţie este frecvent folosită. Cu această notaţie, se completează
instrucţiunea de creare a tabelului ANGAJATI astfel:
CREATE TABLE ANGAJATI (
IdAngajat integer PRIMARY KEY,......
IdSecţie integer,
CHECK (Salariu >=1500),
UNIQUE (Nume,Prenume,DataNasterii,Adresa),
FOREIGN KEY (IdSectie)REFERENCES SECTII(IdSectie));
Se observă că în modelul relaţional asocierea între relaţii se realizează prin egalitatea valorilor
atributelor cheii străine cu ale cheii referite, fără să fie necesară memorarea unor legături (link-uri) explicite.
Aceasta este una dintre cele mai remarcabile proprietăţile ale modelului relaţional, care permite efectuarea
oricărei interogări fără ca aceasta să fie prevăzută prin structurarea explicită a datelor.

2.3.3.2 MENŢINEREA INTEGRITĂŢII REFERENŢIALE A DATELOR


Integritatea referenţială (referential integrity) este proprietatea bazei de date care garantează că
oricare valoare a unei chei străine se regăseşte printre valorile cheii candidate corespunzătoare din relaţia
referită, sau cheia străină are valoarea NULL (dacă atributele acesteia nu sunt supuse constr. NOT NULL).
Operaţiile de modificare a stării unei relaţii (introducerea, ştergerea şi actualizarea tuplurilor relaţiei)
pot afecta integritatea referenţială a unei baze de date, dacă modificările dintr-o relaţie se fac individual, fără să
se ţină seama de referinţele către şi de la aceasta. Restricţiile care se impun operaţiilor de modificare a relaţiilor
depind de rolul relaţiei în diagrama de referenţiere a bazei de date: relaţie care referă sau relaţie referită. De
multe ori însă, o relaţie poate avea ambele roluri, atât de relaţie care referă cât şi de relaţia referită şi atunci
restricţiile de modificare trebuie să respecte ambele situaţii.
Operaţia de introducere se poate face fără restricţii într-o relaţie referită. În schimb, la introducerea
unui tuplu nou într-o relaţie care referă (care conţine o cheie străină) trebuie să se verifice că în relaţia referită
există un tuplu care are valorile atributelor cheii referite egale cu valorile atributelor cheii străine a tuplului de
introdus. Dacă această condiţie nu este satisfăcută, operaţia de introducere este refuzată.
Operaţia de ştergere a unui tuplu se poate face fără nici o restricţie într-o relaţie care referă o altă
relatie. Într-o relaţie referită, ştergerea unui tuplu poate fi executată în două moduri: ştergere restricţionată sau
ştergere în cascadă.
Ştergerea restricţionată interzice ştergerea unui tuplu din relaţia referită dacă acesta este referit de un
tuplu din relaţia care o referă. Pentru exemplul din figura 2.2, nu se poate şterge tuplul
(2,Proiectare,3000000) din relaţia SECTII, deoarece acest tuplu este referit de tuplul
(3,Vasilescu,...2) din relaţia ANGAJATI. În schimb, tuplul (4,Documentare,1000000) poate fi
şters din relaţia SECTII, deoarece acesta nu este referit de nici un tuplu din relaţia ANGAJATI.
Ştergerea în cascadă permite ştergerea unui tuplu din relaţia referită; dacă tuplul şters era referit de
unul sau mai multe tupluri, atunci se şterg şi acestea din relaţia care o referă; dacă tuplurile şterse din relaţia care
referă sunt, la rândul lor referite de alte tupluri, atunci trebuie să fie şterse şi acestea, ş.a.m.d. Se execută deci o
ştegere în cascadă, pe tot lanţul de referiri.
Operaţia de actualizare poate fi privită ca o ştergere urmată de o introducere, deci restricţiile de
actualizare reprezintă combinaţia restricţiilor de introducere şi de ştergere. Într-o relaţie care referă, actualizarea
cheii străine a unui tuplu este admisă dacă noua valoare a cheii străine se regăseşte ca valoare a cheii candidate
corespunzătoare într-unul din tuplurile relaţiei referite. Într-o relaţie referită, actualizarea valorii cheii secundare
(cheia primară nu poate fi modificată) poate fi făcută în aceleaşi moduri ca şi ştergerea tuplurilor: actualizare
restricţionată sau actualizare în cascadă.

17
Stabilirea modului de ştergere sau de actualizare a tuplurilor se face în comenzile SQL de creare sau
modificare a tabelelor, prin adăugarea uneia din opţiunile ON DELETE, respectiv ON UPDATE, constrîngerii de
cheie străină. Valorile posibile ale acestor opţiuni sunt RESTRICT (pentru ştergerea restricţionată) sau
CASCADE (pentru ştergerea în cascadă); valoarea RESTRICT este implicită. Ca exemplu, se completează
instrucţiunea SQL de creare a tabelului ANGAJATI cu opţiunea de ştergere în cascadă pentru cheia străină
IdSecţie:
CREATE TABLE ANGAJATI (
IdAngajat integer PRIMARY KEY,
Nume varchar (20) NOT NULL,
………………………................................………………..................
FOREIGN KEY (IdSectie)REFERENCES SECTII(IdSectie) ,
ON DELETE CASCADE );

2.4 Indexarea relaţiilor


Unul din avantajele sistemelor de gestiune este acela de a elibera proiectantul bazei de date de
necesitatea de a cunoaşte detaliile de organizare a datelor, prin asigurarea independenţei datelor. Totuşi, această
independenţă nu este completă şi, în stadiul actual de realizare a sistemelor de baze de date, programatorii de
aplicaţii trebuie să ia în consideraţie influenţa pe care modul de stocare şi de regăsire a datelor îl are asupra
performanţelor aplicaţiilor.
Într-o relaţie, privită ca o colecţie de elemente în care nu sunt admise elemente duplicat (deoarece este o
mulţime), operaţiile de bază sunt, ca în orice colecţie de elemente, căutarea, inserarea şi ştergerea unui element,
cărora, desigur, le corespund operaţiile (interogare, inserare, ştergere) din limbajele de manevrare a datelor în
relaţii. Relaţiile unei baze de date sunt memorate în fişiere pe disc, iar comenzile de manevrare a datelor sunt
transformate de SGBD în operaţii asupra fişierelor care stochează relaţiile bazei de date. Modul şi timpul de
execuţie a acestor operaţii de bază depind de modul de reprezentare a mulţimii de elemente (tupluri) ale relaţiei.
În cazul unei mulţimi reprezentate printr-o colecţie neordonată de elemente, timpul de căutare a unui
element creşte proporţional cu numărul de elemente ale mulţimii, deoarece, în cazul cel mai defavorabil, este
necesar să fie parcurse toate elementele colecţiei pentru a găsi elementul dorit. În acest caz se poate considera
timpul de căutare în O(N) pentru o mulţime cu N elemente.
Timpul de căutare a unui element poate să fie micşorat semnificativ dacă elementele mulţimii sunt
ordonate; de exemplu, la reprezentarea unei mulţimi ca arbore binar ordonat, limita asimptotică a timpului de
căutare este în O(log N).
Pentru inserarea unui element într-o mulţime, trebuie mai întâi să fie căutat elementul respectiv în
colecţia (ordonată sau nu) prin care este reprezentată mulţimea respectivă; dacă există deja un element identic, se
interzice inserarea noului element; dacă nu există, atunci se introduce noul element. Timpul de inserare a unui
element într-o mulţime este compus din timpul de căutare al unui element, la care se adaugă timpul de memorare
propriu-zis. Observaţii similare se pot face privind timpul de ştergere a unui element dintr-o mulţime.
În general, operaţiile de căutare, inserare şi ştergere a elementelor într-o mulţime se execută mai rapid
dacă elementele mulţimii sunt reprezentate printr-o colecţie ordonată. Astfel se ajunge la situaţia că, deşi o
relaţie nu presupune ordonarea tuplurilor sale, pentru accelerarea operaţiilor de căutare, inserare şi ştergere, se
folosesc colecţii ordonate, ca de exemplu arbori binari ordonaţi sau tabele de dispersie (hash table).
Un index al unei relaţii (index) este o structură auxiliară memorată în baza de date care permite
accesul rapid la înregistrările (tuplurile) relaţiilor prin ordonarea acestora.
La definirea unei relaţii se stabilesc două categorii de indexuri: indexul primar al relaţiei, care
determină localizarea tuplurilor în fişierele bazei de date, şi zero, unul sau mai mulţi indexuri secundare, care nu
modifică localizarea tuplurilor, dar sunt folosiţi pentru regăsirea tuplurilor după un criteriu dat.

2.4.1 Indexul primar


Indexul primar (primary index) se defineşte pe unul sau mai multe atribute ale relaţiei şi reprezintă
cheia (eticheta) după care ordonează tuplurile relaţiei.
Fiecare SGBD prevede o anumită modalitate de reprezentare a indexului primar. În continuare se va
folosi termenul de etichetă de ordonare, iar termenul de cheie de ordonare va fi evitat, pentru a nu se confunda
cu cheia relaţiei, întrucât este posibil să nu reprezinte acelaşi lucru, deşi, în general, sistemele SGBD definesc în
mod implicit indexul primar pe cheia primară a relaţiei.
Operaţiile de căutare sau ştergere în care se specifică valoarea atributului index primar se execută de
asemenea eficient: se calculează mai întâi grupul căruia îi aparţine tuplul, prin aplicarea funcţiei de dispersie h
asupra valorii atributului index, apoi se caută poziţia tuplului în lista înlănţuită corespunzătoare grupului. După
aceasta, se continuă cu execuţii specifice operaţiei: se şterge sau se citeşte tuplul găsit. De exemplu, pentru

18
rezolvarea interogării "Care este oraşul de domiciliu al studentului cu identificatorul 200?" se găseşte tuplul
(200,Marin,Dan,Craiova) folosind valoarea indexului primar (200).
Dacă într-o operaţie de căutare sau ştergere nu se specifică valoarea atributului index primar, atunci
căutarea unui anumit tuplu presupune parcurgerea (în cazul cel mai defavorabil) a tuturor listelor tuturor
grupurilor tabelei de dispersie. De exemplu, interogarea "Care este oraşul de domiciliu al studentului cu numele
Marin?" găseşte tuplul (200,Marin,Dan,Craiova) parcurgând toate tuplurile şi comparând valoarea
atributului Nume cu constanta Marin, până este găsit tuplul care îndeplineşte această condiţie.
Astfel de situaţii sunt frecvent întâlnite în aplicaţii, dat fiind că interogările se formulează de obicei
folosind un nume, nu un identificator (care este probabil cheie primară, deci conţine indexul primar, dar este
necunoscut utilizatorilor). Pentru rezolvarea mai eficientă a unor astfel de interogări se pot defini indexuri
secundare pe acele atribute care intervin frecvent în interogări.

2.4.2 Indexuri secundare


Un index secundar pe un atribut A al unei relaţii (secondary index) este o structură care conţine o
mulţime de perechi (v,L) ordonate după v; fiecare pereche corespunde unui tuplu al relaţiei, v este valoarea
atributului A, iar L este adresa tuplului respectiv în structura indexului primar al relaţiei.
Un index secundar nu modifică adresa de memorare a unui tuplu (care este conţinută în structura
indexului primar), dar conţine informaţii suplimentare care permit identificarea rapidă a unui tuplu după valoarea
atributului indexului.
Din punct de vedere al eficienţei operaţiilor de căutare în relaţii este avantajos să fie definiţi oricât de
mulţi indexuri secundare, dar fiecare index secundar ocupă spaţiu de memorare suplimentar şi trebuie să fie
actualizat la fiecare operaţie de inserare şi ştergere a unui tuplu, ceea ce înseamnă timp de execuţie suplimentar.
În general, se recomandă utilizarea unui număr cât mai mic de indexuri secundare, definiţi pe atributele care
intervin cel mai frecvent în condiţiile de interogare. Această decizie o ia proiectantul bazei de date prin analiza
cerinţelor din domeniul pentru care se dezvoltă baza de date şi aplicaţiile corespunzătoare.
Dat fiind că indexurile sunt folosite în special pentru îmbunătăţirea performanţelor bazelor de date şi nu
fac parte din modelul relaţional de bază, ei nu sunt cuprinşi în standardul limbajului SQL. Însă majoritatea
sistemelor SGBD încorporează indexuri, conţinând instrucţiuni pentru crearea indexurilor de forma:
CREATE [optiuni] INDEX nume_index ON tabel (lista_atribute);
Forma exactă şi opţiunile acestei instrucţiuni variază de la un sistem SGBD la altul. Una din opţiunile
care se pot introduce în instrucţiunea CREATE INDEX este opţiunea UNIQUE, care specifică faptul că nu pot
exista două tupluri cu aceeaşi combinaţie de valori ale atributelor indexului, deci acele atribute reprezintă o cheie
unică a relaţiei. Unele sisteme SGBD adaugă câte un index secundar pentru fiecare cheie unică, definită prin
constrângerea UNIQUE din comanda CREATE TABLE.

19
3. INTEROGAREA BAZELOR DE DATE

Interogarea (query) este operaţia prin care se obţin datele dorite dintr-o bază de date, selectate
conform unui anumit criteriu (condiţie). Dat fiind că operaţia de interogare este cea mai importantă operaţie de
manevrare a datelor, de multe ori limbajele de manevrare a datelor sunt denumite limbaje de interogare.
Pentru formularea conceptuală a interogărilor în bazele de date relaţionale s-au dezvoltat două limbaje
abstracte de interogare: algebra relaţională şi calculul relaţional [Date95], [Sim99].
Algebra relaţională (relational algebra) constă dintr-o mulţime de operaţii care au ca operanzi relaţii,
iar rezultatul este tot o relaţie.
Calculul relaţional (relational calculus) este bazat pe calculul predicatelor şi exprimă o interogare
formulând o definiţie a rezultatului dorit (de regulă, o relaţie) printr-o expresie de calcul relaţional. Variabilele
unei expresii de calcul relaţional pot fi variabile de tuplu (variabile ale căror valori sunt definite pe mulţimea
tuplurilor unei anumite relaţii) sau variabile de domeniu (variabile ale căror valori sunt definite pe domenii de
definiţie ale atributelor). Pe baza unor astfel de variabile se defineşte calculul relaţional al tuplurilor, respectiv
calculul relaţional al domeniilor.
Aceste limbaje de interogare abstracte, algebra relaţională, calculul relaţional al tuplelor şi calculul
relaţional al domeniilor sunt echivalente din punct de vedere al capacităţii de exprimare a interogărilor,
diferenţele constând în modul de formulare a acestora. S-a demonstrat că, pentru orice expresie de algebră
relaţională, se poate găsi o expresie de calcul relaţional echivalentă şi invers.
Limbajele de interogare reale implementate în sistemele de baze de date relaţionale sunt limbaje
definite pe baza unuia sau altuia din limbajele de interogare abstracte, sau pe o combinaţie a acestora. De
exemplu:
• Limbajul SQL2 este în cea mai mare parte bazat pe algebra relaţională, dar mai conţine şi
construcţii derivate din calculul relaţional.
• Limbajul ISBL (Information System Base Language) al firmei IBM este bazat în întregime pe
algebra relaţională.
• Limbajul QUEL al SGBD Ingres este bazat pe calculul relaţional al tuplurilor.
• Limbajul QBE (Query by Example), dezvoltat la firma IBM este bazat pe calculul relaţional al
domeniilor.
Un limbaj de interogare real este denumit relaţional complet dacă implementează toate operaţiile
prevăzute de unul din limbajele de interogare abstracte. În general, toate limbajele relaţionale implementate în
sistemele SGBD sunt limbaje relaţionale mai mult decât complete, conţinând şi operaţii care nu sunt prevăzute în
limbajele relaţionale abstracte, ca de exemplu, efectuarea unor calcule aritmetice asupra valorilor unor atribute
(sumă, medie, minim, maxim), funcţii de tipărire a relaţiilor, etc.
Limbajul SQL2 este limbajul cel mai utilizat în sistemele relaţionale şi de aceea, în continuare
majoritatea exemplificărilor vor fi prezentate în SQL2.

3.1 ALGEBRA RELAŢIONALĂ


Algebra relaţională (relational algebra) exprimă interogările prin aplicarea unor operatori specializaţi
(operatorii algebrei relaţionale) asupra relaţiilor. E.F. Codd a propus opt operaţii ale algebrei relaţionale,
grupate în două categorii:
• Operaţii pe mulţimi: reuniunea (union), intersecţia (intersection), diferenţa (difference) şi produsul
cartezian (Cartesian product). Aceste operatii reprezintă adaptarea operatiilor corespunzătoare din
teoria mulţimilor şi acţionează asupra relaţiilor văzute ca mulţimi de elemente (tupluri), fără a lua
în consideraţie compoziţia fiecărui element.
• Operaţii relaţionale speciale: restricţia (restriction), proiecţia (projection), joncţiunea (join) şi
diviziunea (division). Aceste operaţii iau în consideraţie compoziţia tuplurilor, formate din valori
ale atributelor relaţiilor.
Toate aceste operaţii trebuie să asigure proprietatea de închidere, adică rezultatul fiecărei operaţii
trebuie să fie tot o relaţie. Această proprietate permite efectuarea operaţiilor imbricate: proiecţia unei joncţiuni
dintre o relaţie şi restricţia aplicată altei relaţii, etc.
Restricţia şi proiecţia sunt operaţii unare (au un singur operand, o relaţie); operatiile pe mulţimi,
joncţiunea şi diviziunea sunt operaţii binare (au doi operanzi, două relaţii).
3.1.1 OPERAŢII PE MULŢIMI
În operaţiile asupra relaţiilor considerate ca mulţimi se impun anumite condiţii celor doi operanzi, astfel
încât relaţia rezultat să fie obţinută ca o mulţime de tupluri omogene. Aceste condiţii depind de tipul operaţiei:
reuniunea, intersecţia şi diferenţa necesită ca relaţiile să fie compatibile, iar produsul cartezian necesită ca
numele atributelor celor două relaţii operand să fie distincte. Pentru ca două relaţii să fie compatibile, trebuie să
aibă acelaşi număr de atribute şi atributele corespondente să fie definite pe domenii compatibile.
Reuniunea a două relaţii compatibile R şi S este o relaţie QU = R ∪ S care conţine toate tuplurile ce
aparţin fie relaţiei R, fie relaţiei S, fie ambelor relaţii. Tuplurile care aparţin ambelor relaţii se introduc în relaţia
rezultat o singură dată, adică nu se duplică.
Operaţia de reuniune se exprimă în limbajul SQL ca o reuniune a două tabele obţinute ca rezultat a două
comenzi SELECT, cu sintaxa:
SELECT lista_coloane1 FROM tabel1 [WHERE condiţie1]
UNION
SELECT lista_coloane2 FROM tabel2 [WHERE condiţie2];

Cele două liste de coloane din clauzele SELECT trebuie să conţină atribute compatibile. Tabelele din
clauzele FROM, ca şi condiţiile din clauzele WHERE pot fi identice sau diferite.
Fie relaţiile: ANGAJATI(IdAngajat,Nume,Prenume,DataNasterii, Adresa,Functie,
Salariu) şi FURNIZORI(IdFurnizor,Nume,Prenume,DataNaşterii,Adresa,Firma). O operaţie de
reuniune pe baza acestor relaţii poate arăta astfel:
SELECT Nume,Prenume FROM ANGAJATI WHERE Adresa = ‘Bucuresti’
UNION
SELECT Nume,Prenume FROM FURNIZORI WHERE Adresa = ‘Bucureşti’;

Rezultatul va fi o relaţie cu atributele (Nume,Prenume) care conţine numele şi prenumele tuturor


angajaţilor şi ale furnizorilor care locuiesc în oraşul Bucureşti. Dacă există tupluri duplicat (un angajat şi un
furnizor cu acelaşi nume şi prenume, ceea ce este posibil), relaţia rezultat conţine un singur tuplu cu valorile
respective. Opţiunea SQL UNION ALL permite ca rezultatul să conţină duplicate, deci acest rezultat nu mai poate
fi numit relaţie. După cum se observă, limbajul SQL admite unele construcţii care nu respectă cerinţele teoretice
ale modelului relaţional.
Intersecţia a două relaţii compatibile R şi S este o relaţie QI = R ∩ S care conţine toate tuplurile care
aparţin atât relaţiei R cât şi relaţiei S. La fel ca şi reuniunea, operaţia de intersecţie se exprimă în SQL ca
intersecţie a două tabele obţinute ca rezultat a două comenzi SELECT, cu sintaxa:
SELECT lista_coloane1 FROM tabel1 [WHERE condiţie1]
INTERSECT
SELECT lista_coloane2 FROM tabel2 [WHERE condiţie2];

Diferenţa a două relaţii compatibile R şi S este o relaţie QM = R - S care conţine toate tuplurile care
aparţin relaţiei R, dar nu aparţin relaţiei S. Operaţia de diferenţă se exprimă în SQL ca diferenţă a două tabele
obţinute ca rezultat a două comenzi SELECT, cu sintaxa:
SELECT lista_coloane1 FROM tabel1 [WHERE condiţie1]
MINUS
SELECT lista_coloane2 FROM tabel2 [WHERE condiţie2];

Reuniunea şi intersecţia sunt comutative (R ∪ S = S ∪ R; R ∩ S = S ∩ R) şi asociative (R ∪(S ∪ T)


= (R ∪ S)∪ T; R ∩(S ∩ T) = (R ∩ S)∩ T). Diferenţa nu este nici comutativă (R – S ≠ S - R), nici
asociativă (R –(S – T) ≠ (R – S)– T).
Produsul cartezian. În teoria mulţimilor, produsul cartezian al mulţimilor R şi S este o mulţime
compusă din toate perechile ordonate de elemente ale celor două mulţimi: R × S = {<a,b> ⏐ a∈ R,b∈ S}.
În algebra relaţională, produsul cartezian al relaţiilor R(A1,A2,... An) şi S(B1,B2,...Bm) este o
relaţie QC(A1,A2,....An,B1,B2,...Bm) = R × S care are ca atribute toate atributele primei relaţii plus toate
atributele celei de-a doua relaţii. Pentru a se obţine tuplurile relaţiei rezultat se combină (se concatenează)
valorile atributelor fiecărui tuplu din prima relaţie cu valorile atributelor tuturor tuplurilor din cea de-a doua
relaţie.
Din această definiţie se observă că gradul relaţiei rezultat este egal cu suma gradelor celor două relaţii
operanzi, iar cardinalitatea este egală cu produsul cardinalităţilor celor două relaţii operand.

2
Ca exemplu, se va calcula produsul cartezian al relaţiilor ANGAJATI şi SECTII, prezentate mai jos în
figura. 3.1.

ANGAJATI
IdAngajat Nume Prenume DataNasterii Adresa Salariu IdSectie
1 Ionescu Ion 1945.01.05 Bucuresti 4000 1
2 Popescu Petre 1972.06.21 Bucuresti 3500 1
3 Vasilescu Ana 1966.04.02 Bucuresti 3000 2
4 Ionescu Ion 1970.11.12 Bucureşti 2500 3

SECTII

IdSectie Nume Buget


1 Productie 4000000
2 Proiectare 3000000
3 Cercetare 2000000

ANGAJATI × SECTII
IdAngajat ANGAJATI. .... ANGAJATI. SECTII. SECTII. Buget
Nume IdSectie IdSectie Nume
1 Ionescu … 1 1 Productie 4000000
1 Ionescu … 1 2 Proiectare 3000000
1 Ionescu … 1 3 Cercetare 2000000
2 Popescu … 1 1 Productie 4000000
2 Popescu … 1 2 Proiectare 3000000
2 Popescu … 1 3 Cercetare 2000000
3 Vasilescu … 2 1 Productie 4000000
3 Vasilescu … 2 2 Proiectare 3000000
3 Vasilescu … 2 3 Cercetare 2000000
4 Ionescu … 3 1 Productie 4000000
4 Ionescu … 3 2 Proiectare 3000000
4 Ionescu … 3 3 Cercetare 2000000

Fig. 3.1. Produsul cartezian a două relaţii.

Pentru ca rezultatul produsului cartezian să fie corect din punct de vedere relaţional, este necesar ca
atributele celor două relaţii operand să aibă nume diferite, deoarece în relaţia rezultat nu pot exista două atribute
cu acelaşi nume. Această cerinţă se rezolvă uşor, prin calificarea numelor unor atribute cu numele relaţiei căreia
îi aparţin sau prin redenumirea atributelor.
Calificarea numelui unui atribut cu numele relaţiei se realizează prin scrierea numelui atributului
precedat de numelui relaţiei, cele două nume fiind separate prin operatorul punct (.), la fel ca în reprezentarea
datelor sau funcţiilor membre ale unui obiect (instanţă a unei clase) în programarea obiect-orientată. De
exemplu, atributul IdSectie din relaţiile ANGAJATI şi SECTII se poate diferenţia prin calificare astfel:
SECTII.IdSectie şi ANGAJATI.IdSectie.
Pentru redenumirea atributelor în algebra relaţională se poate folosi o operaţie specială, care se adaugă
celor opt operaţiii de bază. Sintaxa conceptuală a operaţiei de redenumire este:
RENAME nume_relatie.nume_atribut AS noul_nume_atribut

Operaţia produs cartezian este conceptual comutativă, adică R×S = S×R, dacă se consideră că atributele
unei relaţii nu sunt ordonate. Dacă se consideră schema relaţiei rezultat ca listă a atributelor sale, atunci, prin
convenţie, atributele primei relaţii operand sunt primele în lista de atribute a relaţiei rezultat, iar atributele celei
de-a doua relaţii urmează în lista atributelor relaţiei rezultat.
Operaţia produs cartezian este asociativă, dacă se consideră că ordinea atributelor într-o schemă de
relaţie şi ordinea tuplurilor într-o relaţie nu este relevantă: R ×(S × T) = (R × S)× T.
În limbajul SQL, produsul cartezian a două tabele R şi S se obţine ca o variantă a instrucţiunii SELECT,
într-una din formele:

3
SELECT * FROM R,S;
SELECT lista_coloane FROM R,S;
În prima formă, limbajul SQL admite operaţia produs cartezian şi în situaţia în care în cele două relaţii
operand există două atribute cu acelaşi nume, subînţelegându-se că atributele rezultatului sunt ordonate, mai întâi
fiind atributele primei relatii, urmate de atributele celei de-a doua relatii.
Pentru cea de-a două formă, atributele cu acelaşi nume trebuie să fie calificate cu numele relaţiei
respective. De exemplu, produsul cartezian al relaţiilor SECTII(IdSectie,Nume,Buget) şi
ANGAJATI(IdAngajat,Nume,Prenume,DataNasterii,Adresa,Salariu,IdSectie) se poate scrie în
SQL într-una din formele:
SELECT * FROM SECTII,ANGAJATI;
SELECT SECTII.IdSectie,SECTII.Nume,Buget,IdAngajat,
ANGAJATI.Nume,Prenume,DataNasterii,Adresa,Salariu,
ANGAJATI.IdSectie FROM SECTII,ANGAJATI;

În plus, în limbajul SQL se pot redenumi atributele folosind cuvântul cheie AS între numele unui atribut
şi redenumirea acestuia. În această formă, interogarea precedentă poate fi scrisă astfel:
SELECT SECTII.IdSectie,SECTII.Nume AS SNume,
Buget,IdAngajat,ANGAJATI.Nume AS ANume,
Prenume,DataNasterii,Adresa,Salariu,
ANGAJATI.IdSectie FROM SECTII,ANGAJATI;

În unele implementări ale limbajului SQL nu este necesar cuvântul cheie AS pentru redenumirea
atributelor.

3.1.2 OPERAŢII RELAŢIONALE SPECIALE


În operaţiile speciale asupra relaţiilor se ia în consideraţie compoziţia tuplurilor (combinaţii de valori
ale atributelor) şi se impun anumite condiţii atributelor acestora.
Restricţia (restriction) este o operaţie relaţională unară care selectează dintre tuplurile relaţiei
operand acele tupluri care îndeplinesc o condiţie dată.
Operaţia de restricţie se mai numeşte şi selecţie (şi, într-adevăr, restricţia face o selecţie a tuplurilor),
dar este mai bine să fie evitată această denumire deoarece se poate confunda cu instrucţiunea SELECT, care are
rolul de instrucţiune generală de interogare.
Operaţia de restricţie se notează: σθ(R), unde θ este o expresie logică specificată asupra atributelor
relaţiei R. În relaţia rezultat sunt selectate acele tupluri ale relaţiei R pentru care expresia θ are valoarea 1
(TRUE). Relaţia rezultat are aceleaşi atribute ca şi relaţia operand.
Expresia logică θ este formată din una sau mai multe variabile logice v conectate prin operatorii logici
AND, OR, NOT, ca de exemplu:
θ = v1 AND (v2 OR v3)...
Fiecare variabilă logică v este rezultatul returnat de un operator de comparaţie. Se pot compara valorile
a două atribute sau se poate compara valoarea unui atribut cu o constantă.
De exemplu, pentru a selecta din relaţia ANGAJATI toţi angajaţii care lucrează în secţia 1 şi au salarii
mai mari sau egale cu 4000 şi pe cei care lucrează în secţia 2 şi au salarii mai mari sau egale cu 3000, se
foloseşte restricţia prezentată în figura 3.2. Rezultatul prezentat corespunde stării relaţiei ANGAJATI din figura
3.1.
Precedenţa operatorilor logici este cea cunoscută din logica matematică: NOT, AND, OR; această
precedenţă se poate modifica folosind paranteze. În expresia din figura 3.2 nu sunt neapărat necesare
parantezele, dar au fost introduse pentru a evidenţia mai clar condiţiile impuse valorilor atributelor.

σ(IdSectie=1 AND Salariu ≥4000) OR (IdSectie=2 AND Salariu ≥3000)(ANGAJATI)

IdAngajat Nume Prenume DataNasterii Adresa Salariu IdSectie


1 Ionescu Ion 1945.01.05 Bucuresti 4000 1
3 Vasilescu Ana 1966.04.02 Bucuresti 3000 2

Fig. 3.2. Operaţia de restricţie.

O secvenţă de restricţii poate fi aplicată în orice ordine, adică:

4
σcond1(σcond2(R)) = σcond2(σcond1(R))
Mai mult, se poate observa şi demonstra cu uşurinţă că orice secvenţă de restricţii poate fi înlocuită
printr-o singură restricţie în care expresia logică de condiţie se obţine prin conjuncţia (AND) tuturor condiţiilor:
σcond1(σcond2(..σcondn(R)..)) = σcond1 AND cond2...AND condn(R)

Identitatea de mai sus poate fi interpretată şi invers, şi anume că operaţia de restricţie poate fi divizată
(splitată) în operaţii de restricţii succesive cu condiţii care sunt componentele conjunctive (conectate prin
operatorul AND) ale condiţiei de restricţie.
Cardinalitatea (numărul de tupluri) relaţiei rezultat al operaţiei de restricţie este mai mică sau cel mult
egală cu cardinalitatea relaţiei operand. Situaţia de egalitate apare dacă expresia logică de condiţie este evaluată
la valoarea TRUE pentru oricare tuplu al relaţiei operand. De regulă, însă, prin operaţia de restricţie se obţine un
număr de tupluri mai mic decât numărul de tupluri al relaţiei date.
În limbajul SQL restricţia se exprimă printr-o formă particulară a instrucţiunii SELECT, în care lista de
atribute este formată din toate atributele unei singure relaţii, iar clauza WHERE este obligatorie şi introduce
condiţia de restricţie:
SELECT * FROM tabel WHERE conditie [clauze_secundare];
De exemplu, pentru a obţine restricţia din figura 3.2 se introduce comanda:
SELECT * FROM ANGAJATI WHERE IdSectie = 1
AND Salariu >= 4000 OR IdSectie = 2 AND Salariu >=3000;
În termenii folosiţi în limbajul SQL, restricţia selectează o parte din liniile tabelului operand.
Proiecţia (projection) este o operaţie relaţională unară prin care se selectează o submulţime de
atribute ale relaţiei operand.
Notaţia pentru proiecţie este: Πlista_atribute(nume_relatie). Relaţia rezultat a operaţiei de
proiecţie conţine numai atributele din lista de atribute dată ca parametru, care este o submulţime nevidă a
mulţimii atributelor relaţiei operand.
Două exemple de operaţii de proiecţie asupra relaţiei ANGAJATI cu starea din figura 3.1 sunt
prezentate în figura 3.3.
Dacă lista atributelor de proiecţie este o cheie (sau conţine o cheie) a relaţiei operand, atunci relaţia
rezultat are toate tuplurile distincte (fig. 3.3, a). În această situaţie numărul de tupluri ale relaţiei rezultat este
egal cu numărul de tupluri ale relaţiei operand.
Dacă lista de atribute nu este o cheie (sau nu conţine o cheie) a relaţiei operand, atunci este posibil ca
prin proiecţie să se obţină două sau mai multe tupluri identice, dar în relaţia rezultat sunt eliminate tuplurile
duplicat. De exemplu, în proiecţia pe atributele (Nume,Prenume) a relaţiei ANGAJATI din figura 3.3, b
tuplul (Ionescu,Ion) este introdus o singură dată în relaţia rezultat, deşi el este obţinut de două ori prin
operaţia de proiecţie. În acestă situaţie, numărul de tupluri ale relaţiei rezultat este mai mic decât numărul de
tupluri ale relaţiei operand.
Gradul relaţiei rezultat al unei proiecţii (numărul de atribute) este mai mic sau egal cu gradul relaţiei
operand. Numărul de atribute al relaţiei rezultat este egal cu numărul de atribute al relaţiei operand dacă lista de
proiecţie este identică cu lista atributelor relaţiei date.
ΠIdAngajat,Nume,Prenume(ANGAJATI) Π Nume,Prenume(ANGAJATI)
IdAngajat Nume Prenume Nume Prenume
1 Ionescu Ion Ionescu Ion
2 Popescu Petre Popescu Petre
3 Vasilescu Ana Vasilescu Ana
4 Ionescu Ion
a b

Fig. 3.3. Operaţii de proiecţie:


a - lista atributelor de proiecţie conţine o cheie a relaţiei operand;
b - lista atributelor de proiecţie nu conţine o cheie a relaţiei operand.

Fie o succesiune de operaţii de proiecţie:


Πlista1(Πlista2 ...(Πlistak(R))...)

5
O astfel de succesiune de proiecţii este corectă numai dacă lista1 ⊆ lista2...⊆ listak;
bineînţeles, se consideră listele de atribute ca mulţimi. În această situaţie, întreaga succesiune de proiecţii se
poate înlocui cu proiecţia pe lista de atribute cea mai din stânga: Πlista1(R).
Egalitatea de mai sus se poate interpreta şi reciproc: o proiecţie pe o mulţime de atribute (lista1)
poate fi înlocuită cu o succesiune de proiecţii pe mulţimi de atribute care includ lista de atribute dată.
În limbajul SQL, operaţia de proiecţie se obţine tot prin instrucţiunea de interogare SELECT; lista de
coloane introdusă în instrucţiunea SELECT este lista atributelor de proiecţie. Sub forma:
SELECT DISTINCT lista_coloane FROM nume_tabel;

instrucţiunea SELECT reprezintă o operaţie de proiecţie asupra relaţiei nume_tabel pe atributele date în
lista_coloane. De exemplu, proiecţia din figura 3.3, b se scrie poate în SQL astfel:
SELECT DISTINCT Nume,Prenume FROM ANGAJATI;

Dacă lipseşte clauza DISTINCT şi lista de atribute nu este o supercheie a relaţiei, rezultatul operaţiei
poate conţine tupluri duplicat (deci nu este o relaţie în sensul definiţiei din modelul relaţional).
În termenii folosiţi în limbajul SQL, proiecţia realizează o selecţie a coloanelor unui tabel.
Joncţiunea (cuplarea) - (join) este o operaţie binară a algebrei relaţionale prin care se combină
tuplurile a două relaţii într-o singură relaţie.
Joncţiunea se notează cu semnul >< şi este o operaţie foarte importantă în bazele de date relaţionale,
deoarece ea permite realizarea asocierilor între relaţii. În continuare vor fi prezentate două forme ale operaţiei de
joncţiune: θ-joncţiunea şi joncţiunea naturală.
θ-joncţiunea a două relaţii R(A1,A2,...An) şi S(B1,B2,...Bm) este o relaţie QJ(A1,A2,...
An,B1,B2,...Bm) = R ><θ S, în care fiecare tuplu este o combinaţie a două tupluri, unul din relaţia R (cu
atributele A1,A2,....An), iar celălalt din relaţia S (cu atributele B1,B2,...Bm), combinaţie care satisface
B

condiţia de joncţiune θ. Forma generală a condiţiei de joncţiune θ este:


θ = cond1 AND cond2...AND condi...AND condn

unde fiecare condiţie parţială (condi) este o variabilă logică, rezultat al unei operaţii de comparaţie # (unde #
poate fi unul din operatorii: =, ≠, <, ≤, >, ≥ ) asupra valorilor a două atribute Ai (care aparţine relaţiei R) şi Bi
B

(care aparţine relaţiei S), deci:


condi = Ai # Bi
Atributele Ai şi Bi ale căror valori se compară trebuie să fie definite pe domenii compatibile.
B

Tuplurile în care atributele din condiţiile de joncţiune au valori NULL nu sunt luate în consideraţie pentru
calculul relaţiei rezultat.
Se observă asemănarea operaţiei de θ-joncţiune cu produsul cartezian, dat fiind că tuplurile relaţiei
rezultat sunt combinaţii ale tuplurilor relaţiilor operand, cu număr de atribute (gradul relaţiei) egal cu suma
numărului de atribute (gradul) ale celor doi operanzi. Diferenţa esenţială dintre joncţiune şi produsul cartezian
este aceea că în operaţia de joncţiune se combină numai tuplurile care îndeplinesc condiţia de joncţiune θ, pe
câtă vreme în operaţia produs cartezian în relaţia rezultat se includ toate combinaţiile de tupluri din relaţiile
operand. Ca urmare, operaţia de θ-joncţiune poate fi scrisă ca restricţie cu condiţia θ a produsului cartezian al
celor două relaţii: R ><θ S = σθ(R × S). Ca exemplificare, se va calcula joncţiunea:
ANGAJATI ><θ SECTII = σθ(ANGAJATI × SECTII)
cu condiţia: θ =(ANGAJATI.IdSectie = SECTII.IdSectie), asupra relaţiilor ANGAJATI(IdAngajat,
Nume,Prenume,DataNasterii,Adresa,Salariu,IdSectie) şi SECTII (IdSectie,Nume,Buget) cu
valorile date în figura 3.1. Rezultatul acestei operaţii este dat în figura 3.4.
ANGAJATI ><(ANGAJATI.IdSectie = SECTII.IdSectie)SECTII
IdAngajat ANGAJATI. .... ANGAJATI. SECTII. SECTII. Buget
Nume IdSectie IdSectie Nume
1 Ionescu … 1 1 Productie 4000000
2 Popescu … 1 1 Productie 4000000
3 Vasilescu … 2 2 Proiectare 3000000
4 Ionescu … 3 3 Cercetare 2000000

Fig. 3.4. Operaţie de θ-joncţiune între relaţiile ANGAJATI şi SECTII.

6
Cea mai utilizată formă de θ-joncţiune este echijoncţiunea, în care se foloseşte numai operatorul de
comparaţie de egalitate (=). De altfel, exemplul prezentat mai sus este o echijoncţiune. Într-o echijoncţiune vor
exista întotdeauna una sau mai multe perechi de atribute care au valori identice în fiecare din tuplurile relaţiei
rezultat, şi anume perechile de atribute care sunt comparate pentru egalitate. În figura de mai sus, atributele
ANGAJATI.IdSectie şi SECTII.IdSectie au valori identice în toate tuplurile, dat fiind că acestea au fost
comparate pentru egalitate în condiţia de joncţiune.
Este de remarcat faptul că operatorul de comparaţie de egalitate (=) folosit în modelul relaţional este
corespunzător operatorului (= =) din limbajul C (C++) şi este identic cu operatorul de asignare; diferenţierea
dintre cei doi operatori cu acelaşi semn de reprezentare rezultă din context (expresia în care apar).
Joncţiunea naturală. Dat fiind că într-o relaţie nu sunt necesare două atribute cu valori identice, s-a
definit o nouă operaţie de joncţiune, numită joncţiunea naturală (natural join) sau, chiar mai simplu, joncţiune.
Joncţiunea naturală este o echijoncţiune în care fiecare pereche de atribute comparate pentru egalitate (în
condiţia de joncţiune) se înlocuieşte cu unul singur. Se poate spune că joncţiunea naturală este o echijoncţiune
urmată de o proiecţie pe reuniunea atributelor celor două relaţii.
Dat fiind că θ-joncţiunea este o restricţie a produsului cartezian al celor două relaţii operand, rezultă
joncţiunea naturală ca o proiecţie a unei restricţii a produsului cartezian al celor două relaţii.
Dacă se notează relaţiile operand cu R(A1,A2,...An,B1,B2,...Bm) şi S(B1,B2,...Bm,
C1,C2,...Ck), cu atributele comune (B1,B2,...Bm), rezultatul operaţiei de joncţiune naturală este relaţia
QJ cu expresia:
QJ = R >< S = ΠA1,….An,B1,.…Bm,C1,.…Ck σ(R.B1=S.B1… AND R.Bm=S.Bm)(R × S)
Atributele (B1,B2,...Bm) din cele două relaţii comparate pentru egalitate în joncţiunea naturală se
B

numesc atribute comune (sau atribute de joncţiune) şi trebuie să fie definite pe domenii compatibile. Ele se
consideră identice (chiar dacă au denumiri diferite) şi în reuniunea atributelor se introduc o singură dată.
Joncţiunea naturală se reprezintă numai cu semnul ><, fără să mai fie însoţit de condiţia de joncţiune,
înţelegând prin aceasta că joncţiunea are loc pe atributul (sau atributele) comune ale celor două relaţii.
În figura 3.5 este prezentat rezultatul joncţiunii dintre relaţiile ANGAJATI şi SECTII cu starea din
figura 3.1 pe atributul comun IdSectie. Atributul comun (IdSectie) apare o singură dată în relaţia rezultat.
ANGAJATI >< SECTII
IdAngajat ANGAJATI. ... Salariu IdSectie SECTII. Buget
Nume Nume
1 Ionescu … 4000 1 Productie 4000000
2 Popescu … 3500 1 Productie 4000000
3 Vasilescu … 3000 2 Proiectare 3000000
4 Ionescu ... 2500 3 Cercetare 2000000

Fig. 3.5. Joncţiunea naturală a relaţiilor ANGAJATI, SECTII.

Gradul relaţiei rezultat al joncţiunii naturale a celor două relaţii este: q = n + m + k şi este mai mic
decât suma gradelor celor două relaţii (sumă egală cu n + 2*m + k).
Dacă nu există nici o combinaţie de tupluri care să îndeplinească condiţia de joncţiune, rezultatul
operaţiei este o relaţie cu zero tupluri. Dacă nu se impune nici-o condiţie de joncţiune, joncţiunea devine un
produs cartezian al celor două relaţii, cu un număr de tupluri egal cu produsul (NR × NS) al numărului de tupluri
NR şi respectiv NS, ale celor două relaţii. În cazul general, numărul de tupluri ale relaţiei rezultat al operaţiei de
joncţiune este cuprins între 0 şi (NR × NS).
Operaţia de joncţiune naturală este conceptual comutativă (adică R >< S = S >< R), dacă se consideră că
atributele unei relaţii nu sunt ordonate. Dacă se consideră schema relaţiei rezultat ca listă a atributelor sale,
atunci, prin convenţie, atributele primei relaţii operand sunt primele în lista de atribute a relaţiei rezultat, iar
atributele celei de-a doua relaţii, mai puţin atributul (sau atributele) de joncţiune, urmează în lista atributelor
relaţiei rezultat.
Operaţia de joncţiune naturală nu este, în general, asociativă. Fie mulţimile de atribute disjuncte A, B,
C, D şi relaţiile cu schemele: R(A,B), S(B,C) şi T(A,D). În expresia (R >< S)>< T se efectuează mai
întâi joncţiunea R >< S pe atributul comun B ale celor două relaţii, rezultând o relaţie cu schema
Q(A,B,C), după care se efectuează joncţiunea Q >< T pe atributul comun A.

7
Asocierea de la dreapta la stânga a relaţiilor date (expresia R ><(S >< T)) nu este posibilă, deoarece
joncţiunea (S >< T) nu se poate evalua, dat fiind că relaţiile S(B,C) şi T(A,D) nu au nici-un atribut comun.
Se poate remarca uşor că există şi situaţii în care joncţiunea naturală este asociativă, şi anume când
fiecare pereche de relaţii din expresia dată au atribute comune.
Operaţia de joncţiune naturală este utilizată pentru a combina date din două relaţii, astfel încât
informaţia rezultată să fie cuprinsă într-o singură relaţie. În cazul cel mai frecvent, joncţiunea naturală se
calculează între o relaţie care referă şi relaţia referită, atributul de joncţiune fiind cheia străină (în relaţia care
referă), respectiv cheia primară (sau candidată) în relaţia referită. Rezultatul obţinut reflectă asocierea dintre cele
două relaţii. De exemplu, joncţiunea naturală din figura 3.5 între relaţiile ANGAJATI şi SECTII reflectă
asocierea N:1 între acestea. Din acest exemplu se poate remarca faptul că prin operaţia de joncţiune se obţin
informaţii combinate din cele două relaţii operand. Pentru fiecare tuplu din relaţia care referă (în exemplul de
mai sus, relaţia ANGAJATI) se obţin toate informaţiile din tuplul referit (în exemplul de mai sus, relaţia
SECTII), adică acel tuplu care are valoarea cheii primare egală cu valoarea cheii străine care o referă. În
exemplul de mai sus, prima linie a tabelului rezultat conţine toate informaţiile (nume secţie, buget) despre secţia
în care lucrează angajatul respectiv (secţia 1), etc.
Forţa modelului relaţional constă în posibilitatea de a combina informaţiile din două sau mai multe
relaţii pentru a obţine rezultatul unei interogări, combinare care se poate face printr-una sau mai multe operaţii
de joncţiune. Această posibilitate de combinare a informaţiilor este denumită de unii autori ca o „navigare” prin
baza de date.
În limbajul SQL, θ-joncţiunea se poate exprima direct cu o instrucţiune SELECT pe două sau mai
multe tabele, condiţia de joncţiune θ fiind introdusă prin clauza WHERE. De exemplu, θ-joncţiunea din figura 3.4
se poate obţine prin instrucţiunea:
SELECT * FROM ANGAJATI,SECTII WHERE ANGAJATI.IdSectie = SECTII.IdAngajat;
O joncţiune naturală se poate exprima în limbajul SQL numai în mod explicit, adică trebuie ca lista de
atribute a instrucţiunii SELECT să conţină numai atributele diferite din cele două relaţii (fiecare atribut de
joncţiune se introduce o singură dată), iar în clauza WHERE trebuie introdusă condiţia de egalitate a atributelor
corespondente. De exemplu, joncţiunea naturală ANGAJATI >< SECTII din figura 3.5 se obţine prin
instrucţiunea SQL:
SELECT IdAngajat,ANGAJATI.Nume,Prenume,DataNasterii,
Adresa,Salariu,SECTII.IdSectie,SECTII.Nume,
Buget,IdAngajat FROM ANGAJATI,SECTII
WHERE ANGAJATI.IdSectie = SECTII.IdSectie;
Diviziunea (division) este o operaţie binară a algebrei relaţionale prin care se obţine o relaţie care
conţine atributele diferenţei mulţimilor de atribute ale relaţiilor operand.
Fie două mulţimi de atribute: A = {A1,A2,..An} şi B = {B1,B2,..Bm} şi două relaţii R(A,B)
şi S(B) astfel încât mulţimea atributelor relaţiei S să fie o submulţime a mulţimii atributelor relaţiei R. Relaţia
QD obţinută prin operaţia de diviziune are ca atribute toate atributele diferenţei celor două mulţimi de atribute
(adică acele atribute care aparţin relaţiei R şi nu aparţin relaţiei S) şi conţine acele tupluri t[A] care au
proprietatea că pentru orice tuplu s din S există un tuplu t în R care are atributul B egal cu tuplul s. Se poate
scrie:
QD(A) = R ÷ S = ΠA σR.B = S.B(R)
În limbajul SQL, diviziunea se exprimă printr-o instrucţiune SELECT, introducând explicit lista
atributelor de proiecţie şi condiţia de egalitate a atributelor corespondente din cele două relaţii prin clauza
WHERE.
Algebra relaţională este o colecţie de operaţii asupra relaţiilor. Cele opt operaţii propuse de E.F.Codd
(reuniunea, intersecţia, diferenţa, produsul cartezian, restricţia, proiecţia, joncţiunea, diviziunea), la care se
adaugă operaţia de redenumire a atributelor, nu constituie o mulţime minimă de operaţii ale algebrei relaţionale,
deoarece o parte din operaţii se pot exprima prin intermediul altora. Aşa cum s-a prezentat mai sus, joncţiunea
este o proiecţie a unei restricţii a produsului cartezian al celor două relaţii, iar diviziunea este o proiecţie a unei
restricţii asupra relaţiei deîmpărţit. La fel, intersecţia se poate exprima printr-o expresie construită pe baza
operaţiei de diferenţă: R ∩ S = R – (R – S).
Cinci operaţii (reuniunea, diferenţa, produsul cartezian, restricţia, proiecţia) sunt operaţii primitive şi
constituie mulţimea minimă de operaţii ale algebrei relaţionale. Pe baza lor se poate construi orice expresie a
algebrei relaţionale. Dar şi celelalte trei operaţii (şi în special joncţiunea) sunt operaţii deosebit de utile în
formularea interogărilor, astfel încât algebra relaţională a păstrat toate cele opt operaţii propuse de E.F.Codd, la
care s-a adăugat operaţia de redenumire a atributelor.

8
3.1.3 FORMULAREA INTEROGĂRILOR
Interogările exprimate în limbaj natural se pot formula într-unul din limbajele abstracte de interogare,
algebra relaţională sau calculul relaţional, după care se poate găsi comanda corespunzătoare în limbajul de
interogare implementat de sistemul SGBD în care va fi realizată baza de date (cum este limbajul SQL).
Pentru utilizator, o interogare este o metodă de a regăsi anumite informaţii dintr-o bază de date, prin
intermediul unei aplicaţii de baze de date. Din punctul de vedere al programatorului aplicaţiei de baze de date,
interogarea se exprimă printr-o comandă echivalentă expresiei de interogare, comandă care se transmite
sistemului SGBD.
Din punct de vedere al sistemului de gestiune, o interogare este un program (de exemplu, în limbajul
SQL) pe care îl compilează şi apoi îl execută. Ca orice program, o interogare este prelucrată de către SGBD în
mai multe faze: analiza lexicală, analiza sintactică şi analiza semantică, pentru validarea interogării, urmate de
generarea codului. De asemenea, dacă există mai multe soluţii pentru aceeaşi interogare, sistemul de gestiune
selectează soluţia optimă. Conceptual, subsistemul SGBD de prelucrare a interogărilor constă din următoarele
componente:
• Compilatorul de interogări, care efectuează analiza lexicală şi sintactică a interogării; acesta
validează din punct de vedere sintactic interogarea, adică verifică existenţa relaţiilor, a vederilor, a
indexurilor şi a atributelor implicate în interogare şi utilizarea corectă a acestora.
• Optimizatorul de interogări, care efectuează analiza semantică a interogării şi selectează alternativa
optimă dintre mai multe soluţii posibile de execuţie a interogării.
• Generatorul de cod, care generează programul de execuţie al interogării, conform optimizărilor
efectuate.
• Componenta de execuţie (runtime), care execută programul interogării.
Compilarea interogării se realizează la fel ca orice compilare a programelor, fără aspecte specifice
sistemelor de baze de date. Optimizarea interogărilor este o operaţie specifică sistemelor de gestiune şi utilizează
proprietăţile operaţiilor relaţionale pentru a obţine performanţe de execuţie a interogărilor cât mai bune.
Optimizarea este efectuată de către SGBD, transparent, fără intervenţia programatorului.
În algebra relaţională o interogare se formulează printr-o expresie constând dintr-o secvenţă de
identificatori (nume de relaţii, nume de atribute), constante şi operatori. Pentru exprimarea unei interogări printr-
o expresie de algebră relaţională, trebuie să fie precizate următoarele elemente:
• Lista atributelor relaţiei rezultat, care se numesc atribute de proiecţie.
• Lista relaţiilor din care se extrag informaţiile.
• Condiţia pe care trebuie să o îndeplinească tuplurile relaţiei rezultat.
În funcţie de aceste elemente, se pot studia două situaţii de rezolvare a interogarilor: interogări care se
rezolvă în cadrul unei singure relaţii şi interogări care se rezolvă folosind două sau mai multe relaţii ale bazei de
date.
Interogări într-o singură relaţie. Dacă toate atributele care intervin în interogare (atributele de
proiecţie şi atributele din condiţie) sunt atribute ale unei singure relaţii R, atunci interogarea se poate rezolva la
nivelul acelei relaţii, ca o proiecţie (pe atributele relaţiei rezultat) a restricţiei cu condiţia impusă asupra relaţiei
date, prin expresia:
Q = Πlista_atribute σconditie(R)
Exemplul 3.1. Fie relaţia ANGAJATI definită în figura 3.1 şi interogarea: „Care sunt numele şi
prenumele angajaţilor care au un salariu mai mare sau egal cu 3000?”.
Se observă că această interogare poate fi rezolvată la nivelul unei singure relaţii, relaţia ANGAJATI.
Expresia de algebră relaţională care exprimă interogarea dată este:
Q1 = ΠNume,Prenume σSalariu ≥3000 (ANGAJATI)
Instrucţiunea SQL care realizează această interogare este:
SELECT Nume,Prenume FROM ANGAJATI WHERE Salariu >= 3000;
Rezultatul interogării este următorul:

Nume Prenume
Ionescu Ion
Popescu Petre
Vasilescu Ana

9
Exemplul 3.2. Fie relaţia ANGAJATI definită în figura 3.1 şi interogarea: „Care sunt numele,
prenumele şi salariul angajaţilor care lucrează în secţia cu numărul 1?”
Analizând această interogare se constată că toate atributele de proiectie (nume, prenume, data nasterii şi
salariul unui angajat) şi atributul din condiţia de interogare (numărul sectiei) sunt atribute ale relaţiei
ANGAJATI, deci interogarea poate fi rezolvată la nivelul acestei relaţii.
Expresia de algebră relaţională care exprimă interogarea dată este:
Q2 = ΠNume,Prenume,Salariu σIdSectie = 1(ANGAJATI)
Comanda SQL care realizează această interogare este:
SELECT Nume,Prenume,Salariu FROM ANGAJATI WHERE IdSectie = 1;
Rezultatul interogării este:

Nume Prenume Salariu


Ionescu Ion 4000
Popescu Petre 3500

Interogări în două sau mai multe relaţii. În situaţia în care atributele de proiecţie şi atributele din
condiţia de interogare nu aparţin unei singure relaţii, pentru rezolvarea interogării trebuie să fie folosite toate
acele relaţiile care, împreună, conţin aceste atribute.
Conceptual, o astfel de interogare se rezolvă construind mai întâi o relaţie care să conţină toate
atributele necesare prin combinarea a două sau mai multe relaţii folosind operaţii de produs cartezian sau
joncţiuni, iar rezultatul interogării se obţine prin restricţia (cu condiţia de interogare) şi proiecţia (pe atributele de
proiecţie) a acestei relaţii.
Cazul cel mai frecvent de interogare necesită joncţiunea naturală a două sau mai multe relaţii asociate,
folosind perechea de atribute cheia străină - cheia primară referită pentru fiecare operaţie de joncţiune:
Q = Πlista_atribute σconditie(R >< S >< T...)
Exemplul 3.3. Fie relaţiile ANGAJATI, SECTII definite în figura 3.1 şi interogarea „Care sunt
numele, prenumele şi salariul angajaţilor care lucrează în secţia cu numele Producţie ?”.
Atributele de proiecţie (Nume,Prenume,Salariu) sunt atribute ale relaţiei ANGAJATI; atributul
Nume al unei secţii (care apare în condiţia de interogare) nu se află în aceeaşi relaţie, ci în relaţia SECTII de
aceea, pentru a rezolva această interogare, este necesară combinarea celor două relaţii (fig. 3.6).
ANGAJATI
IdAngajat Nume Prenume DataNasterii Adresa Salariu IdSectie

SECTII
Buget Nume IdSectie

Fig. 3.6. Exprimarea unei interogari pe două relaţii.

Combinarea celor două relaţii se efectuează prin joncţiunea naturală (pe atributul comun IdSectie) a
celor două relaţii. Relaţia rezultat al joncţiunii conţine toate informaţiile necesare interogării: numele, prenumele
şi salariul angajaţilor şi numele secţiei corespunzător numărului secţiei (IdSectie) în care lucrează fiecare
angajat. După aceasta se face restricţia (cu condiţia SECTII.Nume= ‘Productie’), urmată de proiecţia pe
atributele de proiecţie. Expresia finală de algebră relaţională care exprimă interogarea dată este:
Q3 = ΠANGAJATI.Nume,Prenume,Salariu
σ SECTII.Nume='Productie'(ANGAJATI >< SECTII)
Comanda SQL care realizează această interogare este:
SELECT ANGAJATI.Nume,Prenume,Salariu FROM ANGAJATI,SECTII
WHERE SECTII.IdSectie = ANGAJATI.IdSectie
AND SECTII.Nume = ‘Productie’;
Aşa cum s-a mai precizat, în limbajul SQL trebuie să fie introdusă explicit condiţia de joncţiune
naturală (SECTII.IdSectie = ANGAJATI.IdSectie), împreună cu celelalte condiţii de interogare
(SECTII.Nume =‘Productie’).
Rezultatul acestei interogări asupra relaţiilor cu starea din figura 3.1 este:

10
Nume Prenume Salariu
Ionescu Ion 4000
Popescu Petre 3500

Exemplul 3.4. Fie relaţiile FURNIZORI, ACHIZIŢII, COMPONENTE (fig. 3.7), uşor modificate faţă
de cele prezentate în capitolul precedent.
FURNIZORI
IdFurnizor Nume Prenume Adresa
1 Marculescu Mihai Bucuresti
2 Mircescu Vasile Bucuresti
3 Amzulescu Ion Craiova
ACHIZITII
IdComponenta IdFurnizor Cantitate PretUnitar
1 1 100 110
1 2 200 100
2 2 300 200
3 3 300 150
COMPONENTE
IdComponenta Denumire Culoare Greutate
1 Rezistenta Rosu 1
2 Condensator Alb 2
3 Ferita Negru 4

Fig. 3.7. Relaţiile FURNIZORI, ACHIZITII,COMPONENTE.

Interogarea "Care sunt numele şi prenumele furnizorilor care au livrat componente în cantităţi mai mari
sau egale cu 200 ?" necesită joncţiunea relaţiilor FURNIZORI şi ACHIZITII care, împreună, conţin atributele
ce intervin în interogare. Expresia de algebră relaţională care realizează această interogare este:
Q4 = Π Nume,Prenume σCantitate ≥ 200 (FURNIZORI >< ACHIZITII)
În SQL, această interogare se exprimă astfel:
SELECT Nume,Prenume FROM FURNIZORI,ACHIZITII
WHERE FURNIZORI.IdFurnizor = ACHIZITII.IdFurnizor AND Cantitate >= 200;
Rezultatul interogării este:

Nume Prenume
Mircescu Vasile
Amzulescu Ion

Exemplul 3.5. Pentru aceleaşi relaţii din figura 3.7, se consideră interogarea: "Care sunt numele,
prenumele şi adresa furnizorilor care au livrat componenta cu denumirea Rezistenta? ".
Atributele de proiecţie (Nume, Prenume, Adresa) aparţin relaţiei FURNIZORI, iar atributul
Denumire aparţine relaţiei COMPONENTE. Asocierea dintre aceste relaţii este realizată prin relaţia
ACHIZITII, astfel încât această interogare necesită joncţiunea tuturor celor trei relaţii. (fig. 3. 8).
FURNIZORI
IdFurnizor Nume
Nume Prenume
Prenume Adresa
Adresa

ACHIZITII
IdComponenta IdFurnizor Cantitate PretUnitar

COMPONENTE
IdComponenta Denumire
Denumire Culoare Greutate

Fig. 3.8. Interogare pe mai multe relaţii asociate.

11
Pentru realizarea interogării date se vor executa următoarele operaţii:
R1 = ACHIZITII >< COMPONENTE
R2 = FURNIZORI >< R1 = FURNIZORI>< (ACHIZITII >< COMPONENTE)
R3 = σ Denumire ='Rezistenta' (R2)
Q5 = Π Nume,Prenume,Adresa (R3)=
ΠNume,Prenume,Adresa σ Denumire ='Rezistenta'
(FURNIZORI >< (ACHIZITII >< COMPONENTE))
În limbajul SQL, interogarea de mai sus se exprimă prin instrucţiunea:
SELECT Nume, Prenume, Adresa
FROM FURNIZORI,ACHIZITII,COMPONENTE
WHERE FURNIZORI.IdFurnizor = ACHIZITII.IdFurnizor
AND COMPONENTE.IdComponenta = ACHIZITII.IdComponenta
AND Denumire = ‘Rezistenta’;
Rezultatul acestei interogări pentru starea relaţiilor din figura 3.7 este:

Nume Prenume Adresa


Marculescu Mihai Bucuresti
Mircescu Vasile Bucuresti

Se observă că în comanda SQL nu se evidenţiază care sunt operaţiile de joncţiune şi ordinea lor de
execuţie, iar condiţiile de restricţie şi joncţiune sunt cuprinse într-o singură expresie (în clauza WHERE). Sistemul
SGBD este acela care determină modul optim de realizare a operaţiilor conţinute în blocul de interogare.

12
4. DEZVOLTAREA SISTEMELOR DE BAZE DE DATE

În acest capitol se vor prezenta etapele de dezvoltare a bazelor de date şi a aplicaţiilor de baze de date,
activităţi care se desfăşoară în strânsă corelare. Pentru baze de date de dimensiuni mici, care sunt folosite de un
singur utilizator sau de un număr redus de utilizatori, proiectarea este destul de simplă. Însă, pentru baze de date
de dimensiuni medii sau mari, care fac parte din sistemul informatic al unei organizaţii extinse, proiectarea este
mult mai complicată. Astfel de baze de date, care trebuie să satisfacă cerinţele a numeroase aplicaţii şi utilizatori
trebuie să fie proiectate cu multă grijă şi testate cât mai riguros.
Sistemul informatic (information system) include toate resursele unei organizaţii care sunt implicate în
colectarea, administrarea, utilizarea şi diseminarea informaţiilor. Sistemele informatice ale anilor 1960 erau
dominate de sisteme de fişiere (pe disc sau bandă magnetică). După anul 1970, numeroase organizaţii au trecut
treptat la sisteme informatice care folosesc sisteme de baze de date, deoarece acestea permit gestionarea unor
volume de date mai mari într-un timp mai redus, cu multiple posibilităţi de securitate şi protecţie a datelor.

4.1 PROIECTAREA BAZELOR DE DATE

Dezvoltarea sistemelor de baze de date comportă mai multe etape, care pot fi prezentate succint astfel:
1. Analiza şi definirea sistemului: definirea scopului sistemului de baze de date, a utilizatorilor şi a
aplicaţiilor acestuia.
2. Proiectarea sistemului: în această etapă se realizează proiectul logic şi proiectul fizic al sistemului,
pentru un anumit SGBD ales.
3. Implementarea: este etapa în care se scriu definiţiile obiectelor bazei de date (tabele, vederi, etc.)
şi se implementează aplicaţiile software.
4. Testarea şi validarea: noul sistem de baze de date este testat şi validat cât mai riguros posibil.
În general, se consideră că etapa de proiectare a unei baze de date se pot diviza, la rândul ei, în mai multe faze :
1. Proiectarea conceptuală a bazei de date.
2. Alegerea unui SGBD.
3. Proiectarea logică a bazei de date.
4. Proiectarea fizică a bazei de date.
În mod tipic, dezvoltarea unei baze de date constă din desfăşurarea a două categorii de activităţi
paralele, aşa cum se poate vedea în figura de mai jos.

Faza 1: Colectarea Cerinţele Cerinţele


şi analiza cerinţelor de date de prelucrare

Faza 2: Proiectare Proiectarea schemei conceptuale şi Proiectarea tranzacţiilor


conceptuală a schemelor externe (independente (independente de SGBD)
de SGBD)

Faza 3: Alegerea unui


SGBD

Faza 4: Proiectare Proiectarea schemei conceptuale şi


logică a schemelor externe
(dependente de SGBD)

Faza 5: Proiectare Proiectarea


fizică schemei interne
(dependentă de SGBD)

Faza 6: Implementare Instrucţiuni de descriere a Implementarea tranzacţiilor


datelor (LDD) (dependente de SGBD)
(dependente de SGBD)
Prima categorie de activităţi se referă la proiectarea structurii şi a conţinutului bazei de date, iar cea de-
a doua categorie se referă la proiectarea modului de prelucrare a datelor. Aceste două categorii de activităţi sunt
strâns corelate: toate prelucrările care se efectuează în tranzacţii depind de structura datelor memorate, iar
proiectarea fizică a bazei de date depinde de prelucrările necesare în tranzacţii.
Cele şase faze de proiectare şi implementare enumerate mai sus nu se desfăşoară strict într-o singură
secvenţă. În multe cazuri este necesară modificarea proiectului dintr-o fază iniţială într-una din fazele ulterioare,
pentru a se obţine rezultatele dorite. Aceste bucle de reacţie între faze (sau în interiorul unei faze) sunt, în general
frecvente în cursul proiectării unei baze de date, dar nu au mai fost reprezentate în figura 7.1, pentru a nu
complica diagrama respectivă.

4.1.1 COLECTAREA ŞI ANALIZA CERINŢELOR


Înainte de a se proiecta efectiv o bază de date, este necesar să se cunoască ce rezultate se aşteaptă
utilizatorii potenţiali să obţină de la baza de date respectivă şi ce informaţii primare sunt disponibile pentru
aceasta. De asemenea, este necesar să se cunoască ce aplicaţii se vor efectua (aplicaţii de gestiune a stocurilor,
aplicaţii contabile, aplicaţii de urmărire a consumurilor, aplicaţii de salarizare, etc.).
Toate aceste activităţi oferă informaţii slab structurate, în general în limbaj natural, pe baza cărora se
pot construi diagrame, tabele, grafice, etc., manual sau folosind diferite instrumente software de proiectare.
Această fază este puternic consumatoare de timp, dar este crucială pentru succesul sistemului informatic.

4.1.2 PROIECTAREA CONCEPTUALĂ A BAZELOR DE DATE


Faza de proiectare conceptuală a bazelor de date implică două activităţi paralele: proiectarea schemei
conceptuale şi a schemelor externe ale bazei de date şi proiectarea tranzacţiilor.

4.1.2.1 Proiectarea schemei conceptuale de nivel înalt


Deşi nu este obligatoriu, această fază se poate menţine independentă de SGBD şi produce un model de
date de nivel înalt, care va fi implementat după transpunerea lui într-un model de date specific. Chiar dacă
proiectanţii pot porni direct cu scheme conceptuale specifice unui anumit SGBD (care se mai numesc şi scheme
logice), este totuşi recomandabil să se realizeze mai întâi schema conceptuală de nivel înalt independentă de
SGBD, deoarece o astfel de schema conceptuală este o descriere stabilă şi inavuabilă a bazei de date, iar alegerea
unui SGBD şi deciziile ulterioare de proiectare se pot schimba fără ca aceasta să se schimbe.
Pentru proiectarea schemei conceptuale se identifică elementele esenţiale ale acesteia: tipurile de
entităţi şi atributele lor precum şi asocierile dintre aceste tipuri. Se pot defini, dacă este necesar, subtipuri, prin
specializări ale tipurilor de bază, sau supertipuri, prin generalizări ale tipurilor deja definite. Acest proiect
conceptual de nivel înalt este realizat pe baza cerinţelor definite în prima etapă de proiectare şi se reprezintă, în
general printr-o diagramă Entitate-Asociere (extinsă).
Există mai multe aspecte privind modul de abordare a proiectării conceptuale. Un prim aspect se referă
la modul de proiectare a schemei conceptuale a bazei de date: proiectare prin integrarea cerinţelor şi proiectare
prin integrarea schemelor externe.
În proiectarea prin integrarea cerinţelor (care se mai numeşte şi proiectare centralizată) se realizează
mai întâi integrarea (combinarea) tuturor cerinţelor de proiectare într-un singur set de cerinţe, pe baza căruia se
proiectează schema conceptuală a bazei de date. Din această schema conceptuală (unică) proiectată se deduc
schemele externe (vederile) corespunzătoare diferitelor grupuri de utilizatori şi aplicaţii.
În proiectarea prin integrarea vederilor (schemelor) externe se proiectează câte o schemă
corespunzătoare fiecărui grup de utilizatori şi aplicaţii, pe baza cerinţelor acestora, după care se combină aceste
scheme într-o singură schemă conceptuală globală, pentru întreaga bază de date.
Fiind dat un set de cerinţe, pentru un singur utilizator sau pentru mai mulţi utilizatori ai bazei de date,
proiectarea schemei conceptuale care să satisfacă aceste cerinţe se poate aborda într-o varietate de strategii,
dintre care cele mai relevante sunt proiectarea ascendentă (bottom-up) şi proiectarea descendentă (top-down).
În proiectare ascendentă a bazelor de date se porneşte de la o schemă conceptuală universală care
conţine toate atributele, care sunt apoi grupate pentru a forma tipuri de entităţi şi asocierile dintre acestea.
Proiectul poate fi rafinat prin grupări ulterioare şi creare de supertipuri şi subtipuri ale tipurilor existente.
În proiectarea descendentă a bazelor de date se porneşte de la o schemă conceptuală dezvoltată în
modelul Entitate-Asociere (extins) în care sunt cuprinse aspectele de bază (tipuri de entităţi şi asocieri ale
acestora). Dezvoltarea şi rafinarea acestui model se face prin adăugarea de noi atribute şi constrângeri, crearea
unor subtipuri (prin specializare) şi asocierea acestora cu tipurile de bază.
Aşadar, în această fază de proiectare se obţine schema conceptuală de nivel înalt a bazei de date, care
este independentă de orice model de date specific (ierarhic, reţea, relaţional, etc.), şi de orice sistem de gestiune
care poate fi folosit pentru realizarea bazei de date.

2
EXEMPLU DE PROIECTARE A SCHEMEI CONCEPTUALE DE NIVEL
ÎNALT A UNEI BAZE DE DATE
Entităţi. Tipurile de entităţi puternice (normale) care se pot defini pentru modelarea activitătii unei
întreprinderi sunt cele descrise anterior (SECTII, ANGAJATI, PROIECTE), la care se mai pot adauga alte
câteva tipuri, ca de exemplu: PRODUSE,COMPONENTE,FURNIZORI,CLIENTI, cu semnificaţie evidentă.:
SECTII(Nume,Buget)
ANGAJATI(Nume,Prenume,DataNasterii,Adresa,Functie,Salariu)
PROIECTE(Denumire,Termen,Buget)
PRODUSE(Denumire,Descriere)
COMPONENTE(Denumire,Descriere)
FURNIZORI(Nume,Prenume,Adresa)
CLIENTI(Nume,Prenume,Adresa)
La aceste mulţimi de entităţi se adaugă mulţimea de entităţi slabe:
DEPENDENTI(Nume,Prenume,DataNasterii,GradRudenie)
Pentru tipul ANGAJATI se defineşte o specializare disjunctă parţială, cu subtipurile INGINERI şi
SECRETARE. Aceste subtipuri se află în asociere 1:1 cu tipul de bază ANGAJATI, moştenesc atributele acestuia
şi fiecare mai conţine atribute specifice:
INGINERI(Specialitatea)
SECRETARE(VitezaRedactare)
Asocieri. Asocierile dintre mulţimile de entităţi se stabilesc în funcţie de modul în care se desfăşoară
activitatea modelată. De exemplu, dacă în întreprinderea respectivă activitatea este organizată pe secţii şi fiecare
angajat lucrează într-una (şi numai una) dintre secţii, atunci între mulţimile de entităţi SECTII-ANGAJATI
există o asociere 1:N.
Asocierea ANGAJATI-PROIECTE este o asociere M:N dacă se consideră că la un proiect lucrează mai
mulţi angajaţi şi fiecare angajat poate lucra la mai multe proiecte.
Un produs este compus din mai multe componente şi fiecare componentă poate fi inclusă în mai multe
produse, deci asocierea COMPONENTE-PRODUSE este este M:N.
O componentă poate fi achiziţionată de la mai mulţi furniori, iar un furnizor poate oferi mai multe
componente, deci asocierea FURNIZORI-COMPONENTE este M:N. Pentru această asociere se stabileşte că
trebuie să conţină şi o referinţă la angajatul care se ocupă de acea achiziţie şi atunci se poate considera că
mulţimile de entităţi FURNIZORI-COMPONENTE-ANGAJATI se află toate într-o asociere ternară, M:N:P.
La fel, mulţimile de entităţi PRODUSE-CLIENTI-ANGAJATI se află toate într-o asociere M:N:P.

SECTII PROIECTE COMPONENTE

1 M COMPOZITII M
ACTIVITATI
N
N N M
ACHIZITII
PRODUSE
ANGAJATI P
N M
1 VANZARI

N N

DEPENDENTI INGINERI SECRETARE FURNIZORI CLIENTI

Diagrama E-A a bazei de date a unei întreprinderi.

O mulţime de entităţi slabe se află, de regulă, în asociere N:1 cu mulţimea de entităţi puternice de care
depinde. In exemplul dat, între mulţimea DEPENDENTI şi mulţimea de entităţi ANGAJATI există o asociere N:1.
O mulţime de entităţi de un subtip dat este, de regulă, în asociere 1:1 cu mulţimea de entităţi de
supertipul acesteia. Pentru exemplul de mai sus, un angajat poate fi un (singur) inginer; iar un inginer este chiar
un angajat, deci asocierea între mulţimile de entităţi ANGAJATI şi INGINERI există o asociere cu raportul de
cardinalitate 1:1. La fel, între mulţimile de entităţi ANGAJATI şi SECRETARE există o asociere 1:1.

3
Schema conceptuală a unei baze de date relaţionale poate fi dezvoltată independent de un anumit
SGBD, urmând ca ulterior să se adauge diferite trăsături specifice SGBD-ului folosit. De obicei însă, fiecare
SGBD pune la dispoziţie un număr de instrumente mai mult sau mai puţin „prietenoase” de proiectare a relaţiilor
şi a asocierilor, astfel încât în mod frecvent, proiectul conceptual se dezvoltă de la început în cadrul sistemului
SGBD în care se va realiza baza de date. De exemplu, Microsoft Access oferă o interfaţă vizuală de proiectare a
relaţiilor şi a asocierilor deosebit de uşor de utilizat. La fel, sistemele SQL Server sau Oracle9i oferă instrumente
grafice pentru proiectarea vizuală a relaţiilor.

4.1.2.2 Proiectarea tranzacţiilor


Atunci când se proiectează o bază de date, se cunosc în mare parte şi ce tipuri de aplicaţii se vor
executa. Un aspect important în proiectarea bazelor de date este specificarea caracteristicilor funcţionale ale
acestor aplicaţii cât mai devreme în cursul procesului de proiectare, astfel încât schema bazei de date să includă
toate informaţiile necesare cu privire la aceste aplicaţii. În plus, cunoaşterea importanţei relative a diferitelor
tranzacţii executate de aplicaţiile bazei de date, precum şi a frecvenţei de invocare a acestora, permite luarea
unor decizii în etapa de proiectare fizică a bazei de date.
După implementarea sistemului de baze de date vor mai fi identificate şi implementate noi tranzacţii.
Totuşi, cele mai importante tranzacţii sunt cel mai adesea cunoscute înainte de implementarea sistemului.

4.1.3 ALEGEREA UNUI SGBD


Alegerea unui SGBD pentru implementarea unei baze de date se face ţinând seama de mai mulţi factori:
tehnici, economici şi administrativi. Factorii tehnici trebuie să asigure alegerea unui SGBD adecvat sarcinii de
realizare a sistemului informatic al întreprinderii. Se iau în consideraţie:
• Modelul de date (ierarhic, reţea, relaţional, obiect-orientat, obiect-relaţional) care se potriveşte cel
mai bine cu structura informaţiilor stocate şi prelucrate.
• Capacitatea de stocare a datelor.
• Posibilităţile de control al concurenţei şi de refacere a datelor.
• Limbajele şi interfeţele de programare disponibile.
De asemenea, înainte de achiziţionarea unui anumit SGBD trebuie să se cunoască configuraţia
hardware-software necesară, precum şi alte cerinţe de dezvoltare a programelor de aplicaţii (compilatoare,
drivere, etc.). Factorii economici şi administrativi care trebuie să fie analizaţi la alegerea unui SGBD sunt:
• Costul de achiziţie a software-ului, care include costul de bază, la care se adaugă diferite opţiuni
(opţiuni de salvare-refacere, documentaţie, limbaje şi interfeţe de programare, drivere, etc.).
• Costul de întreţinere, pentru a obţine serviciul furnizorului de menţinere la zi a versiunii SGBD.
• Costul de pregătire a personalului se referă la cursurile care se organizează pentru persoanele care
se ocupă cu întreţinerea şi operarea sistemului.
• Cunoştinţele de programare a personalului într-un anumit SGBD; dacă majoritatea personalului din
organizaţia respectivă este foarte bun cunoscător al unui anumit SGBD, atunci acesta poate fi
favorizat, deoarece se pot reduce astfel costurile de pregătire.
Beneficiile achiziţionării unui anumit SGBD nu sunt uşor de apreciat sau de măsurat, dar analiza
raportului cost-performanţe poate da o imagine a rezultatelor obţinute.

4.1.4 PROIECTAREA LOGICĂ A BAZELOR DE DATE


În faza de proiectare logică a unei baze de date se realizează schema conceptuală globală şi schemele
conceptuale (vederile) externe pentru sistemul SGBD ales, pornind de la schema conceptuală şi schemele externe
de nivel înalt independente de SGBD, proiectate în faza precedentă. Această fază de proiectare logică poate fi
realizată în două subfaze:
• Transpunerea schemei conceptuale în modelul de date al sistemului SGBD ales, dar independent de
sistemul de gestiune propriu-zis. În cazul modelului relaţional, transpunerea se face prin
corespondenţa dintre elementele schemei conceptuale de nivel înalt reprezentată prin diagrama
Entitate-Asociere (tipuri de entităţi, atribute, asocieri) şi elementele modelului relaţional (relaţii,
atribute, referinţe). Se obţine un proiect logic dependent de modelul de date, dar independent de
sistem. În această subfază se face şi analiza normalizării relaţiilor, normalizarea fiecărei relaţii până
la nivelul adecvat şi documentarea tuturor dependenţelor de date care nu sunt determinate de chei
ale relaţiilor (necesară pentru proiectarea procedurilor de verificare şi impunere a dependenţelor
respective).

4
• Rafinarea schemei conceptuale şi a schemelor externe obţinute anterior, astfel încât să se utilizeze
unele (sau cât mai multe) din facilităţile oferite de sistemul SGBD ales (modul de generare a
cheilor primare, definirea constrângerilor, etc.).
Rezultatul acestei faze de proiectare îl constituie, aşadar, schema conceptuală şi schemele externe ale
bazei de date, dependente de sistemul SGBD ales şi de modelul de date al acestuia.
Pentru transpunerea modelului Entitate-Asociere (reprezentat prin diagrama E-A) în model relaţional se
parcurg în principal două etape, rezultatul fiind schema conceptuală a bazei de date relaţionale:
• Proiectarea relaţiilor corespunzătoare mulţimilor de entităţi din diagrama E-A.
• Proiectarea asocierilor; asocierile se pot reprezenta prin chei străine sau prin relaţii de asociere;
acestea sunt relaţii suplimentare care se adaugă în schema conceptuală a bazei de date relaţionale
pentru a reprezenta, în general, asocierile M:N; uneori se folosesc relaţii de asociere şi pentru
reprezentarea asocierilor 1:1 sau a asocierilor 1:N.

4.1.4.1 Proiectarea relaţiilor


În figura 2.4 este dată schema conceptuală a bazei de date relaţionale corespunzătoare diagramei E-A
din figura 2.3, dezvoltată în MS Access. În MS Access relaţiile şi asocierile între ele sunt reprezentate vizual în
diagrama de asocieri (Relationships), care este corespondentul relaţional al diagramei E-A.
Mulţimile de entităţi puternice (normale) din diagrama E-A devin relaţii, cu atributele date de
atributele entităţilor. Numele fiecărei relaţii trebuie să fie unic într-o bază de date. Numele unui atribut trebuie să
fie unic în cadrul unei relaţii, dar pot exista atribute cu acelaşi nume în relaţii diferite, care sunt complet diferite
în cadrul bazei de date, posibil diferite şi din punct de vedere semantic. De exemplu atributul Nume al unui
angajat este diferit de atributul Nume al unei secţii.
În relaţiile corespunzătoare mulţimilor de entităţi puternice cheia primară se defineşte fie ca o cheie
naturală (combinaţie de atribute care definesc în mod unic un tuplu al relaţiei), fie ca o cheie primară artificială.
În exemplul prezentat, în relaţiile care corespund mulţimilor de entităţi puternice (ANGAJATI,SECTII,
PROIECTE,COMPONENTE,PRODUSE,FURNIZORI,CLIENTI), s-a adăugat câte o cheie primară artificială
(IdAngajat,IdSectie,IdProiect,etc.).
Mulţimile de entităţi slabe din diagrama E-A devin, de regulă, relaţii aflate în asociere N:1 cu relaţia
corespunzătoare mulţimii de entităţi de care acestea depind. Pentru realizarea acestei asocieri, în relaţia
dependentă se adaugă o cheie străină care referă cheia primară a relaţiei referite (puternice).
Cheia primară a relaţiei dependente poate fi o combinaţie formată din atributul cheie străină şi alte
atribute care asigură posibilitatea de identificare unică a unui tuplu sau poate fi o cheie artificială. Cheia primară
a relaţiei DEPENDENTI este compusă din atributul cheie străină IdAngajat, care referă cheia primară a
relaţiei ANGAJATI şi atributele Nume şi Prenume (ale persoanei dependente):
DEPENDENTI(IdAngajat,Nume,Prenume,DataNasterii,GradRudenie)
O altă modalitate de modelare a mulţimilor de entităţi slabe este aceea de a defini o relaţie separată care
conţine atributele mulţimii entităţilor slabe şi încă o relaţie, pentru asocierea acesteia cu relaţia corespunzătoare
mulţimii entităţilor puternice de care acestea depind.
Pentru exemplul dat, se poate defini o relaţie care descrie mulţimea persoanelor dependente
DEPENDENTI(IdDependent,Nume,Prenume,Data-Nasterii), iar asocierea cu relaţia ANGAJATI
se poate reprezenta printr-o relaţie specială, numită relaţie de asociere (aşa cum se va explica într-un paragraf
următor): AD(IdDependent,IdAngajat,GradRudenie). Relaţia AD conţine atributele IdAngajat şi
IdDependent, care sunt şi chei străine ce referă cheile primare cu acelaşi nume din relaţiile ANGAJATI,
respectiv DEPENDENTI şi formează împreună cheia primară.

Mulţimile de entităţi care sunt subtipuri ale unui tip de entitate dat devin relaţii aflate în asociere 1:1
cu relaţia corespunzătoare mulţimii de entităţi de tipul respectiv (supertip). Pentru realizarea acestei asocieri în
relaţia corespunzătoare subtipului de entitate se defineşte o cheie străină care referă cheia primară din relaţia
corespunzătoare supertipului de entitate; această cheie străină este în acelaşi timp şi cheie primară în relaţia
corespunzătoare subtipului de entitate.

5
Diagrama logica a bazei de date INTREPRINDERE în MS Access.

În exemplul prezentat, asocierile ANGAJATI-INGINERI şi ANGAJATI-SECRETARE sunt asocieri


1:1. În relaţia INGINERI atributul IdAngajat este cheie străină care referă cheia primară cu acelaşi nume din
relaţia ANGAJATI şi este în acelaşi timp şi cheie primară; la fel, în relaţia SECRETARE atributul IdAngajat
este cheie străină care referă cheia primară cu acelaşi nume din relaţia ANGAJATI şi este în acelaşi timp şi cheie
primară.

4.1.4.2 Proiectarea asocierilor


Asocierea binară N:1 dintre două mulţimi de entităţi puternice din diagrama E-A se realizează în
modelul relaţional prin intermediul unei chei străine în prima relaţie (cea cu multiplicitatea N a asocierii) care
referă cheia primară (sau o cheie candidată) din relaţia referită (cea cu multiplicitatea 1 a asocierii). De exemplu,
asocierea N:1 între relaţiile ANGAJATI-SECTII se realizează prin cheia străină IdSecţie adăugată relaţiei
ANGAJATI, care referă cheia primară cu acelaşi nume a relaţiei SECTII.
Astfel de atribute (pentru definirea cheilor străine) nu există în modelul E-A şi ele trebuie să fie
adăugate în modelul relaţional pentru definirea asocierii N:1, aşa cum în modelul ierarhic pentru o astfel de
asociere se adaugă link-uri (pointeri) între noduri. Asocierea binară N:1 poate apare şi ca asociere între o relaţie
corespunzătoare unei mulţimi de entităţi slabe şi relaţia corespunzătoare mulţimii de entităţi puternice de care
aceasta depinde, aşa cum a fost prezentată mai sus.
Asocierea binară M:N dintre două mulţimi de entităţi din diagrama E-A se realizează în modelul
relaţional prin intermediul unei noi relaţii, numită relaţie de asociere. Această nouă relaţie se află în asociere
M:1, respectiv N:1 cu fiecare din cele două relaţii date prin intermediul a două chei străine care referă cheile
primare (sau cheile candidate) din relaţiile date.
De exemplu, pentru a reprezenta asocierea M:N dintre relaţiile COMPONENTE-PRODUSE se adaugă o
nouă relaţie numită COMPOZITII, care conţine cheile străine IdComponenta şi IdProdus, ce referă cheile
primare cu acelaşi nume din relaţiile COMPONENTE, respectiv PRODUSE.
O relaţie de asociere poate să conţină şi alte atribute în afara cheilor străine, atribute care caracterizează
asocierea dintre relaţiile date. De exemplu, în relaţia COMPOZITII atributul NrComponente, care reprezintă
numărul de componente de un anumit tip conţinute de un produs, caracterizează asocierea respectivă.
Cheia primară a unei relaţii de asociere binară poate fi o cheie artificială sau poate fi compusă din cheile
străine, eventual împreună cu alte atribute ale relaţiei. În exemplul dat, cheia primară a relaţiei COMPOZITII
este compusă din cheile străine IdComponenta şi IdProdus.

6
Asocierea M:N dintre relaţiile ANGAJATI-PROIECTE se realizează prin introducerea relaţiei de
asociere ACTIVITATI, care conţine două chei străine, IdAngajat şi IdProiect, care referă cheile primare
cu acelaşi nume din relaţiile corespunzătoare. Cheia primară a relaţiei ACTIVITATI este compusă din cheile
străine IdAngajat şi IdProiect.
Asocierea binară 1:1 între două mulţimi de entităţi puternice se poate transpune în modelul relaţional în
două moduri: fie prin intermediul unei chei străine (ca un caz particular al unei asocieri N:1), fie printr-o relaţie
de asociere (ca un caz particular al unei asocieri M:N). Ca exemplu, vom considera relaţiile SOT, SOTIE,
corespunzătoare a două mulţimi de entităţi puternice aflate în asociere 1:1:
SOT(IdSot,Nume,Prenume,DataNasterii,Adresa)
SOTIE(IdSotie,Nume,Prenume,DataNasterii,Adresa)
Asocierea 1:1 între aceste relaţii se poate realiza prin intermediul unei chei străine care se introduce în
prima relaţie, referind a doua relaţie:
SOT(IdSot,Nume,Prenume,DataNasterii,Adresa,IdSotie)
SOTIE(IdSotie,Nume,Prenume,DataNasterii,Adresa)
La fel de bine se poate defini o cheie străină în cea de-a doua relaţie, referind prima relaţie.
O abordare alternativă pentru realizarea asocierii 1:1 între două relaţii corespunzătoare unor mulţimi de
entităţi puternice este aceea de a crea o nouă relaţie (relaţie de asociere) în care se definesc două chei străine care
referă cheile primare din relaţiile pe care le asociază.
Pentru exemplul de mai sus se va defini relaţia de asociere CASATORIE care conţine cheile străine
IdSot şi IdSotie, ce referă relaţiile SOT, respectiv SOTIE. Cheia primară a acestei relaţii poate fi
combinaţia celor două chei străine sau se poate introduce un nou atribut de identificare (IdCasatorie):
CASATORII(IdCasatorie,IdSot,IdSotie,DataCasatoriei)
Asocierea binară 1:1 dintre o mulţime de entităţi de subtip şi mulţimea de entităţi supertip din diagrama
Entitate-Asociere este tot o asociere binară cu raportul de cardinalitate1:1 între relaţiile corespunzătoare în
modelul relaţional. Aşa cum a fost prezentat mai sus, acest tip de asociere se realizează prin definirea în relaţia
corespunzătoare subtipului de entităţi a unei chei străine care referă cheia primară din relaţia corespunzătoare
supertipului de entităţi; această cheie străină este în acelaşi timp şi cheie primară în relaţia corespunzătoare
subtipului de entităţi.
Asocierea multiplă M:N:P:…. dintre mai mult de două mulţimi de entităţi din diagrama E-A se
realizează în mod asemănător cu asocierea binară, prin intermediul unei noi relaţii care se află în asociere M:1,
N:1, P:1, etc, cu fiecare din relaţiile date. Această asociere este realizată prin intermediul mai multor chei
străine, fiecare cheie străină referind cheia primară (sau o cheie candidată) dintr-una din relaţiile date. Cheia
primară a unei relaţii de asociere multiplă se defineşte în mod asemănător cu cheia primară a unei relaţii de
asociere binară şi poate fi o cheie artificială sau poate fi compusă din toate cheile străine, eventual împreună cu
alte atribute ale relaţiei, care caracterizează asocierea respectivă.
De exemplu, relaţia ACHIZITII realizează asocierea între relaţiile COMPONENTE, FURNIZORI,
ANGAJATI. Ea conţine trei chei străine (IdComponenta, IdFurnizor, IdAchizitor) care referă relaţiile
COMPONENTE, FURNIZORI, respectiv ANGAJATI. Cheia primară a relaţiei de asociere ACHIZITII nu se
poate compune numai din cheile străine, deoarece pot exista două sau mai multe tupluri cu aceleaşi valori ale
cheilor străine: un achizitor (IdAchizitor) poate cumpăra acelaşi tip de componentă (IdComponenta) de
la acelaşi furnizor (IdFurnizor), dar la date diferite. De aceea, cheia primară se defineşte fie ca o combinaţie
a cheilor străine cu alte atribute ale relaţiei, fie ca o cheie artificială (cheia artificială IdAchizitie).
Aşa cum se poate vedea în figura 2.8, schema conceptuală a bazei de date relaţionale rezultate conţine
atât relaţiile corespunzătoare mulţimilor de entităţi din modelul E-A, cât şi relaţiile de asociere introduse pentru a
reprezenta asocierile binare M:N sau asocierile multiple M:N:P.
Se poate concluziona că, deşi există unele detalii de definire a diferitelor categorii de asocieri, în
modelul relaţional toate asocierile se realizează prin intermediul cheilor străine.

4.1.4.3 Proiectarea relaţiilor normalizate


în prima formă normală
O relaţie este normalizată în prima formă normală (FN1) dacă fiecare atribut ia numai valori atomice
şi scalare din domeniul său de definiţie.
O situaţie frecvent întâlnită în proiectarea bazelor de date este aceea în care un atribut al unui tip de
entitate poate lua mai multe valori pentru fiecare entitate.

7
De exemplu, în mulţimea de entităţi PERSOANE(Nume,Prenume, Adresa,NrTelefon)
atributul NrTelefon poate lua mai multe valori (numărul telefonului de acasă, al telefonului de la birou, al
telefonului mobil, etc). Relaţia corespunzătoare unei astfel de mulţimi de entităţi, în care un atribut poate avea
valori multiple (un vector de valori) este o relaţie nenormalizată, care nu este admisă de sistemele de gestiune a
bazelor de date relaţionale.
Transformarea unei relaţii nenormalizate în relaţie normalizată în prima forma normală (FN1), în care
atributele iau numai valori scalare, se poate face în două modalităţi.
În prima modalitate se înlocuieşte atributul care ar putea avea valori multiple cu câte un atribut pentru
fiecare din posibilităţile existente. În cea de-a doua modalitate, se înlocuieşte atributul care ar putea avea valori
multiple cu o nouă relaţie care referă relaţia iniţială printr-o cheie străină. Cheia primară a relaţiei nou introduse
este formată din cheia străină şi atributul extras din relaţia iniţială. De exemplu, în relaţia:
PERSOANE(IdPersoana,Nume,Prenume,Adresa,NrTelefon)
atributul NrTelefon poate lua mai multe valori (telefon de acasă, telefon la birou, telefon mobil), deci
este o relaţie nenormalizată.
Această relaţie se poate normaliza prin înlocuirea atributului NrTelefon cu trei atribute, câte unul
pentru fiecare valoare posibilă:
PERSOANE(IdPersoana,Nume,Prenume,Adresa,
TelefonAcasa,TelefonBirou,TelefonMobil)
Relaţia rezultată după această modificare este normalizată, dar soluţia este neeconomică, dat fiind că
este alocat spaţiu pentru memorarea numărului maxim de valori posibile (în exemplul dat sunt maxim trei valori)
pentru toate tuplurile, deşi este posibil ca în multe situaţii să nu fie completate decât una sau două valori, iar în
rest să fie valori de NULL. O soluţie mai eficientă este de a înlocui relaţia dată cu relaţiile:
PERSOANE(IdPersoana,Nume,Prenume,Adresa)
TELEFOANE(IdPersoana,NrTelefon)
aflate în asociere 1:N prin cheia străină IdPersoana din relaţia TELEFOANE, care referă cheia
primară cu acelaşi nume din relaţia PERSOANE.

4.1.5 PROIECTAREA FIZICĂ A BAZELOR DE DATE


Proiectarea fizică a bazei de date este procesul de alegere a structurilor de memorare şi de acces a
fişierelor bazei de date, pentru a obţine performanţe cât mai bune, pentru cât mai multe din aplicaţiile proiectate.
Fiecare SGBD oferă o varietate de opţiuni de organizare a fişierelor şi a modului de acces la datele
stocate: indexuri, gruparea înregistrărilor corelate în aceleaşi blocuri pe disc (clustering), tabele de dispersie
(hashing), etc. După alegerea sistemului SGBD, în faza de proiectare fizică a bazei de date se aleg structurile
fişierelor bazei de date dintre cele oferite de sistemul de gestiune respectiv, cele mai potrivite cu cerinţele de
proiectare a sistemului de baze de date. Ca parametri generali de alegere a opţiunilor proiectului fizic al unei
baze de date relaţionale se pot enumera:
• Timpul de răspuns. Acesta este intervalul de timp dintre lansarea în execuţie a unei tranzacţii şi
primirea răspunsului la acea tranzacţie. Cea mai mare pondere în timpul de răspuns o are execuţia
operaţiilor în sistemul SGBD, dar există şi factori care nu se află sub controlul acestuia (viteza de
operare a procesorului, dimensiunea memoriei principale, planificarea sarcinilor de către sistemul
de operare, timpii de comunicaţie, etc.).
• Utilizarea spaţiului de memorare. Aceasta este dimensiunea spaţiului pe disc utilizat de fişierele
bazei de date şi de structurile de acces la date.
• Capacitatea tranzacţională (transaction throughput). Acesta este numărul mediu de tranzacţii care
pot fi prelucrate pe minut de către sistemul de baze de date, măsurat în momentele de vârf ale
încărcării. Acesta este un parametru critic în multe aplicaţii, în particular în acele aplicaţii în care
există un număr mare de utilizatori care accesează concurent baza de date (aplicaţii bancare,
rezervări de bilete, etc.).

4.1.6 IMPLEMENTAREA BAZELOR DE DATE


În faza de implementare a bazelor de date relaţionale se creează obiectele principale ale bazei de date
(tabele, vederi, indexuri), pe baza proiectului logic şi a proiectului fizic, folosind limbajul de descriere a datelor
(LDD) oferit de sistemul SGBD ales.
De asemenea, în faza de implementare se proiectează şi se implementează şi procedurile care asigură
verificarea şi forţarea tuturor constrângerilor explicite (aserţiuni, dependenţe de date care nu sunt determinate de
chei ale relaţiilor), a căror previziune şi documentare a fost realizată în faza de proiectare logică a bazei de date.

8
Baza de date poate fi apoi populată cu date obţinute prin conversia unor date existente sub formă de
fişiere sau introduse direct în tabele. Este esenţial ca în faza de populare a bazei de date să fie verificate şi
impuse toate constrângerile prevăzute, deci să fie implementate şi verificate toate triggerele, procedurile stocate
sau funcţiile din programele de aplicaţii de verificare a constrângerilor explicite.
Tot în această fază programatorii de aplicaţii implementează, pe baza specificaţiilor conceptuale ale
tranzacţiilor, programele de aplicaţii, în diferite tehnologii de programare disponibile (limbaje procedurale de
extensie a limbajului SQL, limbajul SQL integrat, interfeţe şi biblioteci de programare, etc.).
După crearea şi popularea bazei de date şi implementarea programelor de aplicaţii se poate trece la
etapa de operare a sistemului de baze de date, în paralel cu monitorizarea şi întreţinerea acestuia.

4.2 LIMBAJE, INTERFETE SI BIBLIOTECI DE PROGRAMARE


A APLICAŢIILOR DE BAZE DE DATE
Aplicaţiile de baze de date sunt, în general, mult mai complexe decât alte categorii de aplicaţii, deoarece
trebuie să realizeze, pe de o parte interfaţa cu sistemele de gestiune a bazelor de date, pentru introducerea şi
extragerea datelor, iar pe de altă parte, interfaţa cu utilizatorii, astfel încât aceştia să poată efectua diferite
operaţii. Pe lângă aceste funcţii de interfaţare, programele de aplicaţii de baze de date trebuie să implementeze şi
algoritmii de calcul necesari.
Interfeţele cu utilizatorii sunt, în general, interfeţe grafice, cunoscute sub numele de formulare (forms).
Prin intermediul controalelor (ferestre cu diferite funcţionalităţi) din cadrul formularelor, utilizatorii sunt ghidaţi
să introducă numai datele strict necesare, de cele mai multe ori prin selectarea lor din mai multe valori prezentate
(şi valide), astfel încât să se limiteze posibilitatea introducerii unor date eronate. Rezultatele operaţiilor cerute de
utilizatori sunt afişate, de asemenea în formulare şi pot fi tipărite la dorinţă, sub formă de rapoarte. De exemplu,
o aplicaţie de gestiune a stocurilor oferă o interfaţă grafică utilizatorilor, prin intermediul căreia aceştia pot să
introducă în baza de date diferite informaţii (cantităţi de marfuri intrate sau ieşite, etc.); programul calculează
diferiţi indicatori de gestiune (nivelul stocurilor, dinamica stocurilor, etc.) şi afişează sau tipăreşte rezultatele
calculate.
Interfeţele grafice cu utilizatorii se pot realiza prin tehnologii software de uz general, dar multe sisteme
de dezvoltare a bazelor de date oferă suport pentru dezvoltarea acestora. De exemplu, Microsoft Access permite
dezvoltarea vizuală a aplicaţiilor, inclusiv formularele de interfaţă; sistemul de dezvoltare Oracle conţine
utilitarul Oracle Forms care permite crearea interfeţelor grafice cu utilizatorii, etc.
Sistemele de gestiune a bazelor de date relaţionale prelucrează instrucţiuni (comenzi) SQL. Limbajul
SQL (în oricare din standardele definite până în prezent) este un limbaj neprocedural, care permite definirea
datelor şi operaţii de manipulare a acestora, dar nu prevede instrucţiuni de control al ordinii de execuţie a
operaţiilor. De aceea, pentru realizarea aplicaţiilor de baze de date au fost dezvoltate o multitudine de limbaje şi
biblioteci de programare: limbaje procedurale de extensie a limbajului SQL, limbajul SQL integrat, biblioteci de
funcţii sau de clase pentru comunicarea cu bazele de date.
Un limbaj procedural (procedural language) este o extensie a limbajului SQL şi permite combinarea
instrucţiunilor SQL cu instrucţiuni de control al ordinii de execuţie. Astfel de limbaje sunt folosite în principal în
cadrul sistemelor de gestiune, pentru stocarea unor proceduri de calcul, dar există şi posibilitatea ca programele
de aplicaţii să transmită sistemului SGBD blocuri (loturi) de instrucţiuni într-un limbaj procedural. Transmiterea
unui bloc de instrucţiuni în locul transmiterii individuale a fiecărei instrucţiuni SQL, asigură performanţe de
execuţie a aplicaţiilor mai ridicate, datorită reducerii traficului în reţea.
Majoritatea sistemelor de gestiune sunt prevăzute cu cel puţin un limbaj procedural, cele mai cunoscute
fiind: PL/SQL pentru sistemele de gestiune Oracle, Transact-SQL pentru sistemele de gestiune Microsoft SQL
Server, PL/PGSQL şi PL/Pearl pentru sistemul de gestiune PostgreSQL, etc.
Pentru dezvoltarea programelor de aplicaţii de baze de date se pot aborda două tehnologii diferite:
• Limbajul SQL integrat într-un limbaj de nivel înalt.
• Interfeţe de programare a aplicaţiilor.
În limbajul SQL integrat (Embeded SQL), instrucţiunile limbajului SQL sunt incluse direct în codul
programului sursă scris într-un limbaj gazdă de nivel înalt (Ada, PL/1, Pascal, Fortran, Cobol, C). Controlul
fluxului de operaţii este realizat prin instrucţiunile limbajului gazdă, iar operaţiile cu baza de date sunt realizate
prin instrucţiuni SQL.
Interfeţele de programare a aplicaţiilor (Application Programming Interface - API) sunt dezvoltate ca
biblioteci de funcţii sau de clase, iar programele de aplicaţie folosesc apelul funcţiilor prevăzute de interfaţa
respectivă pentru a comunica cu serverul bazei de date.
Interfeţele de programare a aplicaţiilor permit dezvoltarea aplicaţiilor de baze de date prin apeluri de
funcţii (call-level interface – CLI) într-o manieră flexibilă, uşor de dezvoltat şi de întreţinut. Interfeţele separă

9
programul de aplicaţie de biblioteca de acces la baza de date, iar această separare oferă modularitate, stabilitate şi
independenţă mai ridicată a aplicaţiilor.
Fiecare dintre aceste limbaje şi biblioteci reprezintă în sine o topică extinsă, fiind subiectul multor cărţi
de specialitate, pe lângă manualele de referinţă, manualele de programare, manualele de dezvoltare, etc.
Prezentarea lor succintă într-un singur capitol nu înlocuieşte necesitatea consultării unor astfel de documentaţii
atunci când se programează efectiv, ci are ca scop formarea unor cunoştinţe generale despre problematica
programării aplicaţiilor de baze de date. În secţiunile care urmează este abordată descrierea comparată a
posibilităţilor de programare oferite de diferite limbaje şi biblioteci şi este urmărit modul în care acestea reflectă
caracteristicile bazelor de date relaţionale.

4.2.1 LIMBAJE PROCEDURALE DE EXTENSIE A LIMBAJULUI SQL


Un limbaj procedural (procedural languages) combină instrucţiuni SQL cu instrucţiuni pentru
controlul ordinii de execuţie, asigurând extinderea posibilităţilor sistemelor de gestiune a bazelor de date.
Limbajele procedurale permit definirea unor variabile locale, instructiuni de control al ordinii de
execuţie (bucle while, instrucţiuni condiţionale if, etc.), precum si suport de creare a cursoarelor, a
procedurilor stocate, a funcţiilor definite de utilizator şi a declanşatorilor (triggere).
Un cursor (cursor) este o structură care permit memorarea (folosind un buffer în memorie) a unei
mulţimi de linii returnate ca rezultat de o instrucţiune de interogare.
Programele de aplicaţii nu pot prelucra deodată toate liniile rezultatului şi folosesc cursoare pentru
extragerea individuală a unei linii (sau a unui grup de linii) din mulţimea de linii rezultat. În fiecare moment,
într-un cursor există o poziţie curentă (linie curentă) în mulţimea de linii rezultat. La fiecare operaţie de
extragere, se citesc una sau mai multe linii relativ la poziţia curentă a cursorului, iar această poziţie se
actualizează conform modului de parcurgere (înainte sau înapoi). Cursoarele permit salvarea, folosirea repetată
sau derularea de mai multe ori a liniilor pe care le conţin. De asemenea, cursoarele oferă mai multe posibilităţi de
control al accesului concurent a mai multor utilizatori la tabelele unei baze de date.
În standardul SQL2 sunt prevăzute instrucţiunile principale de definire şi de operare a cursoarelor:
DECLARE CURSOR, OPEN, FETCH şi CLOSE. Instrucţiunea de definire a unui cursor SQL2 are sintaxa:
DECLARE nume_cursor [INSENSITIVE][SCROLL] CURSOR
FOR instructiune_select
[FOR {READ ONLY|UPDATE [OF col1[,...n]]}];

O astfel de declaraţie creează cursorul cu numele nume_cursor pentru mulţimea de linii rezultat
returnată de instrucţiunea instructiune_select şi setează atributele de comportare (behavior attributes) ale
cursorului: atributul de senzitivitate şi atributul de actualizare.
Instrucţiunea OPEN nume_cursor, specificată în standardul SQL2, permite deschiderea unui cursor
(care a fost declarat cu numele nume_cursor printr-o instrucţiune DECLARE CURSOR); deschiderea
cursorului înseamnă popularea cursorului cu datele din tabelele la care se referă instrucţiunea SELECT a
cursorului. Instrucţiunea de extragere a liniilor dintr-un cursor are sintaxa:
FETCH [FROM] nume_cursor INTO lista_variabile;

şi defineşte operaţia de extragere a unei linii (din poziţia curentă) din cursorul cu numele cursor_name în
variabilele din lista lista_variabile.
Instrucţiunea CLOSE nume_cursor, specificată în standardul SQL2, permite închiderea unui cursor
(care a fost declarat cu numele nume_cursor printr-o instrucţiune DECLARE CURSOR); închiderea cursorului
înseamnă eliberarea buferului de memorie ocupat de cursorul respectiv.
Cursoarele se pot crea atât la nivelul limbajului SQL2 sau al extensiilor procedurale ale acestuia, cât şi
prin intermediul limbajului SQL integrat (Embedded SQL) sau a bibliotecilor şi interfeţelor de programare a
aplicaţiilor de baze de date (ca, de exemplu, ODBC, JDBC).
Cursoarele pot fi memorate în server, iar clientul primeşte câte o linie (sau un grup de linii) de la server
la fiecare instrucţiune de extragere, sau pot fi memorate în client şi sunt folosite direct în programul respectiv.
Cursoarele create prin intermediul unui limbaj de extensie SQL sunt cursoare la server şi pot fi definite
în funcţiile şi procedurile stocate din server. În interfeţele de programare se pot defini atât cursoare la server cât
şi cursoare la client. Aceste aspecte vor fi detaliate pentru fiecare limbaj sau interfaţă în parte. În general, se
consideră că este mai avantajos să fie folosite cursoare la server decât cursoare la client, deoarece memorarea în
client necesită ca întreaga mulţime de tupluri să fie transferată dintr-o dată de la server la client, chiar dacă
ulterior, din diferite cauze, clientul nu va folosi decât o parte din tuplurile pe care le-a primit prin reţea şi le-a
memorat.

10
O procedură stocată (stored procedure) este o procedură care implementează o parte din algoritmii de
calcul ai aplicaţiilor şi care este memorată în baza de date la fel ca şi alte obiecte ale bazei de date.
Procedurile stocate sunt compilate şi optimizate de sistemul de gestiune o singură dată, atunci când sunt
folosite prima oară şi rămân memorate în server pentru oricâte apeluri ulterioare.
Apelul unei proceduri stocate de către un client (aplicaţie) produce execuţia tuturor instrucţiunilor
procedurii şi returnarea rezultatului (dacă există), eliminând cerinţa ca acel client să transmită individual fiecare
instrucţiune necesară pentru rezolvarea sarcinii de calcul respective. În felul acesta se reduce, pe de o parte,
comunicaţia între aplicaţie şi serverul bazei de date şi, pe de altă parte, chiar timpul de execuţie a sarcinii
respective, dat fiind că procedura stocată este deja compilată şi optimizată.
Există însă şi pericolul ca, dacă foarte multe aplicaţii îşi desfăşoară operaţiile de prelucrare pe server
prin intermediul procedurilor stocate, serverul să fie congestionat şi să nu mai asigure performanţe satisfăcătoare
nici uneia din aplicaţii. Cu alte cuvinte, utilizarea intensă a procedurilor stocate poate duce la scăderea
scalabilităţii sistemului de baze de date. Bineînţeles, o analiză atentă a modului de distribuire a operaţiilor de
prelucrare între execuţia la serverul de baze de date (în proceduri stocate) şi execuţia la client, în programele de
aplicaţii sau în alte niveluri intermediare de execuţie distribuită (middleware) poate asigura atât perfomanţe
ridicate de execuţie cât şi scalabilitatea sistemelor de baze de date.
O funcţie definită de utilizator (user-defined function) este o funcţie memorată în baza de date, la fel
ca o procedură stocată; diferenţa între acestea este că o funcţie returnează întotdeauna o valoare şi poate fi
folosită direct în expresii, pe câtă vreme o procedură stocată poate să nu returneze nici o valoare.
Un trigger este o procedură stocată cu o funcţionare specială, care este executată automat atunci când
se efectuează o operaţie de actualizare a unei relaţii.
Triggerele sunt asociate cu comenzi de inserare (INSERT), ştergere (DELETE) sau actualizare
(UPDATE) a tuplurilor dintr-o anumită relaţie şi au ca utilizare principală extinderea capacităţii sistemului de
gestiune de menţinere a integrităţii datelor relaţionale.
Prin triggere se pot specifica şi impune procedural constrângerile explicite, cum sunt dependenţele de
date (dependenţe funcţionale, multivalorice, sau de joncţiune) care nu sunt determinate de chei ale relaţiilor. De
asemenea, triggerele mai sunt folosite şi pentru generarea automată a unor valori care rezultă din valori ale altor
atribute, precum şi pentru jurnalizarea transparentă a evenimentelor sau culegerea de date statistice în legătură cu
accesarea relaţiilor.
Standardul SQL2 nu prevede comenzi pentru crearea triggerelor, dar ele pot fi create folosind
instrucţiuni ale limbajelor procedurale de extensie a limbajului SQL. În general, triggerele definite în diferite
limbaje procedurale sunt neportabile.
Limbajele procedurale de extensie a limbajului SQL, deşi au aceeaşi utilizare generală în programarea
bazelor de date, sunt specifice fiecărui SGBD şi nu sunt tocmai compatibile între ele. În continuare se vor
prezenta pe scurt caracteristicile limbajelor Transact-SQL şi PL/SQL. Detalii privind aceste limbaje se găsesc,
bineînţeles, în documentaţia sistemelor de gestiune care le utilizează.

4.2.1.1 LIMBAJUL TRANSACT SQL


Programele Transact-SQL pot fi executate ca proceduri memorate în cadrul bazei de date sau ca loturi
(batchs) de instrucţiuni transmise sistemului prin intermediul unei aplicaţii sau a unui program utilitar de acces
interactiv la baza de date. Programele utilitare de acces interactiv din distribuţia SQL Server sunt isql sau osql (la
nivel de linie de comandă) şi SQL Query Analyzer (care oferă şi o interfaţă grafică). De asemenea, numeroase
operaţii de definire a tabelelor şi a altor caracteristici ale bazelor de date se pot face din consola de administrare
(Enterprise Manager) a sistemului SQL Server.
Loturi de prelucrare. Un lot (batch) constă dintr-o secvenţă de instrucţiuni Transact-SQL terminată cu
comanda GO. Există mai multe reguli de grupare a instrucţiunilor în loturi. De exemplu: orice instrucţiune
CREATE trebuie să fie prima în lot; de aceea nu pot fi grupate în acelaşi lot instrucţiunile CREATE TABLE,
CREATE VIEW, CREATE TRIGGER, CREATE RULE; etc.
Sistemul de gestiune SQL Server compilează lotul de instrucţiuni şi, dacă este corect din punct de
vedere sintactic, începe execuţia lui; dacă există erori de compilare, lotul nu este executat deloc. Erorile apărute
în cursul execuţiei unui lot pot opri numai execuţia instrucţiunii eronate (de exemplu, în cazul erorilor de
nerespectare a unor constrângeri) sau pot opri execuţia instrucţiunii care a provocat eroarea şi a tuturor
instrucţiunilor care urmează după aceasta. În acest din urmă caz vor fi efectuate şi operaţii legate de gestiunea
tranzacţiilor, dacă este cazul (rularea înapoi - rollback- a operaţiilor efectuate înainte de apariţia erorii).
Variabile locale. În limbajul Transact-SQL pot fi folosite variabile locale pentru memorarea unor valori
care pot fi testate sau modificate (ca în orice alt limbaj) şi, în plus, asigură transferul datelor către şi de la tabelele
bazei de date. Variabilele locale au ca domeniu de definiţie lotul, procedura sau blocul în care au fost declarate.

11
O variabilă locală se declară folosind instrucţiunea DECLARE care specifică un identificator (un nume
care trebuie să înceapă cu caracterul @) şi tipul variabilei. Sintaxa de declarare arată astfel:
DECLARE @nume_variabila tip_date
Limbajul Transact-SQL suportă toate tipurile de date prevăzute în standardul SQL2 şi, în plus, permite
definirea de către utilizator a unor noi tipuri de date. Iniţializarea variabilelor locale se poate face printr-o
comandă SELECT, cu următoarea sintaxă:
SELECT @nume_variabila = expresie
De exemplu, declararea variabilei locale cu numele @contor, de tip întreg (int) şi iniţializarea
acesteia cu valoarea 1 se face astfel:
DECLARE @contor int
SELECT @contor = 1
Instrucţiuni de control al ordinii de execuţie. Ordinea de execuţie a instrucţiunilor unui lot sau a unei
proceduri stocate este controlată prin următoarele instrucţiuni de control:
BEGIN...END WAITFOR
GOTO WHILE
IF...ELSE BREAK
RETURN CONTINUE

Instrucţiuni SQL. Limbajul Transact-SQL suportă toate instrucţiunile SQL, cu unele modificări de
sintaxă, astfel încât să poată fi folosite în combinaţie cu variabilele locale ale programului.
De exemplu, formă modificată a instrucţiunii SELECT, prin care se asignează variabile locale cu valori
ale unor atribute selectate, este:
SELECT @var1 = col1, @var2 = col2, ... @varn = coln
FROM lista_tabele WHERE conditie
O astfel de instrucţiune este utilă pentru interogările care returnează o singură linie, deoarece variabilele
locale sunt setate cu valorile coloanelor primei linii a rezultatului, iar valorile din celelalte linii se pierd
(nemaifiind loc unde să fie memorate).
DECLARE @id_angajat int, @nume char(20, @prenume char(20)
SELECT @nume = Nume, @prenume = Prenume
FROM ANGAJATI WHERE IdSAngajat = @id_angajat
Atunci când o interogare returnează o mulţime de linii se poate folosi un cursor.
De asemenea, variabilele locale pot fi folosite în construcţia condiţiei din clauza WHERE a instrucţiunii
SELECT, în orice loc unde poate fi folosită o variabilă de coloană sau o constantă.

4.2.2 INTERFETE SI BIBLIOTECI DE PROGRAMARE


A APLICATIILOR DE BAZE DE DATE
Deşi nu sunt prevăzute în standardul SQL, interfeţele de programare a aplicaţiilor de baze de date s-au
dezvoltat şi diversificat deosebit de mult şi reprezintă în momentul de faţă cea mai folosită tehnologie de
realizare a acestor aplicaţii. Multe sisteme de gestiune a bazelor de date prezintă biblioteci de programare care
oferă o interfaţă compusă din funcţii şi macrodefiniţii ce permit unei aplicaţii să interacţioneze cu serverul bazei
de date. În general, funcţiile prevăzute în astfel de interfeţe permit conectarea la server, transmiterea către server
a unor instrucţiuni SQL şi preluarea rezultatelor returnate de server. Cele mai multe biblioteci sunt dezvoltate
pentru limbajul C (de exemplu, biblioteca C pentru sistemul Microsoft SQL Server - DB-Library for C), dar
există biblioteci şi pentru alte limbaje (C++, Perl, PHP, etc).
Pe lângă aceste biblioteci specifice diferitelor sisteme SGBD, s-au dezvoltat şi bibiloteci care oferă
interfeţe cu un grad ridicat de generalitate, care pot fi folosite pentru mai multe tipuri de sisteme SGBD. Cele
mai cunoscute sunt interfeţele ODBC (Open DataBase Connectivity) sau JDBC (Java DataBase Connectivity),
care vor fi prezentate în ultimele secţiuni ale acestui capitol.

12
4.2.2.1 INTERFAŢA ODBC
Bibliotecile de programare a aplicaţiilor specifice fiecărui SGBD prezintă dezavantajul dependenţei
codului dezvoltat faţă de sistemul folosit la un moment dat; dacă se schimbă sistemul SGBD, atunci se schimbă
întreaga interfaţă şi o mare parte din programele de aplicaţii trebuie să fie rescrise.
Tehnologia ODBC (Open Database Connectivity) oferă o interfaţă de programare a aplicaţiilor
(Application Programming Interface – API) prin apel de funcţii (call-level interface - CLI), independentă de
sistemul SGBD folosit. Această independenţă se obţine prin intermediul unor drivere, care sunt specifice fiecărui
SGBD, în timp ce funcţiile prevăzute în interfaţă, care pot fi apelate de aplicaţii, sunt aceleaşi, definite de
standardul ODBC.
Pentru a folosi interfaţa ODBC, trebuie să fie instalat driverul pentru sistemul SGBD necesar. Unele
drivere ODBC se instalează atunci când se instalează unele pachete de programe. De exemplu, driverele ODBC
pentru baze de date Access, FoxPro se instaleză o dată cu pachetul Microsoft Office. Alte drivere ODBC se pot
instala separat, dacă există pachetul de instalare respectiv. Administratorul de drivere încarcă driverul specific
sistemului de gestiune folosit de aplicaţie.

Interfaţa ODBC Aplicaţie

Administrator de drivere

Driver Driver Driver

Sursa Sursa Sursa


de date de date de date

SGBD şi SGBD şi SGBD şi


Baza de date Baza de date Baza de date

Arhitectura ODBC.

Administratorul de drivere, precum şi driverele specifice diferitelor sisteme de gestiune de baze de date
sunt, în general, furnizate ca biblioteci dinamice care sunt legate (link) în programul de aplicaţie.
Pentru orice tip de SGBD pentru care este instalat un driver ODBC se poate defini o sursă de date
folosind administratorul de surse de date (ODBC Data Source Administrator) care atribuie un nume de sursă de
date (şi alte opţiuni, depinzând de driverul instalat) unei baze de date.
Un program de aplicaţie care utilizează interfaţa ODBC apelează funcţii ODBC care sunt implementate
în driver. Driverul transformă apelurile de funcţii ODBC în comenzi SQL (sau într-un limbaj procedural de
extensie a limbajului SQL, implementat de sistemul de gestiune respectiv) pe care le transmite sistemului de
gestiune, preia rezultatele şi le returnează programului de aplicaţie.
Acestă tehnologie, care oferă şi avantajul unui standard la care au participat majoritatea producătorilor
de sisteme de gestiune a bazelor de date, a fost extrem de bine primită de programatorii de aplicaţii de baze de
date şi s-a dezvoltat foarte rapid. Drivere ODBC sunt disponibile pentru majoritatea sistemelor de gestiune a
bazelor de date şi sunt produse de furnizorii de SGBD-uri sau de alte firme producătoare de software.
Interfaţa ODBC poate fi folosită direct în programarea aplicaţiilor, sau poate fi accesată prin
intermediul altor interfeţe, de nivel mai ridicat, care înglobează funcţiile ODBC, oferind modalităţi de
programare obiect-orientate: RDO (Remote Data Objects), DAO (Data Access Objects), clase MFC (Microsoft
Foundation Class) pentru baze de date.

4.2.2.2 INTERFAŢA JDBC


Ca şi interfaţa ODBC, interfaţa JDBC este o interfaţă de programare a aplicaţiilor de baze de date
independentă de platformă şi de sistemul SGBD. Interfaţa JDBC constă din clase în limbajul Java şi permite
interacţiunea cu baze de date relaţionale, precum şi cu alte surse de date în format tabelar.

13
Prima versiune a specificaţiilor JDBC 1.0 API este conţinută în biblioteca java.sql (care face parte
din sistemul de dezvoltare standard Java - J2SDK). Noi funcţionalităţi au fost adăugate în specificaţiile JDBC
2.0 API, şi JDBC 2.1 API, dintre care o parte sunt conţinute în versiunile recente ale bibliotecii java.sql, iar
altă parte în biblioteca de extensie javax.sql.
Ca şi interfaţa ODBC, interfaţa JDBC este o interfaţă la nivel de apeluri de funcţii (call-level interface -
CLI) independentă de sistemul de gestiune, prin care programele de aplicaţie Java transmit către baza de date
instrucţiuni SQL ca şiruri de caractere. Interfaţa JDBC, care s-a dezvoltat pe baza interfeţei ODBC, permite
accesul direct la bazele de date relaţionale din programe Java, din care apelul funcţiilor ODBC este destul de
dificil şi produce scăderea nivelului de securitate oferit de tehnologia Java.

14
5. NORMALIZAREA RELAŢIILOR
La proiectarea bazelor de date relaţionale se stabileşte mulţimea relaţiilor, prin selectarea tipurilor
relevante de entităţi din realitatea modelată şi a asocierilor dintre acestea. Modul în care se pot stabili relaţiile
unei baze de date nu este unic şi de aceea este necesar să existe criterii de evaluare a calităţii relaţiilor, astfel
încât acestea să asigure atât integritatea datelor cât şi performanţe de interogare cât mai bune. Criteriul de
evaluare a relaţiilor se bazează pe conceptul de dependenţe de date.
Dependenţele de date (data dependencies) reprezintă constrângeri care se impun valorilor atributelor
unei relaţii şi care determină proprietăţile relaţiei în raport cu operaţiile de inserare, ştergere şi actualizare a
tuplurilor. Pe baza dependenţelor de date se pot stabili reguli de definire a relaţiilor, astfel încât acestea să
prezinte anumite proprietăţi, proprietăţi care caracterizează formele normale ale relaţiilor (sau gradele de
normalizare ale acestora).
O formă normală a unei relaţii (normal form) presupune anumite condiţii pe care trebuie să le
îndeplinească valorile atributelor şi dependenţele de date definite pe acea relaţie.
Iniţial, E.F. Codd a propus trei forme normale, numite prima formă (FN1), a doua formă (FN2) şi a treia
formă normală (FN3). Ulterior, a fost introdusă o definiţie mai completă a celei de-a treia forme, care a primit
numele de forma normală Boyce-Codd (FNBC). Toate aceste forme normale se referă la condiţiile pe care
trebuie să le îndeplinească dependenţele funcţionale între atributele unei relaţii. Forme normale superioare, cum
sunt a patra formă normală (FN4) şi a cincea formă normală (FN5) impun condiţii dependenţelor multivalorice,
respectiv dependenţelor de joncţiune între atributele unei relaţii.
Formele normale ale relaţiilor formează o colecţie ordonată (FN1, FN2, FN3, FNBC, FN4, FN5), şi ele
impun condiţii din ce în ce mai restrictive asupra dependenţelor de date admise, minimizând astfel redundanţa şi
anomaliile de actualizare a relaţiilor. Ordonarea formelor normale de la FN1 la FN5 înseamnă că orice relaţie
aflată în FN2 este, de asemenea, în FN1, orice relaţie în FN3 este în FN1 şi FN2, etc.
Normalizarea relaţiilor (normalization) constă în descompunerea lor, astfel încât relaţiile rezultate să
îndeplinească condiţii din ce în ce mai restrictive în ceea ce priveşte dependenţele de date, adică să corespundă
unor forme normale cât mai avansate.
Prin normalizare se elimină (sau se micşorează) redundanţa datelor memorate în relaţii şi anomaliile
care provin din această redundanţă. Totuşi, după normalizare, o parte din interogări vor necesita joncţiuni între
relaţiile rezultate prin descompunere, joncţiuni care nu erau necesare dacă relaţia nu ar fi fost descompusă şi care
conduc la creşterea duratei de execuţie a acestor operaţii de interogare.
De aceea, în practica proiectării bazelor de date, normalizarea relaţiilor nu se face întotdeauna până în
forma normală cea mai înaltă. Proiectanţii pot păstra relaţiile într-o formă de normalizare mai scăzută (de obicei
în forma FN3 sau FNBC) cu condiţia ca aceste situaţii să fie bine cunoscute şi documentate, pentru a se trata în
mod corespunzător operaţiile în care ar putea să apară anomalii de actualizare a relaţiilor.

5.1 REDUNDANŢA DATELOR ŞI ANOMALIILE DE ACTUALIZARE


A RELAŢIILOR
Problemele legate de redundanţa datelor memorate în relaţii vor fi studiate pe un exemplu.
Fie relaţia AP(IdAngajat,Nume,Prenume,Adresa,IdProiect,Ore). Fiecare tuplu al relaţiei
conţine informaţii despre un angajat şi numărul de ore aferente fiecăruia dintre proiectele la care acesta lucrează.
Semnificaţia atributelor este destul de clară. Atributul IdAngajat este numărul de identificare (unic) al unui
angajat; Nume, Prenume şi Adresa sunt date ale angajatului. Atributul IdProiect este identificatorul
(numărul) unui proiect la care lucrează angajatul; se poate considera că este o cheie străină care referă cheia
primară cu acelaşi nume dintr-o altă relaţie, care descrie proiectele instituţiei, dar acest lucru este mai puţin
important în acest moment. Atributul Ore reprezintă numărul de ore lucrate de angajat la proiectul respectiv.
Dacă se admite că un angajat lucrează la mai multe proiecte, atunci cheia primară a relaţiei AP este PK =
{IdAngajat,IdProiect}. O stare a relaţiei AP este dată în figura 5.1, a.
Se observă că datele despre fiecare angajat (Nume, Prenume, Adresa) se repetă în fiecare tuplu
corespunzător fiecărui proiect la care acesta lucrează, ceea ce reprezintă un grad ridicat de redundanţă a datelor.
Această redundanţă are ca efect atât creşterea spaţiului de memorare a relaţiei, cât şi anomalii de actualizare a
relaţiei. Anomaliile de actualizare a relaţiei apar la inserarea, ştergerea sau actualizarea tuplurilor relaţiei.
Anomalii de inserare. În relaţia AP nu se pot introduce date despre un angajat (identificatorul
angajatului, numele, prenumele, adresa) dacă nu există cel puţin un proiect la care acesta să lucreze. Într-adevăr,
nu se poate introduce un tuplu cu valoare NULL a atributului IdProiect, deoarece acest atribut face parte din
cheia primară a relaţiei.
O altă anomalie de inserare în relaţia AP este aceea că se poate introduce un nou tuplu care conţine alte
valori ale atributelor Nume, Prenume sau Adresa, pentru aceeaşi valoare a identificatorului IdAngajat. De
exemplu, se poate introduce tuplul (1,Dragomir,Eugen,Bucuresti,P3,110). Acest tuplu este
acceptat de SGBD, deoarece are cheia primară (1,P3), care nu mai există în relaţia AP. Dar în acest moment
starea relaţiei AP nu este consistentă din punct de vedere semantic deoarece există doi angajati (Ionescu Ion
şi Dragomir Eugen) care au acelaşi număr de identificare (IdAngajat = 1).

AP
IdAngajat Nume Prenume Adresa IdProiect Ore
1 Ionescu Ion Bucuresti P1 100
2 Popescu Petre Craiova P1 200
3 Marin Mihai Ploieşti P2 120
1 Ionescu Ion Bucuresti P2 150
2 Popescu Petre Craiova P2 50
a
A P
IdAngajat Nume Prenume Adresa IdAngajat IdProiect Ore
1 Ionescu Ion Bucuresti 1 P1 100
2 Popescu Petre Craiova 2 P1 200
3 Marin Mihai Ploieşti 3 P2 120
1 P2 150
2 P2 50
b
Fig. 5.1. a - Relaţia AP; b - descompunerea relaţiei AP în relaţiile A şi P.

Anomalii de ştergere. Dacă se şterg toate tuplurile referitoare la un anumit proiect din relaţia AP, atunci
se pot pierde toate datele referitoare la acei angajaţi care lucrează doar la proiectul respectiv. De exemplu, dacă
se şterg tuplurile referitoare la proiectul P2 (1,Ionescu,Ion,Bucuresti,P2,150), (2,Popes-
cu,Petre,Craiova,P2,50), (3,Marin,Mihai,Ploieşti,P2,120), atunci se pierd toate informaţiile
despre angajatul Marin Mihai (identificatorul angajatului, numele, prenumele, adresa).
Anomalii de actualizare. Dacă se modifică valoarea unuia din atributele care au valori redundante
(Nume,Prenume sau Adresa) într-un tuplu al relaţiei AP, starea relaţiei poate deveni inconsistentă. De
exemplu, dacă în tuplul (1, Ionescu,Ion,Bucuresti,P2,150) se modifică atributul Nume la valoarea
Gheorghiu, atunci în relaţie vor exista tuplurile: (1,Ionescu,Ion, Bucuresti,P1,100) şi
(1,Gheorghiu,Ion,Bucuresti,P2,150), adică doi angajati cu nume diferite (Ionescu şi
Gheorghiu) au acelaşi număr de identificare (1). De asemenea, pot să apară numeroase alte situaţii de
inconsistenţă atunci când se fac actualizări într-o astfel de relaţie care prezintă date redundante.
Dacă se doreşte păstrarea consistenţei relaţiei AP, trebuie să fie luate măsuri speciale, care constau în
realizarea unor proceduri care să verifice datele la fiecare operaţie de actualizare a relaţiei şi să interzică
operaţiile care produc inconsistenţă.
Anomaliile descrise mai sus se pot elimina dacă relaţia AP se descompune în două relaţii echivalente:
relaţia A(IdAngajat,Nume,Prenume,Adresa), cu cheia primară IdAngajat şi relaţia
P(IdAngajat,IdProiect,Ore), cu cheia primară {IdAngajat,IdProiect}, iar atributul IdAngajat
este o cheie străină care referă cheia primară cu acelaşi nume din relaţia A (fig. 5.1, b).
Aceste relaţii nu mai prezintă redundanţă şi nici anomalii la actualizarea lor (se poate verifica uşor acest
lucru). De exemplu, nu se admite inserarea tuplulului (1,Dragomir,Eugen,Bucureşti) în relaţia A cu
starea din figura 5.1, deoarece sistemul de gestiune refuză introducerea unui nou tuplu cu aceeaşi valoare (1) a
cheii primare (IdAngajat).
Relaţiile A şi P sunt “mai bune” din punct de vedere al volumului de date memorate şi sunt, de fapt,
relaţii cu un grad de normalizare mai ridicat, aşa cum se va demonstra în secţiunile următoare.
În schimb, interogările asupra acestor două relaţii A, P sunt mai costisitoare (ca timp de execuţie) faţă
de interogările similare executate numai în relaţia AP. Fie, de exemplu, interogarea “Care este numărul de ore pe
care le lucrează angajatul cu numele Ionescu la proiectul P1 ?”. Expresiile de algebră relaţională pentru
realizarea acestei interogări în relaţia AP şi respectiv, în relaţiile A şi P sunt:

2
Q1 = ΠOre σNume ='Ionescu' AND IdProiect ='P1'(AP)
Q2 = ΠOre σNume ='Ionescu' AND IdProiect ='P1'(A><P)

Pentru realizarea interogării Q2 este necesară o operaţie de joncţiune între cele două relaţii A şi P în care
a fost descompusă relaţia iniţială AP, operaţie care, în general, este mai costisitoare ca timp de execuţie decât
operaţia de restricţie aplicată unei singure relaţii.
Cum se poate şti care este cea mai bună alegere a relaţiilor? Răspunsul la această întrebare nu este
foarte simplu, deoarece necesită numeroase informaţii privind exploatarea ulterioară a bazei de date (ce spaţiu de
memorare a relaţiilor va fi disponibil, ce interogări se vor efectua asupra relaţiilor, în ce situaţii este important să
se obţină o viteză de execuţie mare, etc.).
Principial, o relaţie trebuie să corespundă unui singur tip de entitate (sau asociere) precis definită şi
atributele acesteia să corespundă numai acelui tip de entitate şi să nu fie combinate cu atribute ale altor tipuri de
entităţi. Această cerinţă corespunde unei definiţii semantice simple, concise şi clare a relaţiei.
În exemplul de mai sus, relaţia A corespunde tipului de entitate “angajat” şi are ca atribute numai pe
acelea care se referă la acest tip de entitate. La fel, relaţia P corespunde asocierii M:N între tipul de entitate
“angajat” şi tipul de entitate “proiect”(care nu apare explicit în acest exemplu) şi atributele acesteia se referă
numai la acest aspect (numărul de ore lucrate de un angajat la un anumit proiect).
Definiţia semantică a acestor relaţii este simplă şi concisă: “A este relaţia care conţine date despre
angajaţii instituţiei”; “P este relaţia care conţine numărul de ore lucrate de angajaţi la proiecte”.
În schimb, relaţia iniţială AP conţine atribute amestecate despre angajaţi şi proiectele la care aceştia
lucrează, iar definiţia ei semantică nu mai este atât de simplă şi clară: “AP este o relaţie care conţine date despre
angajaţii instituţiei şi câte ore lucrează la fiecare proiect”.
O abordare mai precisă a problemei definirii relaţiilor bazelor de date se poate face pe baza analizei
dependenţelor de date şi a normalizării relaţiilor.

5.2 DEPENDENŢE FUNCŢIONALE


O dependenţă funcţională - DF - (functional dependency) în relaţia cu schema R = {A1,A2,...An}
între două mulţimi de atribute X şi Y (care sunt submulţimi ale lui R) există dacă şi numai dacă, în orice stare a
relaţiei R, fiecărei valori a atributului (simplu sau compus) X îi corespunde o singură valoare a atributului
(simplu sau compus) Y.
O dependenţă funcţională este deci o constrângere între două submulţimi de atribute X şi Y ale unei
relaţii şi se notează X→Y. Ca exprimare, se mai spune că există o dependenţă funcţională de la X la Y, sau că
atributul Y este dependent funcţional de X.
Noţiunea de dependenţă funcţională este o extindere a noţiunilor de superchei şi chei ale relaţiilor: atât
supercheile (şi, implicit cheile) relaţiilor cât şi dependenţele funcţionale sunt constrângeri pe care trebuie să le
respecte valorile atributelor relaţiilor.
După cum s-a definit în Capitolul 2, o submulţime K a unei mulţimi de atribute R (care este schema
relaţiei cu acelaşi nume) este supercheie a relaţiei R dacă nu există două tupluri diferite în relaţia R care să aibă
aceleaşi valori ale atributelor din mulţimea K. Aceasta înseamnă că, dacă t1[K]=t2[K], atunci
t1[R]=t2[R], adică t1 şi t2 sunt identice şi reprezintă un singur tuplu în relaţia R.
Dependenţa funcţională X→Y este satisfăcută de relaţia R atunci când, pentru orice pereche de tupluri
t1 şi t2, t1[X] = t2[X] implică t1[Y] = t2[Y]. Aşadar, fiind dată o dependenţă funcţională X→Y în
relaţia cu schema R, atributul X este o supercheie a relaţiei dacă atributul determinat (Y) este chiar mulţimea R.
O dependenţă funcţională X→Y impune condiţia ca unei valori (x) a atributului determinant X să îi
corespundă o singură valoare (y) a atributului determinat Y. Adică, toate tuplurile care au valoarea (x) a
atributului X, trebuie să aibă aceeaşi valoare (y) a atributului Y. Dat fiind că, în cazul unei superchei, atributul
determinat este chiar mulţimea R a atributelor relaţiei, şi că într-o relaţie nu pot exista două sau mai multe tupluri
identice, înseamnă că nu pot exista două sau mai multe tupluri cu aceeaşi valoare (k) a supercheii; această
condiţie este cunoscută ca fiind condiţia de unicitate a supercheii.
Dat fiind că oricare valoare a supercheii este unică într-o relaţie, valorile atributelor determinate de
aceasta sunt absolut necesare şi unice şi nu produc redundanţa datelor. În schimb, fiecare pereche de valori
(x,y) ale atributelor X şi Y ale dependenţei funcţionale X→Y poate să apară de mai multe ori în aceeaşi relaţie,
indicând faptul că valoarea y este determinată în mod unic de valoarea x, dar acest lucru se repetă de mai multe
ori în relaţie, deci reprezintă o redundanţă a datelor.

3
Aceeaşi proprietate, de a determina funcţional mulţimea de atribute ale relaţiei R, o are şi orice cheie CK
(primară sau secundară) a relaţiei date (CK→R), dat fiind că o cheie este o supercheie cu proprietatea de
ireductibilitate.
Fiind dată o relaţie şi o mulţime de dependenţe funcţionale care trebuie să fie satisfăcute de orice stare a
relaţiei, o parte din dependenţele funcţionale pot fi determinate de chei ale relaţiei (şi acestea sunt constrângeri
implicite, care nu produc redundanţa datelor şi nici anomalii de actualizare a relaţiei şi sunt impuse automat de
sistemul de gestiune), iar altă parte pot fi determinate de alte atribute care nu sunt chei ale relaţiei (şi acestea sunt
constrângeri explicite care produc redundanţa datelor şi anomalii de actualizare a relaţiei, nu sunt impuse de
sistemul de gestiune şi necesită proceduri speciale pentru verificarea şi impunerea lor).
Această diferenţă de comportare a dependenţelor funcţionale în funcţie de tipul atributului determinant
(cheie sau non-cheie) face ca analiza lor să necesite cunoaşterea cheilor relaţiei. Cheile relaţiilor se pot preciza
explicit (şi atunci ele implică dependenţele funcţionale corespunzătoare) sau pot fi deduse din mulţimea
dependenţelor funcţionale stabilite de proiectant, folosind algoritmi de găsire a cheilor din mulţimea
dependenţelor funcţionale. Un astfel de algoritm va fi prezentat într-o secţiune următoare.
Tipuri de dependenţe funcţionale şi atribute. Dependenţele funcţionale pot fi parţiale sau totale. O
dependenţă funcţională X→Y este parţială (partial dependency) dacă există o submulţime proprie Z a mulţimii
X (adică Z ⊂ X) care determină funcţional pe Y (Z→Y). O dependenţă funcţională X→Y este totală (full
dependency), dacă nu există nici o submulţime proprie Z a lui X care să determine funcţional pe Y. Din această
definiţie rezultă că, dacă atributul X este simplu, dependenţa funcţională X→Y este totală.
Dependenţa funcţională X→Y (unde X ⊆ R, Y ⊆ R) este satisfăcută de orice relaţie R dacă Y ⊆ X. O
astfel de dependenţă funcţională se numeşte dependenţă funcţională trivială.
Dacă atributul (simplu sau compus) CK este o cheie candidată a relaţiei R, atunci, conform proprietăţii
de ireductibilitate a cheii candidate, dependenţa CK→R este totală, adică nu există o submulţime proprie CK’ a
lui CK care să prezinte proprietatea CK’→R.
Atributele care aparţin unei chei se numesc atribute prime, iar celelalte se numesc atribute neprime.
Exemple de dependenţe funcţionale. Se consideră relaţia AP definită mai sus, în care se pot stabili
următoarele dependenţe funcţionale, pe baza semnificaţiei atributelor acesteia:
a) IdAngajat→Nume
b) IdAngajat→Prenume
c) IdAngajat→Adresa
d) {IdAngajat,IdProiect}→Ore
Aceste dependenţe funcţionale specifică faptul că identificatorul angajatului determină în mod unic
numele, prenumele şi adresa acestuia (dependenţele a, b şi c) şi că numărul de identificare al angajatului şi
identificatorul proiectului determină în mod unic numărul de ore pe care le lucrează respectivul angajat la acel
proiect (dependenţa d). Cheia primară a relaţiei {IdAngajat, IdProiect} poate fi definită explicit, sau
poate fi dedusă din dependenţele funcţionale de mai sus.

5.2.1 MULŢIMI DE DEPENDENŢE FUNCŢIONALE


În mod obişnuit, proiectantul bazei de date specifică acele dependenţe funcţionale între atribute care
sunt evidente din punct de vedere semantic. În afara acestor dependenţe funcţionale, mai există un număr destul
de mare de dependenţe funcţionale care se menţin pentru orice stare a relaţiei, şi care se pot deduce din mulţimea
celor specificate, folosind regulile de deducere (inferenţă - inference rules) ale lui Armstrong. Aceste reguli sunt
următoarele:
1) Reflexivitatea (reflexivity): dacă Y ⊆ X, atunci X→Y. Prin această regulă se deduc numai
dependenţe funcţionale triviale, care nu aduc informaţii suplimentare, dar care sunt utile în
combinaţie cu alte reguli de inferenţă. Următoarele dependenţe funcţionale se deduc prin
reflexivitate în relaţia AP:
e) {IdAngajat,IdProiect}→IdAngajat
f) {IdAngajat,IdProiect}→IdProiect

2) Augmentarea (augmentation): dacă X→Y, atunci (X ∪ Z)→(Y ∪ Z). Următoarele dependenţele


funcţionale (g şi h) sunt deduse prin augmentare, pornind de la dependenţele funcţionale (a) şi
respectiv (b), augmentate cu mulţimea Z = {Nume} (şi tinând seama de faptul că Nume ∪
Nume = Nume):
g) {IdAngajat,Nume}→Nume

4
h) {IdAngajat,Prenume}→Prenume

3) Tranzitivitatea (transitivity): dacă X→Y şi Y→Z, atunci X→Z. De exemplu, din dependenţele (a) şi
(e) rezultă:
i) {IdAngajat,IdProiect}→Nume
Armstrong a demonstrat aceste trei reguli (demonstraţia este foarte simplă şi se bazează pe definiţia
dependenţelor funcţionale, [Arm74]) precum şi faptul că ele sunt suficiente pentru calculul tuturor dependenţelor
funcţionale pe care le implică o mulţime dată de dependenţe funcţionale. În afara acestor trei reguli de bază se
mai pot folosi şi alte reguli, care se pot deduce din acestea, şi anume:
4) Proiecţia (projection): dacă X→Y ∪ Z, atunci X→Y şi X→Z.
5) Reuniunea (union): dacă X→Y şi X→Z, atunci X→(Y ∪ Z).
6) Pseudo-tranzitivitatea (pseudo-transitivity): dacă X→Y şi (W ∪ Y)→Z, atunci (W ∪ X)→Z.
Reprezentarea grafică a dependenţelor funcţionale se poate face ca în figura 5.2 pentru relaţia AP.
Notaţiile folosite sunt uşor de înţeles.
Nume

Prenume
IdAngajat
Adresa
IdProiect
Ore
Fig. 5. 2. Dependenţele funcţionale ale relaţiei AP.

Închiderea unei mulţimi de dependenţe funcţionale (closure of a set of functional dependencies).


Fiind dată o mulţime F de dependenţe funcţionale, mulţimea tuturor dependenţelor funcţionale care sunt
implicate de F se numeşte închiderea mulţimii F şi se notează F+.
Ca exprimare, se mai spune că dependenţele funcţionale din F+ reprezintă consecinţa logică a
dependenţelor funcţionale din F. Mulţimea tuturor dependenţelor funcţionale F+ se poate deduce prin aplicarea
repetată a regulilor de inferenţă ale lui Armstrong asupra dependenţelor funcţionale din F.
De multe ori este necesar să se verifice dacă o anumită dependenţă funcţională X→Y poate fi dedusă
dintr-o mulţime dată F de dependenţe funcţionale. Pentru aceasta se poate afla închiderea F+ a mulţimii F şi se
testează dacă (X→Y)∈F+. Această metodă este, însă, destul de laborioasă, deoarece mulţimea F+ este o
mulţime de dimensiune mare, chiar pentru mulţimi F cu un număr mic de elemente. Exista si o metoda mai
rapida de a afla dacă o anumită dependenţă funcţională X→Y poate fi dedusă dintr-o mulţime dată F de
dependenţe funcţionale, care se bazează pe noţiunea de închidere (X+) a unui atribut X în raport cu o mulţime F
de dependenţe funcţionale.
Mulţimi echivalente de dependenţe funcţionale (equivalent sets of functional dependencies). Două
mulţimi de dependenţe funcţionale E şi F sunt echivalente dacă închiderile lor sunt egale (E+ = F+; acest lucru
înseamnă că: ∀DF ∈ E ⇒ DF ∈ F+ şi ∀DF ∈ F ⇒ DF ∈ E+).
Mulţime minimă de dependenţe funcţionale (minimal set of functional dependencies). O mulţime de
dependenţe funcţionale G este minimă dacă satisface următoarele condiţii: (a) membrul drept al oricărei
dependenţe funcţionale din G este un atribut simplu; adică, pentru orice dependenţă funcţională X→Y din G, Y
este un atribut simplu; (b) orice dependenţă funcţională din G este totală; (c) mulţimea G este ireductibilă, adică,
dacă se exclude o dependenţă funcţională din G, mulţimea rezultată H nu este echivalentă cu G (adică H+ ≠ G+).
Se poate observa că mulţimea dependenţelor FAP definite pentru relaţia AP este o mulţime minimă de
dependenţe funcţionale, deoarece satisface cele trei condiţii impuse.
Acoperirea minimă a unei mulţimi F de dependenţe funcţionale (minimal cover of a set F of
functional dependencies) este o mulţime minimă de dependenţe funcţionale G care este echivalentă cu F, adică
G+ = F+. S-a demonstrat că pot exista mai multe acoperiri minime ale unei mulţimi de dependenţe funcţionale şi
că întotdeauna se poate găsi cel puţin una dintre acestea folosind algoritmul de mai jos [Elm00].

5
5.2.1.1 Dependenţele funcţionale şi cheile relaţiilor
Aşa cum s-a mai arătat, în orice relaţie pot exista două categorii de dependenţe funcţionale:
• Dependenţe funcţionale determinate de cheile relaţiei; astfel de dependenţe funcţionale nu produc
redundanţa datelor şi nici anomalii de actualizare a relaţiei.
• Dependenţe funcţionale în care atributul determinant nu este o cheie a relaţiei; astfel de dependenţe
funcţionale produc redundanţa datelor şi anomalii de actualizare a relaţiei.
Constrângerile de cheie (primară sau secundară) sunt constrângeri implicite, conţinute în definiţia
relaţiei (constrângerile PRIMARY KEY sau UNIQUE din comanda CREATE TABLE) şi sunt verificate şi
impuse automat de sistemul de gestiune; proiectantul bazei de date nu trebuie să prevadă nimic suplimentar
pentru ca aceste constrângeri să fie satisfăcute de orice stare a relaţiei.
În schimb, dependenţele funcţionale în care atributul determinant nu este o cheie a relaţiei sunt
constrângeri explicite, care nu sunt verificate şi nici impuse de sistemul de gestiune. Verificarea şi impunerea
acestor dependenţe funcţionale se poate face numai procedural, prin triggere, proceduri stocate, sau funcţii
incluse în programele de aplicaţie.
De exemplu, în relaţia A, singurele dependenţe funcţionale sunt cele determinate de cheia primară a
relaţiei IdAngajat: IdAngajat→Nume, IdAngajat→Prenume, IdAngajat→Adresa. La fel, în relaţia P,
dependenţa funcţională {IdAngajat,IdProiect}→Ore este determinată de cheia primară a relaţiei.
Aceste dependenţe sunt evidente, rezultate din semnificaţia atributelor definite iniţial pentru relaţia AP (din care
provin relaţiile A şi P), dar pot fi şi deduse şi prin proiecţia mulţimii dependenţelor funcţionale ale relaţiei AP,
modalitate care va fi prezentată ulterior. Toate aceste dependenţe funcţionale sunt verificate şi impuse automat
de către SGBD la fiecare operaţie de actualizare a acestor relaţii şi nu necesită nici o procedură specială.
În relaţia AP, pe lângă dependenţa {IdAngajat,IdProiect}→Ore care este determinată de cheia
primară (şi este deci verificată automat de sistemul de gestiune), mai există dependenţele:
IdAngajat→Nume,IdAngajat→Prenume, IdAngajat→Adresa, care nu sunt determinate de o cheie a
relaţiei (IdAngajat nu este cheie în relaţia AP). Pentru astfel de dependenţe funcţionale trebuie să se prevadă
proceduri care să verifice şi să impună respectarea constrângerilor respective. Pentru relaţia AP, este necesar ca
la orice operaţie de actualizare a relaţiei să se verifice valorile care urmează să fie introduse (sau modificate) şi
să nu se admită doi sau mai mulţi angajaţi cu acelaşi număr de identificare dar cu nume, prenume sau adresă
diferite, ceea ce înseamnă impunerea dependenţelor funcţionale care nu sunt determinate de cheia relaţiei
(IdAngajat→Nume, IdAngajat→Prenume, IdAngajat→Adresa). Astfel de proceduri de verificare vor fi
prezentate într-o secţiune următoare.

5.2.2 DESCOMPUNEREA RELAŢIILOR


O descompunere D a schemei de relaţie R (relation schema decomposition) este formată din submulţimi
ale lui R (D = {R1,R2,...Rk}, unde R1 ⊂ R, R2 ⊂ R, ...Rk ⊂ R, şi R = R1 ∪ R2...∪ Rk). Relaţiile cu
schemele R1,R2,...Rk sunt proiecţii ale relaţiei R pe submulţimile de atribute corespunzătoare şi reprezintă
descompunerea relaţiei R.
Descompunerea unei relaţii, efectuată în scopul normalizării, nu poate fi făcută oricum, ci trebuie să fie
respectate anumite condiţii de conservare a semnificaţiei atributelor şi a dependenţelor dintre ele, aşa cum erau
definite în relaţia iniţială. În această secţiune şi în cele care urmează se va exprima în mod frecvent o schemă de
relaţie ca mulţime a atributelor sale, ceea ce permite notaţii şi definiţii mai simple. Din punct de vedere semantic,
descompunerea relaţiilor urmăreşte o separare a conţinutului de informaţie din relaţia dată, astfel încât fiecare
dintre relaţiile rezultate să reprezinte un singur tip de entitate sau o asociere între două tipuri de entităţi. Dintre
toate descompunerile posibile, numai o parte au proprietatea că pot reconstitui prin joncţiune naturală relaţia
iniţială R.
Ca terminologie, se poate considera strict descompunerea unei scheme de relaţie ca descompunere a
unei mulţimi de atribute (R) în submulţimi ale acesteia (R1,R2,...Rk), sau se poate considera descompunerea
relaţiei R prin proiecţia relaţiei R pe submulţimile de atribute corespunzătoare. Ambele expresii pot fi
considerate corecte deoarece, chiar dacă există o diferenţă de nuanţă între ele, este clar la ce se referă şi nu pot să
apară confuzii. O situaţie asemănătoare se poate observa şi la alte noţiuni legate de normalizare. De exemplu, se
poate folosi expresia de normalizare a schemei unei relaţii, prin descompunerea schemei de relaţie date, sau
normalizarea relaţiei, prin descompunerea relaţiei date.
Fiind dată o relaţie cu schema R şi mulţimea F a dependenţelor funcţionale ale acesteia, o
descompunere D a relaţiei R se numeşte descompunere reversibilă dacă are proprietăţile de joncţiune fără
pierdere de informaţie şi de conservare a dependenţelor funcţionale.

6
5.2.1.2 Proprietatea de joncţiune fără pierdere de informaţie
Fie r extensiunea (starea) unei relaţii cu schema R (adică r(R) este valoarea la un moment dat a
relaţiei cu schema R) şi ΠRi(r) proiecţia acestei relaţii pe mulţimea de atribute Ri ⊂ R. Descompunerea D =
{R1,R2,...Ri,..Rk} are proprietatea de joncţiune fără pierdere de informaţie - lossless join) dacă r
= ΠR1(r)>< ΠR2(r)...>< ΠRi(r)..>< ΠRk(r).
Proprietatea de joncţiune fără pierdere de informaţie (sau de conservare a informaţiei) a unei
descompuneri se referă la posibilitatea de a obţine aceeaşi valoare a relaţiei prin joncţiunea relaţiilor rezultate din
acea descompunere. Acest lucru înseamnă că, dacă prin joncţiunea proiecţiilor relaţiei date iniţial se obţine o
relaţie identică cu aceasta, descompunerea are proprietatea de joncţiune fără pierdere de informaţie.
Lipsa acestei proprietăţi se manifestă în mod paradoxal prin apariţia unor tupluri noi în relaţia obţinută
prin joncţiunea relaţiilor R1,R2,...Ri,...Rk, tupluri care nu existau în relaţia R. Aceste tupluri parazite
apar ca o pierdere de informaţie din relaţia R şi provin din pierderea unor constrângeri care existau în R şi care s-
au pierdut prin descompunerea D = {R1,R2,..Ri,..Rk}.
Teorema lui Ullman. Descompunerea D = {R1,R2} a unei relaţii R este o descompunere fără pierdere
de informaţie la joncţiune în raport cu mulţimea F a dependenţelor funcţionale, dacă şi numai dacă este
îndeplinită una din condiţiile: (a)((R1 ∩ R2)→(R1 − R2))∈ F+, sau (b) ((R1 ∩ R2)→(R2 − R1))∈ F+.
Această teoremă oferă o modalitate de descompunere a unei relaţii în două relaţii fără pierdere de
informaţie la joncţiune. Din teorema lui Ullman se vede că o descompunere a unei relaţii în două relaţii este fără
pierdere de informaţie la joncţiune dacă atributul comun al celor două relaţii rezultate (atributul R1 ∩ R2, pe care
se va executa joncţiunea naturală) determină funcţional una din diferenţele dintre mulţimile atributelor celor
două relaţii, (R1 − R2) sau (R2 − R1).
Din condiţiile impuse în teorema lui Ullman, se poate deduce cu uşurinţă că, dacă intersecţia atributelor
celor două relaţii rezultate este sau conţine o cheie a uneia dintre aceste relaţii, atunci descompunerea este fără
pierdere de informaţie la joncţiune. Într-adevăr, dacă (R1 ∩ R2) este o supercheie în R1, atunci acest atribut
determină funcţional orice submulţime de atribute din R1, inclusiv submulţimea de atribute (R1 − R2) adică
(R1 ∩ R2)→(R1 − R2). În mod asemănător se raţionează în situaţia în care (R1 − R2) este o supercheie în R2.
Reciproc, dacă descompunerea unei relaţii R în două scheme de relaţie R1 şi R2 respectă teorema lui
Ullman, atunci atributul comun al celor două relaţii rezultate (adică intersecţia R1 ∩ R2) este supercheie fie în
relaţia R1, fie în relaţia R2. Demonstraţia este foarte simplă. Dacă (R1 ∩ R2)→(R1 − R2), atunci, prin
augmentare cu (R1 ∩ R2) se obţine (R1 ∩ R2)→((R1 − R2)∪(R1 ∩ R2)), rezultă (R1 ∩ R2)→R1 şi deci
(R1 ∩ R2) este supercheie în relaţia R1. Un raţionament asemănător se poate face pentru cazul în care (R1 ∩
R2)→(R2 − R1) şi rezultă că atributul comun este supercheie în relaţia R2.
Revenind la exemplele prezentate în secţiunile precedente, se va analiza dacă descompunerile efectuate
(în mod intuitiv) prezintă proprietatea de joncţiune fără pierderi de informaţie. Pentru analiza descompunerii
relaţiei AP în relaţiile A şi P se calculează:
A ∩ P = {IdAngajat}, A − P = {Nume,Prenume,Adresa}
P − A = {IdProiect,Ore}
Din dependenţele funcţionale IdAngajat→Nume,IdAngajat→Prenume,IdAngajat→
Adresa se poate deduce dependenţa funcţională IdAngajat→{Nume,Prenume,Adresa} prin regula
de reuniune a dependenţelor funcţionale. Rezultă că ((A ∩ P)→(A − P))∈ F+, deci această descompunere
îndeplineşte prima condiţie din teorema lui Ullman şi este o descompunere fără pierdere de informaţie la
joncţiune.

5.2.1.3 Proprietatea de conservare a dependenţelor funcţionale


Informal, descompunerea unei relaţii cu schema R în relaţiile cu schemele R1,R2,...Rk prezintă
proprietatea de conservare a dependenţelor funcţionale definite de mulţimea F (dependency-preservation) dacă
oricare din dependenţele din F se regăseşte sau poate fi dedusă din dependenţele funcţionale ale relaţiilor
R1,R2,...Rk.
Conservarea dependenţelor funcţionale este necesară pentru ca verificarea şi impunerea constrângerilor
definite de dependenţele funcţionale respective să poată fi efectuată la nivelul unei singure relaţii.
Dacă una sau mai multe dependenţe funcţionale se pierd prin descompunerea unei relaţii, atunci
constrângerile (definite de dependenţele funcţionale respective) pot fi impuse numai dacă se calculează
joncţiunea naturală a relaţiilor rezultate. O astfel de modalitate de verificare este posibilă, dar ineficientă

7
(operaţia de joncţiune fiind o operaţie costisitoare ca timp de execuţie), şi de aceea, în proiectarea bazelor de date
se folosesc, pe cât posibil, descompuneri care conservă dependenţele funcţionale.
Înainte de a da o definiţie formală a proprietăţii de conservare a dependenţelor funcţionale, se va defini
proiecţia unei mulţimi de dependenţe funcţionale pe o relaţie.
Proiecţia unei mulţimi de dependenţe funcţionale. Fiind dată mulţimea dependenţelor funcţionale F
pe relaţia cu schema R, şi o descompunere D = {R1,R2,..Ri,...Rk} a lui R, proiecţia lui F pe Ri (unde 1
≤ i ≤ k), notată ΠRi(F), este o mulţime de dependenţe funcţionale X→Y din F+, astfel încât X ⊆ Ri şi Y ⊆ Ri.
Această definiţie semnifică faptul că o dependenţă funcţională aparţine proiecţiei lui F pe Ri dacă atât
atributele din partea stângă cât şi atributele din partea dreaptă ale dependenţei funcţionale date aparţin relaţiei
Ri. De asemenea se observă că dependenţele funcţionale ale proiecţiei lui F pe Ri pot să aparţină nu neapărat lui
F, ci închiderii lui F (adică mulţimii F+).
Pe baza acestor observaţii, se poate da definiţia proprietăţii de conservare a dependenţelor funcţionale.
Conservarea dependenţelor funcţionale. O descompunere D = {R1,R2, ...Ri,...Rk} a unei
relaţii cu schema R prezintă proprietatea de conservare a dependenţelor funcţionale definite de mulţimea F,
dacă reuniunea proiecţiilor lui F pe relaţiile Ri (unde 1 ≤ i ≤ k) este echivalentă cu F, adică: ((ΠR1(F)) ∪
(ΠR2(F)) ∪... ∪ (ΠRk(F)))+ = F+ .
Ca exemplu de descompunere cu conservarea dependenţelor funcţionale, se analizează descompunerea
prezentată la începutul acestui capitol, a relaţiei AP = {IdAngajat,Nume,Prenume,Adresa,IdProiect,
Ore} în relaţiile A = {IdAngajat,Nume,Prenume,Adresa} şi P = {IdAngajat,IdProiect, Ore}.
Mulţimea FAP a dependenţelor funcţionale definite pe AP este:
FAP = {IdAngajat→Nume,IdAngajat→Prenume,
IdAngajat→Adresa,{IdAngajat,IdProiect}→Ore}

Proiecţia mulţimii FAP pe relaţia A este:


ΠA(FAP)={IdAngajat→Nume,IdAngajat→Prenume,IdAngajat→Adresa}
deoarece toate atributele implicate de aceste dependenţe funcţionale aparţin relaţiei A: IdAngajat ∈ A,Nume
∈ A,Prenume ∈ A,Adresa ∈ A.
Proiecţia mulţimii FAP pe relaţia P este:
ΠP(FAP)={{IdAngajat,IdProiect}→Ore}

deoarece toate atributele implicate de această dependenţă funcţională aparţin relaţiei P: IdAngajat ∈
P,IdProiect ∈ P,Ore ∈ P.
Dat fiind că ΠA(FAP)∪ ΠP(FAP) = FAP, rezultă că această descompunere conservă dependenţele
funcţionale date.
Nu întotdeauna este posibilă efectuarea unei descompuneri cu conservarea tuturor dependenţelor
funcţionale. Dacă, prin descompunerea unei relaţii, se pierd una sau mai multe dependenţe funcţionale,
constrângerile impuse de acestea nu mai pot fi verificate la nivelul nici uneia din relaţiile rezultate. În această
situaţie, procedurile de verificare şi impunere a respectării unor astfel de dependenţe funcţionale trebuie să
efectueze mai întâi joncţiunea relaţiilor, pentru a obţine o relaţie în care să se poată verifica acele dependenţe. Un
astfel de exemplu va fi prezentat în secţiunea 5.2.6, dedicată formei normale Boyce-Codd.
Cele două proprietăţi ale unei descompuneri, conservarea informaţiei şi conservarea dependenţelor
funcţionale sunt independente. Este posibil ca o descompunere să fie fără pierdere de informaţie, dar să nu
conserve dependenţele funcţionale, sau invers.

5.2.3 PRIMA FORMA NORMALĂ A RELAŢIILOR (FN1)


O relaţie este normalizată în prima formă normală (FN1) dacă fiecare atribut ia numai valori atomice
şi scalare din domeniul său de definiţie.
Caracterul de atomicitate se referă la faptul că valoarea unui atribut are semnificaţie numai în ansamblul
ei şi nu permite descompunerea în componente care să poată fi manevrate separat. Chiar dacă tipul de date peste
care este definit domeniul unui atribut este reprezentat prin mai multe componente, acestea nu au semnificaţie
luate individual. Valoarea scalară a unui atribut se referă la faptul că un atribut nu poate avea decât o valoare (din
domeniul de definiţie pe care este definit) pentru fiecare tuplu.
Sistemele SGBD relaţionale nu admit relaţii care să nu fie cel puţin în prima formă normală, dar
proiectarea relaţiilor normalizate în prima formă normală este simplă şi întotdeauna posibilă, şi a fost deja
prezentată în Capitolul 2.

8
5.2.4 A DOUA FORMĂ NORMALĂ A RELAŢIILOR (FN2)
O relaţie este normalizată în a doua formă normală (FN2) în raport cu o mulţime de dependenţe
funcţionale F, dacă este în FN1 şi dacă în F+ nu există nici o dependenţă funcţională parţială a unui atribut
neprim faţă de o cheie a relaţiei.
Din această definiţie rezultă că, dacă orice cheie a unei relaţii este formată dintr-un singur atribut,
relaţia este în FN2. Acest lucru este evident, deoarece orice dependenţă funcţională faţă de un atribut simplu este
o dependenţă funcţională totală.
De asemenea, este evident faptul că o relaţie compusă din două atribute este în FN2, deoarece, fie cheia
este formată din ambele atribute şi atunci nu există atribute neprime, fie cheia este formată dintr-unul din atribute
iar dependenţa funcţională a celuilalt atribut (care este atribut neprim) faţă de cheie este totală.
Ca exemplu de normalizare în FN2, se va relua relaţia AP prezentată la începutul acestui capitol (fig.
5.4). Schema relaţiei este: AP(IdAngajat,Nume, Prenume,Adresa,IdProiect,Ore), iar mulţimea
dependenţelor funcţionale FAP stabilite pe baza semnificaţiei atributelor este cea specificată deja, adică:
FAP = {IdAngajat→Nume,IdAngajat→Prenume,
IdAngajat→Adresa,{IdAngajat,IdProiect}→Ore}

PK Nume

IdAngajat
Ore Prenume
IdProiect
Adresa
a
Nume PK
IdAngajat
IdAngajat Prenume Ore
IdProiect
Adresa
b c

Fig. 5.4. a - Dependenţe funcţionale în relaţia AP (AP nu este în FN2);


b, c -descompunerea relaţiei AP în relaţiile A şi P.
Cheia primară este {IdAngajat,IdProiect} şi se poate deduce din mulţimea dependenţelor
funcţionale. Această relaţie este în FN1, deoarece toate atributele iau valori atomice şi scalare. Atributele prime
sunt IdAngajat, IdProiect iar atributele neprime sunt Nume,Prenume,Adresa,Ore. Pentru a
verifica dacă relaţia este în FN2 trebuie să fie testat tipul dependenţei funcţionale (totală sau parţială) a fiecărui
atribut neprim faţă de cheia primară (care este singura cheie a relaţiei în acest exemplu).
Se va testa pentru început tipul dependenţei funcţionale a atributului Nume faţă de cheia primară a
relaţiei, adică {IdAngajat,IdProiect}→Nume. Această dependenţă există în virtutea proprietăţii cheii
primare. De altfel, ea se poate deduce din dependenţele funcţionale ale relaţiei, prin aplicarea regulilor de
inferenţă ale lui Armstrong. Conform proprietăţii de reflexivitate, există dependenţa funcţională
{IdAngajat,IdProiect}→IdAngajat, şi aplicând regula de tranzitivitate între această dependenţă
funcţională şi dependenţa funcţională IdAngajat→Nume, rezultă {IdAngajat,IdProiect}→Nume.
Dependenţa funcţională {IdAngajat,IdProiect}→Nume este parţială, datorită faptului că
submulţimea {IdAngajat} a atributului compus cheie primară determină funcţional atributul Nume:
IdAngajat→Nume. Rezultă că relaţia AP nu este în FN2. Redundanţa datelor şi anomaliile de actualizare pe
care acestea le produc au fost descrise la începutul capitolului. Tot în această parte s-a arătat că relaţia AP se
poate descompune în relaţiile A(IdAngajat,Nume,Prenume,Adresa) şi P(IdAngajat,IdProiect,Ore),
care nu mai prezintă redundanţă a datelor şi anomalii de actualizare a tuplurilor. Pentru a determina proprietăţile
acestei descompuneri se studiază cele două relaţii rezultate A şi P.
Mulţimea dependenţelor funcţionale din relaţia A este FA=ΠA(FAP)={IdAngajat→Nume,
IdAngajat→Prenume,IdAngajat→Adresa}, din care se deduce cheia primară IdAngajat. În toate
dependenţele funcţionale din FA atributele neprime Nume,Prenume,Adresa sunt total dependente faţă de
cheia primară a relaţiei, deci relaţia A este în FN2.

9
Mulţimea dependenţelor funcţionale din relaţia P este FP = ΠP(FAP) = {{IdAngajat,IdProiect}
→Ore}, din care se deduce cheia primară {IdAngajat,IdProiect}. În dependenţa funcţională din FP
atributul neprim Ore este total dependent faţă de cheia primară a relaţiei, deci relaţia P este în FN2.
În secţinile anterioare s-a demonstrat că descompunerea relaţiei AP în relaţiile A şi P are proprietatea de
conservare a informaţiei (secţiunea 5.2.2.1) şi proprietatea de conservare a dependenţelor funcţionale (secţiunea
5.2.2.2), aşadar această descompunere este o descompunere reversibilă în relaţii FN2.

5.2.5 A TREIA FORMĂ NORMALĂ A RELAŢIILOR (FN3)


O relaţie este normalizată în a treia formă normală (FN3) în raport cu o mulţime de dependenţe
funcţionale F dacă este în FN2 şi dacă în F+ nu există nici o dependenţă funcţională netrivială a unui atribut
neprim faţă de alt atribut neprim al relaţiei.
Orice relaţie formată din două atribute este în FN3 deoarece ea se află în FN2 (s-a demonstrat în
secţiunea precedentă), şi nu poate exista nici un atribut neprim care să determine funcţional un alt atribut neprim,
deoarece o relaţie cu două atribute nu poate avea decât cel mult un atribut neprim.
Ca exemplu de normalizare a unei relaţii în a treia formă normală, se consideră schema relaţiei
AFS(IdAngajat,Nume,Prenume,Adresa,Func-tie,Salariu), cu mulţimea dependenţelor funcţionale
FAFS = {IdAngajat→ Nume, IdAngajat→Prenume, IdAngajat→Functie, Functie→Salariu} (fig.
5.5). Cheia primară a relaţiei este atributul IdAngajat, şi ea poate fi dedusă din mulţimea FAFS a dependenţelor
funcţionale.
Se consideră că fiecare atribut ia numai valori atomice şi scalare, deci relaţia este în FN1. Primele patru
dependenţe funcţionale sunt dependenţele funcţionale totale ale unor atribute neprime faţă de cheia primară a
relaţiei, deci relaţia este în FN2.
Dependenţa funcţională (Functie→Salariu) semnifică faptul că în instituţia respectivă toţi salariaţii
cu aceeaşi funcţie au acelaşi salariu (adică funcţia determină salariul, ceea ce este plauzibil). Această dependenţă
funcţională a atributului neprim Salariu faţă de alt atribut neprim (Functie), arată că relaţia nu este în a
treia formă normală (FN3).

Nume
Nume
Prenume
Prenume
IdAngajat
IdAngajat Adresa
Adresa
Functie
Functie
b

Salariu Functie Salariu

a c
Fig. 5.5. a- Dependenţele funcţionale ale relaţiei AFS (AFS nu este în FN3);
b, c - descompunerea relaţiei AFS în relaţiei AF şi FS.
Chiar dacă relaţia AFS este în FN2, în aceasta încă mai există redundanţă a datelor, deoarece valoarea
salariului corespunzător unei funcţii se înregistrează de mai multe ori, pentru fiecare salariat care deţine acea
funcţie. Această redundanţă provoacă anomalii de inserare, ştergere şi actualizare a tuplurilor. De exemplu, nu se
poate înregistra valoarea salariului corespunzător unei anumite funcţii, dacă nu se înregistrează cel puţin un
salariat cu acea funcţie. Aceasta este anomalia de inserare. Ca anomalie de ştergere, se poate observa că, dacă se
şterg tuplurile corespunzătoare tuturor salariaţilor care deţin o anumită funcţie, atunci se pierde informaţia
referitoare la salariul corespunzător funcţiei respective. Anomalii de actualizare apar atunci când se actualizează
valoarea salariului unui angajat: dacă se modifică salariul fără să se modifice corespunzător şi funcţia, atunci pot
exista în relaţie două sau mai multe tupluri care au aceeaşi valoare a atributului Functie, dar valori diferite ale
atributului Salariu, deci nu este respectată dependenţa funcţională Functie→Salariu.
Astfel de redundanţe şi anomaliile de actualizare pe care le provoacă se pot elimina dacă se
descompune relaţia în două (sau mai multe) relaţii, care să nu conţină date redundante.
Relaţia AFS se poate descompune în relaţiile AF(IdAngajat,Nume, Prenume,Adresa,Functie)
şi FS(Functie,Salariu). Se poate verifica uşor că fiecare din aceste relaţii se află în FN3.

10
Dependenţele funcţionale ale relaţiei AF reprezintă proiecţia dependenţelor funcţionale date iniţial
(FAFS) pe relaţia AF: FAF = ΠAF(FAFS)= {IdAngajat→Nume,IdAngajat→Prenume,IdAngajat→Functie}.
Cheia primară a relaţiei AF este IdAngajat, şi se poate deduce uşor din dependenţele funcţionale ale relaţiei AF
(FAF). Se observă că toate dependenţele funcţionale ale relaţiei AF determinate de cheia primară sunt totale şi că
nu există nici o dependenţă a unui atribut neprim faţă de alt atribut neprim, deci relaţia AF este în FN3.
Dependenţele funcţionale ale relaţiei FS reprezintă proiecţia dependenţelor funcţionale date iniţial
(FAFS), pe relaţia FS: FFS = ΠFS(FAFS)={Functie→Salariu}. Cheia primară a relaţiei este atributul Functie
şi se deduce cu uşurinţă din mulţimea FFS a dependenţelor funcţionale ale acestei relaţii. Singura dependenţă
funcţională a relaţiei FS este o dependenţă funcţională totală determinată de cheia primară a relaţiei, deci relaţia
FS este în FN3. Pentru verificarea proprietăţii de joncţiune fără pierdere de informaţie se calculează AF ∩ FS =
{Functie} şi FS − AF = {Salariu}. Deoarece dependenţa funcţională (Functie→Salariu)∈ FAFS, rezultă
(conform teoremei lui Ullman) că descompunerea relaţiei AFS în relaţiile AF şi FS este o descompunere fără
pierdere de informaţie la joncţiune.
Pentru verificarea conservării dependenţelor funcţionale, se calculează mai întâi reuniunea
dependenţelor funcţionale ale relaţiilor AF şi FS:
FAF ∪ FFS = {IdAngajat→Nume,IdAngajat→Prenume,
IdAngajat→Functie,Functie→Salariu}
Se observă că FAFS = FAF ∪ FFS, deci descompunerea realizată conservă dependenţele funcţionale
stabilite iniţial. Aşadar, descompunerea relaţiei AFS în relaţiile AF şi FS are atât proprietatea de conservare a
informaţiei cât şi proprietatea de conservare a dependenţelor funcţionale, deci este o descompunere reversibilă.
S-a demonstrat că orice schemă de relaţie poate fi descompusă reversibil în scheme de relaţii FN3 şi
există un astfel de algoritm de descompunere.

5.2.6 FORMA NORMALĂ BOYCE-CODD (FNBC)


O relaţie cu schema R este în forma normală Boyce-Codd (FNBC) în raport cu o mulţime F de
dependenţe funcţionale dacă este în FN1 şi dacă, pentru orice dependenţă funcţională netrivială X→Y din F+, X
este o cheie a relaţiei R.
Se poate observa asemănarea dintre formele normale FN3 şi FNBC, ambele impunând condiţia ca
atributul care determină funcţional alte atribute să fie o cheie a relaţiei. Forma normală Boyce-Codd este mai
restrictivă decât FN3, deoarece în FNBC se impune această condiţie tuturor atributelor, prime sau neprime, pe
câtă vreme în FN3 condiţia se impune numai atributelor neprime. Este evident faptul că o relaţie în FNBC este,
de asemenea în FN3, dar o relaţie în FN3 poate să fie sau nu în FNBC.
Orice relaţie formată din două atribute este în FNBC. Explicaţia este similară cu explicaţia dată pentru
faptul că orice relaţie formată din două atribute este în FN2 sau în FN3.

5.2.7 ALGORITMI DE DESCOMPUNERE A RELAŢIILOR


În general, scopul normalizării este acela de a obţine relaţii într-o formă de normalizare cât mai
avansată, care elimină toate sau majoritatea anomaliilor de actualizare.
Orice relaţie se poate descompune în relaţii FN2, FN3 sau FNBC fără pierdere de informaţie la
joncţiune cu ajutorul unui algoritm care este prezentat şi demonstrat în continuare. Deşi acest algoritm nu
garantează conservarea dependenţelor funcţionale, în multe situaţii este posibil ca să se obţină o descompunere
care să prezinte şi această proprietate.
O descompunere care să asigure atât conservarea informaţiei cât şi a dependenţelor funcţionale nu poate
garanta decât obţinerea unor relaţii în FN3 şi un astfel de algoritm (prezentat in manual). În multe situaţii, cu
acest algoritm se pot obţine chiar relaţii FNBC, dar forma normală garantată este doar FN3.

5.2.8 VERIFICAREA ŞI IMPUNEREA CONSTRÂNGERILOR EXPLICITE


Analiza normalizării relaţiilor trebuie să fie realizată pentru orice proiect de baze de date, pentru a
asigura funcţionarea corectă a acesteia. După ce se decide forma normală cea mai potrivita a unei relaţii, este
necesar să se prevadă procedurile de verificare a tuturor constrângerilor explicite rămase.
Dacă relaţia se păstrează într-o formă de normalizare mai redusă, atunci trebuie să se prevadă proceduri
de verificare şi impunere a dependenţelor de date care nu sunt determinate de cheile relaţiei (constrângeri
explicite).
Dacă prin descopmunerea unei relaţii se pierde o dependenţă funcţională, aceasta poate fi impusă
explicit printr-o procedură stocată sau o funcţie în programul de aplicaţie, care execută joncţiunea între relaţiile
rezultate şi impune constrângerea respectivă.

11
5.3 DEPENDENŢE MULTIVALORICE
O dependenţă multivalorică - DMV- (multivalued dependency) X→→Y specificată pe o relaţie cu
schema R = {X,Y,Z} stabileşte următoarele constrângeri pe care trebuie să le respecte orice stare r a relaţiei
R: dacă există două tupluri t1 şi t2 în r astfel ca t1[X] = t2[X]= x, atunci vor exista şi tuplurile t3 şi t4
cu următoarele proprietăţi:
• t3[X] = t4[X] = t1[X] = t2[X] = x;
• t3[Y] = t1[Y] = y1 şi t4[Y] = t2[Y] = y2 ;
• t3[Z] = t2[Z] = z2 şi t4[Z] = t1[Z] = z1 .
Datorită simetriei egalităţilor de mai sus, se poate spune că, dacă într-o relaţie R există dependenţa
multivalorică X→→Y, atunci există şi dependenţa X→→Z, unde Z = R−(X∪Y).
Definiţia de mai sus arată că, fiind dată o valoare particulară a atributului X, mulţimea de valori ale lui
Y determinate de această valoare a lui X este complet determinată numai de X şi nu depinde de valorile
atributelor rămase (Z) ale relaţiei. De aici rezultă că, atunci când două tupluri au valori distincte ale atributului Y
dar aceeaşi valoare a atributului X, aceste valori ale lui Y trebuie să fie repetate pentru orice valoare distinctă a
lui Z care apare pentru această valoare a lui X.
O relaţie cu schema R este în a patra formă normală (FN4) în raport cu o mulţime F de dependenţe
funcţionale şi multivalorice dacă este în FN1 şi dacă, pentru orice dependenţă multivalorică netrivială X→→Y
din F+, X este cheie a relaţiei R.
Se poate observa asemănarea acestei definiţii cu definiţia FNBC: pentru FNBC se impun restricţii
dependenţelor funcţionale, iar pentru FN4 se impun restricţii dependenţelor multivalorice. Dar, dat fiind că o
dependenţă funcţională este un caz particular al unei dependenţe multivalorice, dacă o schemă de relaţie respectă
condiţia de FN4, atunci înseamnă că ea respectă şi condiţia de FNBC. Restricţiile impuse se pot rezuma la faptul
că într-o relaţie nu trebuie să existe decât dependenţe determinate de chei ale relaţiei.

5.4 DEPENDENŢE DE JONCŢIUNE ŞI A CINCEA FORMĂ NORMALĂ


Teorema lui Ullman, ca şi generalizarea acesteia pentru dependenţele multivalorice, precizează
condiţiile de descompunere fără pierdere de informaţie la joncţiune a unei relaţii R în două relaţii R1 şi R2.
Există totuşi situaţii în care nu se poate efectua descompunerea fără pierdere de informaţie la joncţiune a unei
relaţii în două relaţii, dar se poate efectua în trei sau mai multe relaţii. Astfel de cazuri sunt studiate pe baza
dependenţelor de joncţiune şi a celei de-a cincea forme normale (FN5). Este de reţinut că astfel de cazuri sunt
foarte rare şi, în acelaşi timp, foarte dificil de studiat.
Dependenţă de joncţiune (join dependency). Fie o relaţie cu schema R şi R1,R2,...Rk submulţimi
de atribute ale mulţimii R, unde R1 ∪ R2 ∪...∪ Rk = R. Se spune că există o dependenţă de joncţiune pe R,
notată *(R1,R2,...Rk), dacă şi numai dacă orice stare r a relaţiei R este egală cu joncţiunea proiecţiilor
relaţiei pe submulţimile R1,R2,..Rk, adică r = ΠR1(r)>< ΠR2(r)...>< ΠRk(r).
Cu alte cuvinte, *(R1,R2,...Rk) este o dependenţă de joncţiune pe R dacă şi numai dacă
descompunerea lui R în proiecţiile pe R1,R2,...Rk este fără pierdere de informaţie. Se poate spune că relaţia
R este k-decompozabilă fără pierdere de informaţie dacă există o dependenţă de joncţiune *(R1,R2,...Rk).
Cazul k = 1 reprezintă o dependenţă de joncţiune trivială, deoarece o astfel de descompunere este
posibilă pentru orice schemă de relaţie şi nu reprezintă nici o constrângere asupra relaţiei.
Cazul k = 2 al unei dependenţe de joncţiune este o dependenţă multivalorică. Într-adevăr, s-a
demonstrat că o relaţie în care există o dependenţă multivalorică se poate descompune fără pierdere de
informaţie în două relaţii, deci o dependenţă multivalorică îndeplineşte condiţia de dependenţă de joncţiune cu
k = 2. Cu alte cuvinte, o dependenţă multivalorică X→→Y în relaţia R reprezintă o dependenţă de joncţiune
*(XY,XZ), unde Z = R–(X ∪ Y).
Pentru cazul k > 2 dependenţele de joncţiune nu mai sunt echivalente cu dependenţele multivalorice şi,
din nefericire, dependenţele de joncţiune sunt mult mai dificil de identificat şi nici nu există reguli de deducere
(inferenţă) care să genereze toate dependenţele de joncţiune pornind de la o mulţime dată.
Datorită acestor aspecte, analiza dependenţelor de joncţiune are un pronunţat caracter intuitiv. Aşa cum
o dependenţă multivalorică permite reprezentarea în aceeaşi relaţie a două asocieri independente, o dependenţă
de joncţiune permite reprezentarea în aceeaşi relaţie a unui număr de k asocieri independente (k > 2).

12
A cincea formă normală (FN5). O relaţie cu schema R este în a cincea formă normală (FN5) în raport
cu o mulţime F de dependenţe funcţionale, multivalorice sau de joncţiune dacă este în FN1 şi dacă, pentru orice
dependenţă de joncţiune *(R1,R2,... Ri,...Rk) din F+, Ri (unde 1 ≤ i ≤ k) este o cheie a relaţiei R.
Având în vedere faptul că o dependenţă multivalorică este un caz special de dependenţă de joncţiune,
iar o dependenţă funcţională este un caz special de dependenţă multivalorică, se poate afirma că o relaţie care
este în FN5, este implicit în FN4, deci şi în FNBC, ş.a.m.d.
Se poate observa că orice relaţie poate fi transformată în relaţii FN5 printr-o descompunere fără
pierdere de informaţie.

5.5 DECIZII DE NORMALIZARE A RELAŢIILOR


La proiectarea unei baze de date, proiectantul defineşte schemele relaţiilor şi constrângerile de
integritate care trebuie să fie satisfăcute de orice stare a relaţiilor (constrângeri de domeniu, constrângeri de
tuplu, constrângeri de integritate referenţială, dependenţe de date, aserţiuni).
Normalizarea relaţiilor are ca scop transformarea relaţiilor definite iniţial în alte relaţii, astfel încât toate
(sau majoritatea) dependenţele de date impuse să fie păstrate, dar ele să fie determinate de chei ale relaţiilor nou
obţinute.
Acest deziderat al operaţiei de normalizare provine din faptul că dependenţele faţă de cheile relaţiei sunt
constrângeri implicite care nu produc redundanţă a datelor, nici anomalii de actualizare şi sunt verificate automat
de SGBD atunci când relaţia este actualizată, în timp ce dependenţele care nu sunt determinate de chei sunt
constrângeri explicite, care produc redundanţa datelor şi anomalii de actualizare. Aceste din urmă dependenţe
(care nu sunt determinate de cheile relaţiei) nu sunt verificate automat de SGBD şi, pentru evitarea unora din
anomaliile produse, sunt necesare proceduri speciale de calcul (triggere, proceduri stocate sau funcţii în
programele de aplicaţii).
Normalizarea relaţiilor se realizează prin descompunerea acestora şi la fiecare pas de descompunere
trebuie să se verifice conservarea informaţiei şi conservarea dependenţelor de date. Descompunerile care nu
conservă informaţia sunt inacceptabile, dar se pot admite descompuneri care nu conservă dependenţele de date.
Dacă se efectuează o descompunere care nu conservă dependenţele funcţionale, atunci pentru fiecare dependenţă
pierdută trebuie să se prevadă proceduri care să verifice şi să impună constrângerile prevăzute de aceasta. Dacă
o relaţie se păstrează într-o formă de normalizare mai redusă, atunci trebuie să se prevadă proceduri de verificare
a dependenţelor de date care nu sunt determinate de chei ale relaţiei.
Normalizarea relaţiilor asigură un proiect al bazei de date mai concis şi mai portabil şi, de aceea, ca
regulă generală, se consideră că a normaliza este avantajos, chiar dacă normalizarea în sine nu este o garanţie că
s-a realizat cel mai bun model al realităţii modelate. Mai mult, cu cât nivelul de normalizare creşte, cu atât se
obţin mai multe relaţii cu grad mai mic (cu număr mai mic de atribute) şi pentru realizarea fiecărei interogări
sunt necesare mai multe operaţii de joncţiune, ceea ce face ca timpul de execuţie a interogărilor să crească.
În general, se recomandă ca relaţiile asupra cărora se efectuează operaţii de actualizare frecvente să fie
normalizate într-o formă normală cât mai avansată, iar relaţiile asupra cărora se efectuează interogări frecvente
pot fi păstrate într-o formă de normalizare mai redusă. O astfel de soluţie, de a păstra (sau de a aduce) unele
relaţii într-o formă de normalizare mai redusă se numeşte “denormalizare”, şi se efectuează cu scopul de a obţine
performanţe cât mai ridicate la execuţia unor interogări.
Proiectarea bazelor de date pornind de la diagrama Entitate-Asociere conduce, în general, la relaţii
normalizate, deoarece:
• Relaţiile corespunzătoare mulţimilor de entităţi sunt, de regulă, relaţii normalizate, dat fiind că
toate atributele descriu tipul de entitate respectiv.
• Relaţiile de asociere binară, care conţin două chei străine care referă cheile primare din cele două
relaţii pe care le asociază, rezultă, de asemenea, ca relaţii normalizate.
• Relaţiile care modelează asocierile multiple pot să rezulte nenormalizate, aşa cum s-a putut observa
din ultimele două secţiuni (5.3 şi 5.4), şi necesită operaţii de normalizare suplimentare.
Aspectele teoretice şi exemplele prezentate în acest capitol au scopul de a convinge proiectanţii şi
programatorii din domeniul bazelor de date că este practic imposibil să se obţină o funcţionare corectă a unui
sistem de baze de date fără ca să se facă analiza normalizării relaţiilor. Fără analiza normalizării poate exista
oricând redundanţă a datelor care, mai devreme sau mai târziu, se va manifesta ca o inconsistenţă a datelor, a
cărei cauză este greu de identificat şi de localizat.

13
6. GESTIUNEA TRANZACŢIILOR
ŞI REFACEREA BAZELOR DE DATE

În mod obişnuit, un sistem SGBD deserveşte mai mulţi utilizatori, care accesează concurent datele din
tabele. Accesul concurent al utilizatorilor este asigurat prin capacitatea de multiprogramare a sistemului de
operare al calculatorului gazdă, care permite execuţia concurentă a mai multor procese. Execuţia concurentă a
mai multor procese poate avea loc atât într-un sistem uniprocesor, prin partajarea (împărţirea) timpului de
execuţie al procesorului între mai multe procese, cât şi într-un sistem multiprocesor în care mai multe procese
pot fi executate în mod real simultan, pe mai multe procesoare ale sistemului. Indiferent de numărul de
procesoare ale sistemului, accesul concurent al mai multor utilizatori la datele memorate în tabelele unei baze de
date necesită tehnici de menţinere a consistenţei (corectitudinii) şi a siguranţei datelor memorate.
Menţinerea consistenţei şi a siguranţei bazelor de date în situaţia în care mai mulţi utilizatori le
accesează concurent şi în condiţiile în care pot să apară erori de funcţionare (defecte) ale sistemului de calcul se
bazează pe conceptul de tranzacţie care va fi prezentat în secţiunea următoare.

6.1 TRANZACŢII
O tranzacţie (transaction) este o unitate logică de prelucrare indivizibilă (atomică) a datelor unei baze
de date prin care se asigură consistenţa acesteia.
În principiu, orice execuţie a unui program care accesează o bază de date poate fi considerată o
tranzacţie, dacă baza de date este într-o stare consistentă atât înainte cât şi după execuţie. O tranzacţie trebuie să
asigure consistenţa bazei de date indiferent dacă a fost executată individual sau concurent cu alte tranzacţii
precum şi în condiţiile în care au apărut defecte ale sistemului hardware în cursul execuţiei tranzacţiei.
Se va studia problema consistenţei bazelor de date pe exemplul unui sistem de rezervare a locurilor la
curse aeriene. Un număr mare de agenţi de vânzări vor accesa relaţiile care memorează datele de rezervare şi
vânzare a biletelor de avion. De exemplu, vor exista relaţiile:
CURSE(IdCursa,AeroportPlecare,AeroportSosire,Data, NrLocuriLibere)
PASAGERI(IdPasager,Nume,Prenume,Adresa, NrCreditCard)
REZERVARI(IdRezervare,IdPasager,IdCursa)
FACTURI(IdFactura,IdPasager,DataFacturarii,Pret)
Cheile primare şi străine au fost marcate conform convenţiilor care au mai fost folosite şi în secţiunile
precedente, iar semnificaţia atributelor acestor relaţii este destul de clar exprimată chiar prin numele lor. Detalii
ca: tipul locului rezervat (turist, business, etc), reduceri de plată a biletului (bilet copil, etc.), mai multe rezervări
făcute de acelaşi client, intervalul de timp dintre rezervare şi cumpărarea biletului, posibilitatea ca o rezervare să
fie anulată, etc., au fost ignorate, dat fiind că nu modifică fondul problemei de consistenţă a bazei de date.
Atunci când un agent de vânzări rezervă un loc la o cursă şi vinde biletul corespunzător, se efectuează
mai multe operaţii:
1. Se inserează o linie nouă în tabelul PASAGERI, care conţine datele pasagerului.
2. Dacă există locuri libere la cursa dorită, atunci se face propriu-zis rezervarea, prin inserarea unei
linii noi în tabelul REZERVARI, linie care conţine numărul de identificare al pasagerului, numărul
de identificare al cursei şi (eventual) numărul locului rezervat; altfel, rezervarea este imposibilă.
3. La achitarea biletului se inserează o linie în tabelul FACTURI. Acestă înregistrare este folosită
pentru a tipări o factură, care va fi folosită pentru plata în numerar sau va fi trimisă companiei de
cărţi de credit.
4. Se emite (tipăreşte) biletul (pornind de la datele din rezervare şi factura corespunzătoare).
Dacă sistemul se defectează după ce s-a executat pasul 2, s-a făcut o rezervare, dar biletul nu a fost
facturat şi nici emis. Mai rău, dacă defecţiunea are loc după ce s-a executat pasul 3, atunci clientului i se trimite
factura, dar el nu a primit biletul. Astfel de situaţii sunt, bineînţeles, inacceptabile.
Chiar dacă nu se defectează sistemul, pot să apară probleme dacă baza de date este accesată concurent
de mai mulţi utilizatori. De exemplu, dacă doi agenţi de vânzări atribuie acelaşi loc la doi pasageri diferiţi, atunci
vor fi probleme la îmbarcarea pasagerilor.
Dacă toate acţiunile aferente unei rezervări ar fi grupate ca o operaţie indivizibilă (atomică), o parte din
problemele arătate mai sus ar dispărea.
O operaţie indivizibilă de acces la baza de date este numită tranzacţie şi ea fie execută cu succes toate
acţiunile şi se termină cu o validare a modificărilor efectuate asupra bazei de date (commit), fie nu poate efectua
(din diferite motive) toate acţiunile şi este abandonată şi anulată (abort, rollback).
În cazul în care o tranzacţie a efectuat cu succes toate acţiunile şi este validată, în momentul validării
toate modificările efectute asupra bazei de date devin permanente (durabile), vor fi vizibile altor tranzacţii şi nu
mai pot fi anulate. Până în momentul validării, modificările efectuate de tranzacţie au un caracter provizoriu, nu
sunt vizibile altor tranzacţii şi pot fi oricând revocate (anulate).
În cazul abandonării unei tranzacţii, execuţia acesteia este oprită şi efectele tuturor acţiunilor executate
până în momentul abandonării sunt anulate, astfel încât baza de date este adusă în starea de dinaintea lansării
tranzacţiei.

6.1.1 ANOMALII DE EXECUŢIE CONCURENTA A TRANZACŢIILOR


Pentru studierea controlului concurenţei şi al refacerii datelor se vor aborda operaţiile asupra bazei de
date la nivel de articol de date (data item) şi bloc de memorare pe disc. La acest nivel, operaţiile de acces la baza
de date care pot să apară în cursul unei tranzacţii sunt:
• read(X): citeşte articolul X din baza de date într-o variabilă a programului; pentru simplificarea
notaţiilor se va considera că variabila în care se citeşte articolul X este notată, de asemenea, cu X.
• write(X): scrie variabila de program X în articolul X al bazei de date.
Unitatea de transfer a datelor între discul magnetic şi memoria principală a sistemului este un bloc, care
corespunde unui sector de pe disc. În general, un articol de date este un câmp care memorează valoarea unui
atribut dintr-o înregistrare (tuplu), dar poate fi o întreagă înregistrare sau chiar un bloc întreg.
Tranzacţiile lansate de diferiţi utilizatori se pot executa concurent şi este posibil să actualizeze aceleaşi
articole ale bazei de date. Dacă execuţia concurentă a tranzacţiilor este necontrolată, este posibil ca baza de date
să ajungă într-o stare inconsistentă (incorectă), chiar dacă fiecare tranzacţie în parte este un program corect, a
fost executată corect şi nu au apărut defecte de funcţionare ale sistemului.
Actualizare pierdută (lost update). În figura 6.1, a este prezentată una din cele câteva situaţii posibile
de inconsistenţă a datelor memorate atunci când două sau mai multe tranzacţii sunt executate concurent, dar într-
un mod necontrolat. Cele două tranzacţii T1 şi T2 actualizează acelaşi articol (X) al bazei de date şi se execută
concurent prin partajarea (şi întreţeserea) timpului de execuţie al procesorului.

Timp T1 T2 T1 T2
read(X) read(X)
X=X−N X=X−N
read(X) write(X)
X=X+M read(X)
write(X) X=X+M
read(Y) write(X)
write(X) read(Y)
Y=Y+N abort
write(Y)
a b
Fig. 6.1. Inconsistenţa datelor provenită din execuţia concurentă necontrolată a două tranzacţii: a - actualizare
pierdută; b - citire improprie.

Pentru modul de întreţesere în timp a celor două tranzacţii prezentate în figura 6.1, a valoarea memorată
în articolul X în baza de date este X+M, în locul valorii corecte X−N+M (X fiind valoarea iniţială a articolului,
înainte de lansarea tranzacţiilor). Actualizarea efectuată de tranzacţia T1 a fost pierdută, dat fiind că tranzacţia
T2 foloseşte valoarea iniţială a articolului X, înainte ca valoarea calculată de T1(X−N) să fie memorată în baza
de date. Acest tip de funcţionare concurentă incorectă se numeşte actualizare pierdută.
Citire improprie (dirty read). În figura 6.1, b este exemplificată o altă situaţie de inconsistenţă a
datelor, cunoscută sub denumirea de citire improprie - dirty read, sau dependenţă nevalidată - uncommited
dependence sau actualizare temporară - temporary update. Această anomalie poate să apară atunci când una din
tranzacţii este abandonată, iar altă tranzacţie concurentă a utilizat articolele modificate, înainte de readucerea
acestora la valoarea iniţială. În acest exemplu tranzacţia T1 a modificat articolul X, după care a fost abandonată
dintr-o cauză oarecare, dar tranzacţia T2 a citit şi a utilizat articolul modificat X, înainte ca acesta să fie readus la
valoarea sa iniţială. Rezultatul celor două tranzacţii este valoarea X – N + M, în locul valorii corecte, X + M.

2
Citire irepetabilă (nonrepetable read). O altă problemă de inconsistenţă a datelor, cunoscută sub
numele de citire irepetabilă (nonrepetable read, sau analiză inconsistentă - inconsistent analysis), poate să apară
dacă o tranzacţie T citeşte un articol de două ori, iar între cele două citiri, o altă tranzacţie (T’) a modificat chiar
acel articol. În această situaţie tranzacţia T primeşte două valori diferite ale aceluiaşi articol.
Citire fantomă (phantom read). Asemănătoare problemei citirii irepetabile este problema citirii
fantomă, care apare atunci când o tranzacţie prelucrează un set de linii rezultat al unei interogări; dacă în timpul
acestei prelucrări o altă tranzacţie a inserat sau a şters o linie care satisface condiţia interogării respective, atunci
pot să apară sau să dispară linii din mulţimea de linii rezultat, comportare asemănătoare cu aparţia sau dispariţia
fantomelor.
Diferenţa dintre citirea fantomă şi citirea irepetabilă este aceea că citirea fantomă apare dacă tranzacţiile
concurente modifică numărul de linii utilizate de tranzacţia curentă (prin instrucţiuni INSERT sau DELETE), pe
câtă vreme citirea irepetabilă apare dacă tranzacţiile concurente modifică valorile din liniile existente şi
prelucrate de tranzacţia curentă (prin instrucţiuni UPDATE).
Astfel de anomalii, care pot să apară la execuţia concurentă necontrolată a două sau mai mai multor
tranzacţii, evidenţiază necesitatea controlului concurenţei în bazele de date.
Consistenţa datelor memorate în baza de date trebuie să fie asigurată chiar în condiţiile în care o
tranzacţie nu poate fi terminată cu succes, datorită apariţiei unei erori de funcţionare.

6.1.2 PROPRIETĂŢILE TRANZACŢIILOR


Cele mai importante proprietăţi ale tranzacţiilor sunt identificate în literatură prin acronimul ACID:
atomicitate, consistenţă, izolare, durabilitate.
Atomicitatea (atomicity) este proprietatea unei tranzacţii de a reprezenta o unitate de execuţie
indivizibilă, adică de a executa “totul sau nimic”. Dacă o tranzacţie este întreruptă dintr-o cauză oarecare, atunci
sistemul SGBD va asigura, după eliminarea cauzei care a întrerupt executarea tranzacţiei, fie completarea şi
validarea tranzacţiei, fie abandonarea tranzacţiei şi anularea tuturor efectelor acţiunilor efectuate de tranzacţie
până în momentul întreruperii.
Consistenţa (consistency) unei tranzacţii înseamnă proprietatea acesteia de a efectua modificări
corecte ale bazei de date. Cu alte cuvinte, o tranzacţie transformă baza de date dintr-o stare consistentă în altă
stare consistentă. În stările intermediare prin care trece o bază de date în cursul unei tranzacţii, este posibil să
existe unele inconsistenţe, dar starea finală în care ajunge baza de date după execuţia unei tranzacţii trebuie să fie
consistentă. Starea unei baze de date este consistentă dacă respectă toate constrângerile de integritate implicite
sau explicite. Sistemul SGBD nu verifică consistenţa bazei de date după fiecare operaţie (tranzacţie), ci este
sarcina proiectanţilor aplicaţiilor de baze de date ca operaţiile prevăzute în tranzacţii să producă o stare
consistentă a bazei de date.
Izolarea (isolation) este proprietatea unei tranzacţii de a face vizibile modificările efectuate numai
după ce a fost validată (committed). Dacă în acest timp sunt executate alte tranzacţii concurente, acestea nu
“văd” modificările parţiale efectuate de tranzacţia respectivă până în momentul validării tranzacţiei. Proprietatea
de izolare a tranzacţiilor este importantă deoarece elimină fenomenul de abandonare în cascadă a tranzacţiilor.
Dacă rezultatele parţiale ale unei tranzacţii sunt vizibile altor tranzacţii înainte de validarea acesteia şi dacă se
întâmplă ca această tranzacţie să fie abandonată şi anulată (rollback), atunci toate tranzacţiile care au accesat
rezultatele parţiale ale acesteia vor trebui să fie anulate; aceste operaţii de anulare pot produce, la rândul lor alte
anulări, ş.a.m.d. Acest fenomen de anulare în cascadă creează un efect de “domino”. Izolarea este impusă prin
metode de control al concurenţei, pe diferite niveluri de izolare.
Durabilitarea (durability, sau permanenţa - permanency) este proprietatea prin care, după validarea
unei tranzacţii, modificările efectuate de aceasta în baza de date nu vor mai fi pierdute datorită unor defectări
ulterioare a sistemului. Proprietatea de durabilitate este asigurată prin metode de refacere (recovery) ale
sistemului SGBD.

6.1.3 STĂRILE TRANZACŢIILOR


Dacă operaţiile de acces la baza de date ale unei tranzacţii sunt numai operaţii de citire, acea tranzacţie
se numeşte tranzacţie de citire (read-only transaction) şi controlul concurenţei unor astfel de tranzacţii este mult
mai simplu. În cele ce urmează se va studia cazul general, al tranzacţiilor de citire şi scriere (read-write
transactions) care efectuează atât regăsiri de date (prin operaţii de citire) cât şi actualizări (prin operaţii de
scriere), şi care necesită tehnici de control al concurenţei mult mai complexe.
Operaţiile efectuate de o tranzacţie şi înregistrate de administratorul de refacere (recovery manager),
pentru a asigura cele patru proprietăţi importante ale tranzacţiilor (ACID), sunt următoarele:

3
1. begin: această operaţie marchează începutul execuţiei unei tranzacţii.
2. read sau write: sunt operaţii de citire sau scriere a articolelor în baza de date, executate în
cadrul unei tranzacţii.
3. end: această operaţie marchează terminarea operaţiilor de scriere sau citire din baza de date, ceea
ce înseamnă că tranzacţia se poate termina; totuşi, este posibil să fie necesare unele operaţii de
verificare înainte de validarea (commit) tranzacţiei.
4. commit: această operaţie semnalează terminarea cu succes a tranzacţiei, validarea tuturor
modificărilor efectuate în baza de date şi vizibilitatea modificărilor efectuate pentru alte tranzacţii;
din acest moment, modificările efectuate nu mai pot fi anulate, nici pierdute printr-o defectare
ulterioară a sistemului (bineînţeles, dacă mecanismul de refacere al SGBD-ului funcţionează
corect).
5. rollback (sau abort): această operaţie semnalează faptul că tranzacţia a fost abandonată şi că
orice efect pe care tranzacţia l-a avut asupra bazei de date trebuie să fie anulat (printr-o “rulare
înapoi” a operaţiilor).
6. undo: această operaţie este similară operaţiei rollback, dar se aplică unei singure operaţii, nu
unei întregi tranzacţii.
7. redo: această operaţie specifică faptul că unele operaţii ale unei tranzacţii trebuie să fie executate
din nou pentru a se putea valida întreaga tranzacţie.
Ultimele două operaţii sunt necesare numai în anumite tehnici de refacere.
În figura 6.2 este reprezentată diagrama de stare a unei tranzacţii folosind limbajul UML, unde fiecare
stare are o denumire şi fiecare operaţie provoacă o anumită tranziţie între stări.
În starea ACTIVA se ajunge ca urmare a lansării unei tranzacţii prin operaţia begin, iar execuţia
corectă a operaţiilor read sau write nu modifică această stare. Atunci când o tranzacţie se termină, ea trece în
starea PARTIAL VALIDATA în care se efectuează operaţii de verificare impuse de tehnicile (protocoalele) de
control al concurenţei şi de refacere. Dacă ambele verificări sunt îndeplinite cu succes, atunci tranzacţia este
validată (prin execuţia unei operaţii commit) şi trecută în starea VALIDATA; dacă una sau ambele condiţii nu
sunt îndeplinite, tranzacţia este trecută în starea ABANDONATĂ prin execuţia operaţiei abort. Starea
TERMINATĂ este consecinţa imediată a atingerii uneia din stările VALIDATĂ sau ABANDONATĂ şi nu necesită
nici o altă operaţie pentru efectuarea acestei tranziţii. În cursul stării ACTIVĂ, orice tranzacţie poate fi
abandonată (şi trecută în starea ABANDONATĂ prin execuţia unei operaţii abort), dacă diferite verificări
efectuate nu sunt îndeplinite.
read,
write

begin end commit


ACTIVĂ PARTIAL VALIDATĂ
VALIDATĂ

abort abort

ABANDONATĂ TERMINATĂ

Fig. 6.2. Diagrama de stare a unei tranzacţii.

Pentru refacerea bazei de date în condiţiile în care unele tranzacţii sunt abandonate sau în care pot
apărea defecte de funcţionare, sistemul SGBD menţine un fişier jurnal, în care memorează operaţiile efectuate de
tranzacţii. Fişierul jurnal este memorat pe disc şi nu este afectat de diferite erori de execuţie, cu excepţia unei
defectări catastrofice a discului. În plus, fişierul jurnal este salvat periodic pe un suport auxiliar (bandă
magnetică), ca o măsură de prevedere pentru astfel de defecte catastrofice.
În fişierul jurnal (log file) se înregistrează operaţiile efectuate de fiecare tranzacţie, identificată printr-
un identificator unic (T) generat de sistem.
Prima înregistrare referitoare la o tranzacţie cu identificatorul T este înregistrarea de start a tranzacţiei:
[begin,T]. La fiecare operaţie write(X) executată de o tranzacţie T, în fişierul jurnal se memorează o
înregistrare de tipul [write,T,X,vechea_valoare,noua_valoare], dacă pentru refacerea datelor se

4
folosesc operaţii undo, sau o înregistrare de tipul [write,T,X,noua_ valoare], dacă se folosesc
operaţii redo. Sistemele care nu evită abandonarea în cascadă a tranzacţiilor înregistrează în fişierul jurnal şi
operaţiile de citire read(X), printr-o înregistrare de tipul [read,T,X,valoare].
Pentru fiecare tranzacţie T, în fişierul jurnal se mai memorează starea de terminare: validată (printr-o
înregistrare [commit,T] ) sau anulată (printr-o înregistrare[rollback,T]). După executarea validării şi
înregistrarea ei în fişierul jurnal, efectul tranzacţiei este permanent memorat în baza de date.
Dacă sistemul se defectează, la reluarea execuţiei după îndepărtarea defectului, sistemul SGBD va
aduce baza de date într-o stare consistentă prin examinarea fişierului jurnal. Dat fiind că fişierul jurnal conţine o
înregistrare pentru fiecare operaţie de scriere care a modificat valoarea unui articol al bazei de date, este posibil
de a anula efectul tuturor operaţiilor de scriere efectuate de o tranzacţie care a fost lansată dar nu a fost validată,
parcurgând înapoi fişierul jurnal şi înlocuind valoarea existentă a articolului modificat cu vechea valoare,
memorată în fişierul jurnal. În cazul abandonării în cascadă, trebuie să fie abandonate şi tranzacţiile care au citit
valori modificate de tranzacţiile abandonate.

6.1.4 PLANIFICAREA TRANZACŢIILOR


O planificare (schedule, sau istorie - history) S a n tranzacţii T1,T2,...Ti,...Tn este o ordonare
a operaţiilor tranzacţiilor astfel încât, pentru orice tranzacţie Ti care participă în S, operaţiile lui Ti în S
respectă ordinea iniţială din Ti. În acelaşi timp, alte operaţii (ale altor tranzacţii Tj, unde j ≠ i) pot fi
întreţesute cu operaţii ale tranzacţiei Ti.
Pentru definirea planificărilor se vor reprezenta operaţiile de read, write, commit şi abort ale
tranzacţiilor componente, notate prescurtat cu r, w, c, a, având ca subscript identificatorul tranzacţiei şi ca
parametru articolul pe care l-a citit sau scris. De exemplu, planificările S1 şi S2 din figura. 6.1 se pot descrie
astfel:
S1: r1(X); r2(X); w1(X); r1(Y); w2(X); c2; w1(Y); c1;
S2: r1(X); w1(X); r2(X); w2(X); c2; r1(Y); a1;
Două operaţii dintr-o planificare sunt conflictuale (conflicting operations) dacă aparţin unor tranzacţii
diferite, accesează acelaşi articol şi cel puţin una dintre operaţii este operaţie de scriere. De exemplu, în
planificarea S1, operaţii conflictuale sunt perechile: (r1(X), w2(X)), ( r2(X), w1(X)) şi (w1(X), w2(X)).
O planificare S a n tranzacţii T1,T2,...Ti,...Tn se numeşte completă dacă îndeplineşte
următoarele condiţii:
1. Operaţiile din S sunt exact toate operaţiile celor n tranzacţii, inclusiv operaţiile de validare sau
abandonare, ca operaţii finale executate de fiecare dintre cele n tranzacţii ale planificării.
2. Pentru oricare pereche de operaţii dintr-o tranzacţie Ti, ordinea lor de apariţie în S este aceeaşi cu
ordinea de apariţie în Ti.
3. Pentru fiecare două operaţii conflictuale, una dintre ele trebuie să fie executată înaintea celeilalte.
Aceste condiţii permit ca două operaţii care nu sunt conflictuale să poată fi executate oricum în
planificarea respectivă, fără să fie necesar să se precizeze care operaţie are loc mai întâi. O astfel de ordine a
operaţiilor tranzacţiilor unei planificări se numeşte ordine parţială. Totuşi, într-o planificare, trebuie să fie
specificată o ordine totală (adică să fie precizată care operaţie se execută mai întâi) pentru orice pereche de
operaţii conflictuale (condiţia 3) şi pentru orice pereche de operaţii care aparţin aceleiaşi tranzacţii (condiţia 2).

6.1.5 SERIALIZABILITATEA PLANIFICĂRILOR


Dacă doi utilizatori ai unei baze de date lansează (aproape) simultan două tranzacţii T1 şi T2 şi nu este
permisă nici o întreţesere între operaţiile celor două tranzacţii, atunci sunt posibile două moduri de ordonare a
operaţiilor celor două tranzacţii:
• Se execută mai întâi toate operaţiile tranzacţiei T1, urmate de execuţia tuturor operaţiilor tranzacţiei
T2.
• Se execută mai întâi toate operaţiile tranzacţiei T2, urmate de execuţia tuturor operaţiilor tranzacţiei
T1.

5
Astfel de planificări, care nu întreţes operaţiile tranzacţiilor, ci le execută consecutiv, se numesc
planificări seriale.

Planificări seriale (serial schedules). O planificare S se numeşte serială dacă pentru orice tranzacţie T
participantă în planificare, toate operaţiile lui T se execută consecutiv în S; altfel, planificarea se numeşte
neserială.
În figurile 6.3, a şi b sunt prezentate cele două planificări seriale posibile, SA şi SB ale celor două
tranzacţii T1 şi T2 din figura 6.1, a. Cu notaţia prescurtată a planificărilor prezentată mai sus, cele două
planificări seriale se pot descrie astfel:
SA: r1(X); w1(X); r1(Y); w1(Y); c1; r2(X); w2(X); c2;
SB: r2(X); w2(X); c2; r1(X); w1(X); r1(Y); w1(Y); c1;
Dacă tranzacţiile unei planificări seriale sunt independente între ele şi fiecare tranzacţie este corectă,
atunci planificarea respectivă este corectă.

Timp T1 T2 T1 T2
read(X) read(X)
X=X−N X=X+M
write(X) write(X)
read(Y) read(X)
Y=Y+N X=X−N
write(Y) write(X)
read(X) read(Y)
X=X+M Y=Y+N
write(X) write(Y)

a b

Timp T1 T2 T1 T2
read(X) read(X)
X=X−N X=X−N
write(X) write(X)
read (X) read(Y)
X=X+M read (X)
write(X) X=X+M
read(Y) Y=Y+N
Y=Y+N write(Y)
write(Y) write(X)
c d

Fig. 6.3. a, b - Planificări seriale ale tranzacţiilor T1 şi T2;


c, d - Planificări serializabile echivalente cu planificarea serială a.

Problema planificărilor seriale este aceea că nu permit întreţeserea operaţiilor şi concurenţa


tranzacţiilor. Dacă o tranzacţie a unei planificări seriale aşteaptă un timp oarecare (posibil foarte mare, în cazul
execuţiei interactive cu operatori umani) rezultatul unei operaţii de intrare-ieşire, atunci toate operaţiile care
urmează, precum şi toate celelalte tranzacţii concurente, aşteaptă, irosind timpul procesorului. De aceea, în
general, planificările seriale nu se pot folosi în cazul sistemelor de baze de date cu utilizatori multipli, şi se
realizează planificări care admit concurenţa, asigurând în aceelaşi timp consistenţa bazei de date. Astfel de
planificări sunt planificările serializabile.

Planificări serializabile (serializable schedules). O planificare a n tranzacţii se numşte serializabilă


dacă este echivalentă cu o planificare serială a celor n tranzacţii.
Această definiţie este un alt mod de a spune că o planificare serializabilă este o planificare corectă, dat
fiind că orice planificare serială este corectă.

6
Se pune problema de a defini ce înseamnă două planificări echivalente. Aparent, s-ar putea considera că
două planificări sunt echivalente dacă execuţia lor produce aceeaşi stare finală a bazei de date. Acestă
echivalenţă, numită echivalenţă privind rezultatul (result equivalence), nu este suficientă, dat fiind că două
planificări pot produce în mod accidental aceeaşi stare finală a bazei de date.
O definiţie mai precisă a echivalenţei a două planificări, numită echivalenţa privind conflictele
(conflict equivalence, sau echivalenţa de ordonare -order equivalence) este următoarea:

Planificări echivalente din punct de vedere al conflictelor (conflict-equivalent schedules). Două


planificări sunt echivalente din punct de vedere al conflictelor dacă oricare pereche de operaţii conflictuale se
execută în aceeaşi ordine în cele două planificări.
Dacă două operaţii conflictuale se execută în ordine diferită în două planificări, rezultatele operaţiilor
efectuate asupra datelor memorate pot fi diferite.
În figurile 6.3, c şi d sunt prezentate două planificări echivalente cu planificarea serială SA a celor două
tranzacţii T1 şi T2; notaţia prescurtată a acestor planificări este următoarea:
SC: r1(X); w1(X); r2(X); w2(X); c2; r1(Y); w1(Y); c1;
SD: r1(X); w1(X); r1(Y); r2(X); w1(Y); c1; w2(X); c2;
Echivalenţa a două planificări se stabileşte verificând ordinea de execuţie a operaţiilor conflictuale. Se
observă că perechile de operaţii conflictuale (r1(X), w2(X)), ( w1(X), r2(X)) şi (w1(X), w2(X)) se
execută exact în această ordine în planificările SA, SC şi SD, ceea ce înseamnă că planificările SC şi SD sunt
echivalente cu planificarea serială SA şi sunt, deci, serializabile.
Serializabilitatea unei planificări poate fi testată, dar această operaţie este deosebit de complicată, cu un
cost de execuţie a algoritmului de testare foarte ridicat şi de aceea nu se practică în mod curent. Metodele
practice de asigurare a serializabilităţii planificărilor şi, prin aceasta, a consistenţei bazelor de date, se bazează pe
controlul concurenţei tranzacţiilor.

6.2 TEHNICI DE CONTROL AL CONCURENŢEI


Controlul execuţiei concurente a mai multor tranzacţii este necesar pentru a asigura proprietăţile de
atomicitate, izolare şi durabilitate a tranzacţiilor şi, prin aceasta, consistenţa datelor memorate. Controlul
concurenţei se poate realiza prin protocoale (set de reguli) impuse tranzacţiilor astfel încât, dacă acestea sunt
respectate de fiecare tranzacţie, orice planificare în care astfel de tranzacţii participă este serializabilă şi, deci,
corectă.
Cele mai utilizate tehnici de control al concurenţei sunt cele bazate pe blocarea datelor prin intermediul
zăvoarelor (locks) şi cele bazate pe mărci de timp (timestamps).
Protocoalele de control al concurenţei sunt implementate de sistemele de gestiune a bazelor de date
astfel încât programatorii de aplicaţii nu operează în mod explicit cu zăvoare sau mărci de timp, ci stabilesc
opţiunile prin care sistemul SGBD adoptă anumite tehnici de control al concurenţei. Descrierea în continuare a
algoritmilor şi a operaţiilor de execuţie a tranzacţiilor este o descriere de principiu, prezentată cu scopul
înţelegerii mecanismelor de bază ale controlului concurenţei.

6.2.1 CONTROLUL CONCURENŢEI PRIN BLOCARE


Controlul concurenţei tranzacţiilor prin blocare (concurrency control using locking technique) se
realizează folosind zăvoare. Un zăvor (lock) este o variabilă asociată cu un articol al unei baze de date care
descrie starea acelui articol în raport cu operaţiile care se pot aplica acelui articol. Pentru un articol X, zăvorul
care controlează accesul la acel articol va fi notat L(X). În sistemele de gestiune a bazelor de date se pot utiliza
două tipuri de zăvoare, zăvoare binare şi zăvoare cu stări multiple.
Un zăvor binar (binary lock) poate avea două stări: liber (sau neblocat - free, unlocked) şi ocupat (sau
blocat - busy, locked) sau, mai simplu, stările 1 şi 0. Asupra unui zăvor L(X)se pot executa două operaţii:
operaţia de blocare, lock(X) şi operaţia de eliberare, unlock(X).
Dacă zăvorul articolului X este deja blocat (L(X)=0), atunci operaţia de blocare lock pune în
aşteptare tranzacţia până când zăvorul se eliberează. Dacă zăvorul este liber (L(X)=1), atunci tranzacţia
achiziţionează zăvorul, trecându-l în starea ocupat, după care execută operaţiile necesare asupra articolului X.
După terminarea operaţiilor asupra acelui articol, tranzacţia eliberează zăvorul, prin execuţia operaţiei unlock.
Dacă zăvorul a fost ocupat, tranzacţia aşteaptă până ce acesta este eliberat (de o altă tranzacţie, care şi-a terminat
operaţiile de acces la acel articol), după care execută aceeaşi secvenţă de operaţii: blocarea zăvorului, execuţia
operaţiilor care accesează articolul respectiv şi eliberarea zăvorului.

7
Pentru ca, în orice moment de timp, cel mult o tranzacţie să deţină un anumit zăvor, trebuie ca operaţia
de blocare să fie executată ca operaţie indivizibilă. Acestă cerinţă este implementată prin instrucţiuni speciale ale
procesorului (de tip TestAndSet(TS)). Se pot preciza următoarele reguli (protocolul) pe care trebuie să le
urmeze fiecare tranzacţie care foloseşte zăvoare binare:
1. O tranzacţie T trebuie să lanseze o operaţie de blocare a zăvorului asignat articolului X
(lock(X)), înainte de a efectua orice operaţie de citire (read(X)) sau de scriere (write(X)) a
articolului X.
2. O tranzacţie T trebuie să elibereze zăvorul unui articol X (prin operaţia unlock(X)) după ce a
efectuat toate operaţiile de citire (read(X)) sau de scriere (write(X)) a articolului X.
3. O tranzacţie T nu poate cere un zăvor pe care îl deţine deja.
4. O tranzacţie T nu poate elibera un zăvor pe care nu îl deţine.
Între operaţia de blocare (lock(X)) lansată de o tranzacţie T şi operaţia de eliberare (unlock(X)) a
unui zăvor L(X), tranzacţia T deţine zăvorul respectiv. În cazul zăvoarelor binare, cel mult o tranzacţie poate
deţine un zăvor la un moment dat, şi nici o altă tranzacţie nu poate accesa articolul respectiv. Se poate spune că
zăvoarele binare realizează un mecanism de excludere mutuală, prin care accesul unei tranzacţii la un articol
(tranzacţia care deţine zăvorul acelui articol) exclude accesul altor tranzacţii la acel articol.
Deşi este foarte generală şi relativ simplu de utilizat, tehnica zăvoarelor binare este prea restrictivă şi
limitează în mod nejustificat concurenţa în execuţia tranzacţiilor. De exemplu, mai multe tranzacţii pot efectua
operaţii de citire în mod concurent, fără ca acest lucru să afecteze consistenţa bazei de date, dar acest lucru este
interzis în tehnica zăvoarelor binare. De aceea, multe sisteme de gestiune a bazelor de date utilizează zăvoare cu
stări multiple.

6.2.2 CONTROLUL CONCURENŢEI BAZAT PE MĂRCI DE TIMP


O marcă de timp (timestamp) este un identificator unic al unei tranzacţii, creat de sistemul de gestiune
a bazei de date, care se bazează pe timpul de start al tranzacţiei.
O marcă de timp se poate crea fie folosind valoarea curentă a ceasului sistemului de operare, fie
folosind un numărător care este incrementat la fiecare asignare a unei noi mărci, în ordinea de lansare a
tranzacţiilor. În orice caz, o tranzacţie T va avea o marcă de timp unică, notată TS(T).
Pentru fiecare articol X al bazei de date se definesc două mărci de timp:
• read_TS(X) - marca de timp de citire a articolului X; aceasta este cea mai mare marcă de timp
dintre toate mărcile de timp ale tranzacţiilor care au citit articolul X.
• write_TS(X) - marca de timp de scriere a articolului X; aceasta este cea mai mare marcă de
timp dintre toate mărcile de timp ale tranzacţiilor care au scris în articolul X.
Serializabilitatea planificărilor se obţine dacă se impun anumite condiţii ordinii de accesare a articolelor
de mai multe tranzacţii concurente, în funcţie de mărcile de timp ale acestora.

6.3 TEHNICI DE REFACERE A BAZELOR DE DATE


Refacerea unei baze de date după producerea unui defect (database recovery) înseamnă aducerea
bazei de date într-o stare precedentă corectă, din care, eventual, se poate reconstrui o nouă stare corectă şi cât
mai apropiată de momentul apariţiei defectului. Pentru operaţiile de refacere se foloseşte fişierul jurnal, şi (sau)
o copie de rezervă a bazei de date (database backup) stocată în general pe bandă magnetică.
Dacă baza de date nu este distrusă fizic, dar a devenit inconsistentă datorită unui defect necatastrofic,
atunci strategia de refacere constă în a anula modificările care au produs inconsistenţa (prin operaţii undo) sau,
uneori, de a executa din nou anumite modificări care s-au pierdut (prin operaţii redo). În acest caz nu este
necesară copia de rezervă, ci se foloseşte starea actuală a bazei de date şi fişierul jurnal.
Dacă baza de date a fost puternic distrusă, datorită unei defectări serioase a discului, atunci se
restaurează starea bazei de date din copia de rezervă, după care se reconstruieşte o stare cât mai actuală prin
reaplicarea tuturor tranzacţiilor validate existente în fişierul jurnal, dacă acesta nu a fost deteriorat, sau din ultima
copie salvată a fişierului jurnal.

6.4 CONTROLUL TRANZACŢIILOR


Tehnicile de gestiune a tranzacţiilor şi de refacere a datelor prezentate în secţiunile precedente sunt
incluse în componentele sistemelor de gestiune a bazelor de date (administratorul de tranzacţii şi administratorul
de refacere) într-o formă specifică fiecărui SGBD, cu diferite grade de complexitate.

8
Aplicaţiile de baze de date au un control destul de limitat asupra opţiunilor de gestiune a tranzacţiilor
prin intermediul unor comenzi care se bazează pe standardul SQL2.
În standardul SQL2 sunt prevăzute următoarele comenzi de specificare a tranzacţiilor:
SET TRANSACTION optiuni
COMMIT [WORK] ROLLBACK [WORK]
Comanda SET TRANSACTION stabileşte proprietăţile tranzacţiilor şi admite următoarele opţiuni de
setare a modului de gestiune a tranzacţiilor:
• Nivelul de izolare a tranzacţiilor (ISOLATION LEVEL) cu valorile posibile: READ
UNCOMMITTED, READ COMMITTED, REPETABLE READS, SERIALIZABLE.
• Modul de acces la articole - cu valorile posibile READ ONLY, READ WRITE.
• Modul de refacere a datelor (SET CONSTRAINTS), cu valorile posibile DEFERRED (refacere
amânată) şi IMMEDIATE (refacere imediată).
Nivelul de izolare reprezintă gradul până la care o tranzacţie trebuie să fie izolată de celelalte tranzacţii.
Izolarea totală a tranzacţiilor, în care starea bazei de date este consistentă în permanenţă, iar în tranzacţii nu apar
nici un fel de anomalii, este obţinută pe nivelul cel mai înalt de izolare, denumit SERIALIZABLE, care
corespunde planificărilor serializabile (echivalente cu planificări seriale ale tranzacţiilor). Acest nivel de izolare
totală micşorează gradul de concurenţă a tranzacţiilor, şi, ori de câte ori este posibil, se admit niveluri de izolare
mai scăzute, care admit unele anomalii (controlabile) de execuţie a tranzacţiilor şi asigură un grad de concurenţă
mai ridicat. Nivelurile de izolare determină modul în care sistemul de gestiune a bazei de date introduce
diferitele mecanisme de control al concurenţei (cel mai frecvent zăvoare cu stări multiple). De exemplu, pe
nivelul READ COMMITTED, sunt prevăzute zăvoare partajate pentru toate articolele citite, ceea ce împiedică
apariţia citirilor improprii, dar aceste zăvoare sunt eliberate înainte de terminarea tranzacţiei şi, de aceea, pot
rezulta citiri nerepetabile şi citiri fantomă (Tabelul 6.1).
Tabelul 6.1. Nivelurile de izolare a tranzacţiilor în standardul SQL2

Nivelul Citire Citire Citire


de izolare improprie nerepetabilă fantomă
READ UNCOMMITTED DA DA DA
READ COMMITTED NU DA DA
REPETABLE READS NU NU DA
SERIALIZABLE NU NU NU

Pe orice nivel de izolare, inclusiv pe cel mai slab (READ UNCOMMITTED), se folosesc mecanisme de
control al concurenţei tranzacţiilor care previn pierderea actualizărilor. Astfel de anomalii sunt foarte grave, baza
de date nu reflectă operaţiile care s-au efectuat asupra datelor şi nici nu există vreo posibilitate de refacere a
acestor pierderi. De aceea nu este prevăzut nici un nivel de izolare care să permită pierderea actualizării datelor.
Pe toate nivelurile de izolare, cu excepţia nivelului SERIALIZABLE, pot să apară diferite anomalii, dar
aceste anomalii sunt anomalii de citire, care pot fi gestionate de tranzacţii, şi nu anomalii memorate permanent în
baza de date. De exemplu, dacă se ştie că o tranzacţie va fi executată pe nivelul de izolare READ COMMITTED,
atunci se poate scrie codul tranzacţiei astfel încât aceasta să nu citească datele din tabele decât o singură dată şi
să le memoreze local pentru o altă utilizare, în loc să citească de mai multe ori din tabele.
Cu cât nivelul de izolare a tranzacţiilor este mai scăzut, cu atât pot să apară mai multe anomalii de
actualizare, dar creşte gradul de concurenţă a execuţiei şi scade probabilitatea de apariţie a impasului. De aceea,
pentru proiectarea unor tranzacţii eficiente se recomandă utilizarea unor niveluri de izolare cât mai scăzute, atât
cât este posibil pentru ca tranzacţiile respective să se execute totuşi corect.
O tranzacţie se poate termina fie prin validare (cu comanda COMMIT), fie prin anulare (cu comanda
ROLLBACK). Comanda COMMIT garantează că toate modificările efectuate de tranzacţia respectivă au devenit
permanente. Comanda ROLLBACK termină o tranzacţie şi anulează (rulează înapoi) toate modificările executate
până în acel punct de acea tranzacţie; această comandă se trimite atunci când apare o anumită condiţie (posibil o
eroare), care face imposibilă continuarea cu succes a tuturor operaţiilor tranzacţiei. Instrucţiunile COMMIT şi
ROLLBACK eliberează resursele ocupate de tranzacţie, cum ar fi zăvoarele articolelor.
În general, sistemele SGBD implementează protocoalele şi funcţiile de control al concurenţei şi
gestionează automat execuţia tranzacţiilor şi refacerea datelor, pentru a asigura consistenţa şi integritatea datelor
memorate. Tranzacţiile sunt administrate la nivelul conexiunii unei aplicaţii client cu serverul bazei de date:
atunci când o tranzacţie a fost începută pe o conexiune, toate instrucţiunile următoare executate pe acea
conexiune fac parte din acea tranzacţie, până ce aceasta se termină.
Programatorii de aplicaţii au responsabilitatea să stabilească punctele de început şi de sfârşit ale
tranzacţiilor şi să prevadă în fiecare tranzacţie secvenţele de modificări ale datelor astfel încât acestea să lase
baza de date într-o stare consistentă, care să respecte toate constrângerile, implicite şi explicite. De asemenea, se

9
pot selecta prin program diferite opţiuni de control (nivel de izolare, mod de acces, etc.). Aceste operaţii se pot
realiza prin intermediul unor comenzi care sunt variante ale comenzilor SQL de bază şi care se transmit SGBD-
ului, fie prin instrucţiuni ale unui limbaj procedural de extensie a limbajului SQL, fie prin funcţii ale interfeţelor
de programare (cum sunt interfeţele ODBC, JDBC) (exemple in manual).
Tranzacţiile sunt corecte dacă lasă baza de date într-o stare consistentă şi sunt cu atât mai eficiente cu
cât sunt mai scurte (ca timp de execuţie şi ca număr de articole ale bazei de date accesate). Respectarea acestor
cerinţe are o influenţă pozitivă asupra performanţelor bazelor de date atât prin limitarea frecvenţei de apariţie a
impasului (în cazul folosirii zăvoarelor), cât şi din punct de vedere al eficienţei operaţiilor de anulare şi de
blocare a resurselor. Ori de câte ori se poate înlocui o tranzacţie complexă, cu număr de operaţii şi timp de
execuţie ridicate, cu mai multe tranzacţii scurte, este indicat să se facă această transformare. De asemenea,
pentru menţinerea tranzacţiilor cât mai scurte posibil, se recomandă ca o tranzacţie să nu fie pornită până ce nu
au fost pregătite toate datele (citirea datelor de intrare, parcurgerea, analiza şi prelucrarea acestora).

10

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