Documente Academic
Documente Profesional
Documente Cultură
Date Date
Utilizator Program
final aplicaţie SGBD
Date 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.
2
Vedere Vedere Vedere
Nivelul extern utilizator #1 utilizator #2 utilizator #n
Date
memorate
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.
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.
Reţea
de comunicaţie
Server Server
SGBD SGBD
BD BD
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.
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.
Nume
Atribut
atribut
N
Asociere binară 1:N
1 A
E1 E2 între 2 tipuri de entităţi
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.
1 N
SECTIE ANGAJAT
Cuprinde 1 M
Intretine Lucreaza
N N
DEPENDENT PROIECT
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,...)
1 N N 1
STUDENTI NOTE DISCIPLINE (b)
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).
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.
ANGAJAT
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.
Î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).
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].
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).
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.
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
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.
ANGAJATI
Nume Prenume DataNasterii Adresa Functie Salariu
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.
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.
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.
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.
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.
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;
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;
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.
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;
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.
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));
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
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.
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.
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 );
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.
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.
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’;
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];
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
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
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.
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
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
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
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
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
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:
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
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
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
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:
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.
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.
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.
1 M COMPOZITII M
ACTIVITATI
N
N N M
ACHIZITII
PRODUSE
ANGAJATI P
N M
1 VANZARI
N N
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
• 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.
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.
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.
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.
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.
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.
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ă.
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ă.
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.
Administrator de drivere
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.
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.
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.
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.
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.
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.
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.
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}
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.
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
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.
Nume
Nume
Prenume
Prenume
IdAngajat
IdAngajat Adresa
Adresa
Functie
Functie
b
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.
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.
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.
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.
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.
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
abort abort
ABANDONATĂ TERMINATĂ
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.
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
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:
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.
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
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