175
INTRODUCERE Începând cu anul universitar 2006-2007 şi, implicit, prima promoţie de bolognişti 1 , disciplina Baze de date I a fost introdusă în al doilea an de studii şi la specializarea Contabilitate şi Informatică de Gestiune. Probabil că mulţi şi-au adus aminte de bancul cu „asta ne mai trebuia acuma”. Oricum, împreună cu studenţii de la specializarea Informatică Economică, veterani în luptele de stradă cu bazele de date, vă puteţi reuni, măcar periodic, sub sigla „Suferinţă fără frontiere”. Poate suna paradoxal, dar nu avem de gând să vă ţinem o pledoarie lacrimogenă despre importanţa bazelor de date pentru contabilitate şi tot ceea ce ţine de un sistem informaţional. O (mică) parte dintre dvs. poate va găsi ceva interesant în cele peste 150 de pagini. Ceea ce dorim să vă consiliem (iniţial voiam să scriem „să vă atragem atenţia”, dar suna prea apăsător) este că, pentru a rămâne cu ceva fixat din curs şi, implicit, pentru a-l promova, nu este atât de important să lecturaţi cartea de faţă, ci mai ales să o înţelegeţi şi să exersaţi. Pentru a ne încadra în limita procustiană de pagini, am scos din manual rezumatele capitolelor şi tipurile de probleme de la examen. Acestea, împreună cu diverse listinguri (secvenţe de comenzi SQL), diagrame şi alte materiale suplimentare sunt postate pe portal, la secţiunea afectată disciplinei. Vă încurajăm, de asemenea, să folosiţi grupul de discuţii al disciplinei petru a vă exprima neclarităţile (nedumeririle, consternările etc.) în speranţa unor eventuale lămuriri (sau măcar intimidări ale profesorilor). Încheiem urându-vă un sincer „să vă fie bazele de date uşoare !” şi, de asemenea, propunem să începem materia cu un mic moment de reculegere pentru toate viitoarele victimeale cursului Baze de date I. Noiembrie 2006 - Ianuarie 2010 Echipa BD1: Marin Fotache, Cătălin Strâmbei, Liviu Creţu, Octavian Dospinescu, Florin Sîrbu 1 Beneficiari ai prevederilor Convenţiei de la Bologna

BD1 2010 FotacheM Integral

Embed Size (px)

Citation preview

Page 1: BD1 2010 FotacheM Integral

INTRODUCERE

Începând cu anul universitar 2006-2007 şi, implicit, prima promoţie de bolognişti1,

disciplina Baze de date I a fost introdusă în al doilea an de studii şi la specializarea

Contabilitate şi Informatică de Gestiune. Probabil că mulţi şi-au adus aminte de bancul cu

„asta ne mai trebuia acuma”. Oricum, împreună cu studenţii de la specializarea Informatică

Economică, veterani în luptele de stradă cu bazele de date, vă puteţi reuni, măcar periodic, sub

sigla „Suferinţă fără frontiere”.

Poate suna paradoxal, dar nu avem de gând să vă ţinem o pledoarie lacrimogenă

despre importanţa bazelor de date pentru contabilitate şi tot ceea ce ţine de un sistem

informaţional. O (mică) parte dintre dvs. poate va găsi ceva interesant în cele peste 150 de

pagini. Ceea ce dorim să vă consiliem (iniţial voiam să scriem „să vă atragem atenţia”, dar

suna prea apăsător) este că, pentru a rămâne cu ceva fixat din curs şi, implicit, pentru a-l

promova, nu este atât de important să lecturaţi cartea de faţă, ci mai ales să o înţelegeţi şi să

exersaţi.

Pentru a ne încadra în limita procustiană de pagini, am scos din manual rezumatele

capitolelor şi tipurile de probleme de la examen. Acestea, împreună cu diverse listinguri

(secvenţe de comenzi SQL), diagrame şi alte materiale suplimentare sunt postate pe portal, la

secţiunea afectată disciplinei. Vă încurajăm, de asemenea, să folosiţi grupul de discuţii al

disciplinei petru a vă exprima neclarităţile (nedumeririle, consternările etc.) în speranţa unor

eventuale lămuriri (sau măcar intimidări ale profesorilor).

Încheiem urându-vă un sincer „să vă fie bazele de date uşoare !” şi, de asemenea,

propunem să începem materia cu un mic moment de reculegere pentru toate viitoarele

„victime” ale cursului Baze de date I.

Noiembrie 2006 - Ianuarie 2010

Echipa BD1: Marin Fotache, Cătălin Strâmbei, Liviu Creţu, Octavian Dospinescu, Florin Sîrbu

1 Beneficiari ai prevederilor Convenţiei de la Bologna

Page 2: BD1 2010 FotacheM Integral

2 Baze de date I

Capitolul 1

DESPRE BAZE DE DATE

Obiective:

I. Argumentarea importanţei bazelor de date pentru sistemele

informaţionale actuale

II. Definirea bazelor de date, prin comparaţie cu organizarea datelor în

fişiere independente

III. Prezentarea frugală a principalelor modele de structurare a datelor

în bazele de date

Rezultate aşteptate:

Nici unul

Page 3: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 3

1.1 NEVOIA DE BAZE DE DATE

După cum discutam în cursul de Instrumente software pentru afaceri, folosim bazele de date

pentru că avem memoria prea scurtă şi stăm prost cu calculele. Ca şi în alte privinţe, avem nevoie de

baze de date pentru că suntem nişte limitaţi. Deoarece trăim într-o lume aşezată pe un morman de

hârtii & hârţoage, ne este cu neputinţă să reconstituim ceea ce am făcut adineori, darămite ieri,

săptămâna trecută sau acum un an sau cinci. Necazul e că, de cele mai multe ori, trebuie să ştim nu

numai ce-am făcut noi, dar ce-au făcut şi colegii şi partenerii de afaceri.

Simplificând şi exagerând nepermis lucrurile, am putea spune că există doi poli între care

poate fi poziţionată orice problemă informatică. Pe de o parte, cel al chestiunilor interesante, nu

neapărat cu formule şi calcule complexe, care reclamă un anumit grad de ingeniozitate în rezolvare -

mult invocata şi aşteptata "fisă". Celălalt pol regrupează probleme în care complexitatea calculelor

rareori depăşeşte nivelul celor patru operaţii aritmetice elementare - adunare, scădere şi încă două; în

schimb, volumul informaţiilor şi zecile/sutele moduri de regrupare şi agregare a lor este deconcertant.

Fără a face concurenţă vreunui manual de filosofie, putem spune că, din păcate, ca şi în viaţă,

ponderea problemelor din a doua categorie – să le spunem plicticoase – este mult mai mare decât

ponderea problemelor cu adevărat interesante. Aceasta ar fi vestea proastă. Vestea bună este că se

câştigă enorm de mulţi bani din chestiunile plicticoase. O veste intermediară ar fi că, în majoritate,

problemele pe care le are de rezolvat un informatician presupun elemente din ambele categorii. De

fapt, cei doi poli de care vorbim au o existenţă virtuală, fiind utili mai degrabă din raţiuni didactice &

pedagogice.

Cert este că, încă de la începuturile sale, informatica a fost confruntată nu numai cu efectuarea

de calcule sofisticate, ştiinţifice, dar şi cu stocarea şi gestionarea unui volum de informaţii din ce în ce

mai mare. Astfel încât apariţia unor instrumente software dedicate gestiunii şi prelucrării datelor a fost

doar o problemă de timp.

Prin urmare, avem nevoie de baze de date pentru a păstra, într-un format utilizabil, date şi

informaţii legate de evenimente, tranzacţii etc. şi, la nevoie, de a le regăsi şi prelucra după cum ne cer

împrejurările. Bazele de date nu reprezintă singurul instrument de stocare şi prelucrare a informaţiilor.

Şi într-un banal fişier .DOC (document Word, WordPerfect...), prezentare PowerPoint sau foaie de

calcul (Excel, Lotus 1-2-3...) păstrăm date. Ca să nu mai vorbim de pagini Web. Problema este că în

documente, foi de calcul, prezentări, fişiere HTML etc. datele sunt slab structurate. Pentru a localiza

informaţiile trebuie folosite instrumente de căutare care să depisteze prezenţa unor cuvinte cheie,

eventual în preajma unor alte cuvinte cheie (cu o afacere de genul acesta s-au umplut de bani cei de la

Google).

Încheiem acest mini-paragraf cu două veşti. Cea bună este că, la acest moment, bazele de date

reprezintă cel mai bine structurat mod de păstrare şi scotocire a informaţiilor. Este motivul pentru care

piaţa produselor de lucru cu bazele de date se exprimă în valori de ordinul miliardelor de dolari.

Vestea rea ţine de faptul că, dintre toate datele şi informaţiile pe care le vehiculăm/gestionăm/prelu-

crăm pe hârtie, la telefon, pe bandă sau disc magnetic, optic etc., doar o mică parte sunt preluate şi

preluabile în bazele de date.

Page 4: BD1 2010 FotacheM Integral

4 Baze de date I

1.2 LA ÎNCEPUT A FOST FIŞIERUL

Prima care a resimţit acut nevoia unor instrumente software dedicate administrării unor

cantităţi imense de informaţii a fost armata SUA. În a doua parte a anilor '50 Departamentul Apărării

al SUA a format un grup de specialişti pentru eborarea unui limbaj destinat aplicaţiilor administrative,

în care dificultatea majoră ţinea de volumul imens de resurse materiale şi financiare ce trebuia

"chivernisit" şi pentru care erau necesare rapoarte dintre cele mai diverse. La acel moment apăruse

FLOWMATIC, limbaj precursor celui care a fost considerat câteva decenii regele informaticii

economice – COBOL (Common Business Oriented Language).

Arhitectura aplicaţiilor de acest tip – specifică nu numai COBOL-ului, ci multor limbaje din a

III-a generaţie, denumită flat-files architecture – tradusă, într-o doară, în româneşte drept fişiere

independente – este reprezentată în figura 1.1. Specific acestui mod de lucru, referit ca file-based sau

flat files (fişiere independente), este faptul că fiecare dată (Data1, Data2,... Datan) este descrisă (nume,

tip, lungime), autonom, în toate fişierele în care apare. Mai mult, descrierea fiecărui fişier de date

(câmpurile care-l alcătuiesc, tipul şi lungimea fiecăruia, modul de organizare (sercvenţial, indexat,

relativ etc.)) este obligatorie în toate programele care îl “citesc” sau modifică. Între FIŞIER1,

FIŞIER2, ... FIŞIERn nu există nici o relaţie definită explicit.

Data 1

Data 2

Data 3

Data 4

Data 2

Data 4

Data 5

Data 6

Data 1

Data 5

Data 7

Data 8

FIŞIER 1

FIŞIER 2

FIŞIER 3

PRELUCRARE 1

PRELUCRARE 2

PRELUCRARE 3

Raport 1

Fişier de

legături

Raport 4

Raport 3

Raport 2

Raport 5

DATE FIŞIERE PRELUCRĂRI IEŞIRI

Figura 1.1. Sistem informatic bazat pe organizarea datelor în fişiere independente

Spre exemplu, Data2 este prezentă în două fişiere de date, FIŞIER1 şi FIŞIER2. Dacă, prin

program, se modifică formatul sau valoarea acesteia în FIŞIER1, modificarea nu se face automat şi în

FIŞIER2; prin urmare, o aceeaşi dată, Data2, va prezenta două valori diferite în cele două fişiere, iar

necazurile bat la uşă: informaţiile furnizate de sistemul informatic sunt redundante şi prezintă un mare

risc de pierdere a coerenţei.

Se poate proiecta un mecanism de menţinere a integrităţii datelor, astfel încât actualizarea unei

date într-un fişier să atragă automat actualizarea tuturor fişierelor de date în care aceasta apare, însă, în

sistemele mari, care gestionează volume uriaşe de informaţii, implementarea unui asemenea mecanism

este extrem de complexă şi costisitoare. În plus, fişierele de date sunt uneori proiectate şi

implementate la distanţe mari în timp, în formate diferite: de exemplu, FIŞIER1 este posibil să fi fost

Page 5: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 5

creat cu ajutorul limbajului COBOL, FIŞIER2 în FORTRAN iar FIŞIER3 în BASIC. În asemenea

condiţii, punerea în operă a mecanismului de menţinere a integrităţii devine o utopie.

Chiar numai şi din cele prezentate mai sus, se pot desprinde câteva dezavantaje ale organizării

datelor după modelul fişierelor independente:

- Redundanţa şi inconsistenţa datelor: o aceeaşi dată apare în mai multe fişiere; în aceste cazuri există

riscul modificării acesteia într-un fişier fără a face modificările şi în toate celelalte fişiere.

- Dificultatea accesului. Într-o întreprindere, o aceeaşi informaţie este exploatată de mai mulţi

utilizatori. Spre exemplu, pentru departamentul care se ocupă cu gestiunea stocurilor, intrările de

materiale trebuie ordonate pe magazii (depozite) şi repere, în timp ce pentru departamentul care se

ocupă cu decontările cu partenerii de afaceri ai întreprinderii, intrările trebuie ordonate pe furnizori ai

materialelor. Or, fişierele tradiţionale nu facilitează accesarea datelor după mai multe criterii, specifice

diferiţilor utilizatori sau grupuri de utilizatori.

- Izolarea datelor: când datele sunt stocate în formate diferite, este dificil de scris programe care să

realizeze accesul într-o manieră globală a tuturor celor implicate în derularea unei tranzacţii.

- Complexitatea apăsătoare a actualizărilor. O actualizare presupune adăugarea, modificarea sau

ştergerea unor informaţii din fişiere. Cum prelucrările se desfăşoară în timp real, de la mai multe

terminale (în mediile multi-utilizator), pot apare situaţii conflictuale atunci când doi utilizatori doresc

modificarea simultană a unei aceleaşi date. Rezolvarea acestui gen de conflicte presupune existenţa

unui program-supervizor al prelucrărilor, care este greu de realizat cu o multitudine de fişiere, create la

distanţă în timp şi, în formate diferite.

- Problemele de securitate ţin de dificultatea creării unui mecanism care să protejeze pe deplin datele

din fişiere de accesul neautorizat.

- Probleme legate de integritatea datelor. Informaţiile stocate în fişiere sunt supuse la numeroase

restricţii semantice. Toate aceste restricţii alcătuiesc mecanismul de integritate a datelor, deosebit de

complex în mediile de lucru multi-utilizator şi eterogene.

- Inabilitatea de a obţine răspunsuri rapide la probleme ad-hoc simple.

- Costul ridicat se datorează gradului mare de redundanţă a datelor, eforturilor deosebite ce trebuie

depuse pentru interconectarea diferitelor tipuri de fişiere de date şi pentru asigurarea funcţionării

sistemului în condiţiile respectării unui nivel minim de integritate şi securitate a informaţiilor.

- Inflexibilitatea faţă de schimbările ulterioare, ce sunt inerente oricărui sistem informaţional.

- Modelarea indecvată a lumii reale.

Aceste dezavantaje sunt mai mult decât convingătoare, încât vă puteţi întreba dacă au existat

aşa inconştienţi care să-şi arunce banii pe apa... fişierelor independente. Ei bine, o serie de aplicaţii

dezvoltate în anii '60 sau '70 au fost moştenite şi folosite până zilele noastre. De ce ? Datorită

consistentelor sume investite, care au putut fi amortizate (trecute pe costuri) doar în ani buni, chiar

decenii. Un alt motiv a fost însă funcţionalitatea şi viteza unor asemenea aplicaţii, precum şi

experienţa acumulată de o largă cateogorie de profesionişti în ale IT-ului.

1.3 CE ESTE O BAZĂ DE DATE ?

Sintagma bază de date apare pentru prima dată în titlul unei conferinţe organizate la Santa

Monica (California) în 1964 de System Development Corporation. Consacrarea definitivă a termenului

Page 6: BD1 2010 FotacheM Integral

6 Baze de date I

este marcată de publicarea în anul 1969, de către CODASYL, în cadrul unei conferinţe dedicate

limbajelor de gestiune a datelor, a primului raport tehnic în care este prezentat conceptul de bază de

date. Faţă de modelul fişierelor independente, noutatea o constituie existenţa unui fişier de descriere

globală a bazei, astfel încât să se poată asigura independenţa programelor faţă de date, după cum o

arată şi figura 1.2.

Avantajele organizării informaţiilor în baze de date decurg tocmai din existenţa acestui fişier

de descriere globală a bazei, denumit, în general, dicţionar de date (alte titulaturi: repertoar de date

sau catalog de sistem). Extragerea şi modificarea datelor, altfel spus, lucrul cu fişierele de date, se

derulează exclusiv prin intermediul dicţionarului în care se găsesc informaţii privitoare la structura

datelor şi restricţiile îndeplinite de acestea.

O bază de date (BD) reprezintă un ansamblu structurat de fişiere, care grupează datele

prelucrate în aplicaţiile informatice ale unei persoane, grup de persoane, întreprinderi, instituţii etc.

Formal, BD poate fi definită ca o colecţie de date aflate în interdependenţă, împreună cu descrierea

datelor şi a relaţiilor dintre ele, sau ca o colecţie de date utilizată într-o organizaţie, colecţie care

este automatizată, partajată, definită riguros (formalizată) şi controlată la nivel central.

Fişier de date n

Dicţionar

de date

B A Z A DE D A T E

Aplicaţia 1

Fişier de date 2

Fişier de date 1

Aplicaţia 2 Aplicaţia 3

Figura 1.2. Schemă de principiu a unei baze de date

Atunci când vorbim despre o bază de date, trebuie avute în vedere două aspecte fundamentale

aceste acesteia, schema şi conţinutul. Organizarea bazei de date se reflectă în schema sau structura sa,

ce reprezintă un ansamblu de instrumente pentru descrierea datelor, a relaţiilor dintre acestea, a

semanticii lor şi a restricţiilor la care sunt supuse. Ansamblul informaţiilor stocate în bază la un

moment dat constituie conţinutul sau instanţierea sau realizarea acesteia. În timp ce volumul prezintă

o evoluţie spectaculoasă în timp, schema unei baze rămâne relativ constantă pe tot parcursul utilizării

acesteia. Corespunzător celor două aspecte complementare, schemă/conţinut, limbajele de programare

dedicate bazelor de date se împart în limbaje de definire a datelor (DDL – Data Definition Language)

şi limbaje de manipulare a datelor (DML - Data Manipulation Language). Limbajele aflate în uz, cum

ar fi SQL-ul, prezintă opţiuni atât pentru declararea structurii, cât şi pentru editarea conţinutului şi

consultarea/interogarea bazei.

Page 7: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 7

Într-un sistem informatic ce utilizează BD, organizarea datelor poate fi analizată din mai multe

puncte de vedere şi pe diferite paliere. De obicei, abordarea se face pe trei nivele: fizic sau intern,

conceptual sau global şi extern.

Nivelul fizic (sau intern). Reprezintă modalitatea efectivă în care acestea sunt "scrise" pe

suportul de stocare - disc magnetic, disc optic, bandă magnetică etc.

Nivelul conceptual (sau global). Este nivelul imediat superior celui fizic, datele fiind privite

prin prisma semanticii lor; interesează conţinutul lor efectiv, ca şi relaţiile care le leagă de alte date.

Reprezintă primul nivel de abstractizare a lumii reale observate. Obiectivul acestui nivel îl constituie

modelarea realităţii considerate, asigurându-se independenţa bazei faţă de orice restricţie tehnologică

sau echipament anume. Toţi utilizatorii îşi exprimă nevoile de date la nivel conceptual, prezentându-le

administratorului bazei de date, acesta fiind cel care are o viziune globală necesară satisfacerii tuturor

cerinţelor informaţionale.

Nivelul extern. Este ultimul nivel de abstractizare la care poate fi descrisă o bază de date.

Structurile de la nivelul conceptual sunt relativ simple, însă volumul lor poate fi deconcertant. Iar dacă

la nivel conceptual baza de date este abordată în ansamblul ei, în practică, un utilizator sau un grup de

utilizatori lucrează numai cu o porţiune specifică a bazei, în funcţie de departamentul în care îşi

desfăşoară activitatea şi de atribuţiile sale (lor). Simplificarea interacţiunii utilizatori-bază, precum şi

creşterea securităţii bazei, sunt deziderate ale unui nivel superior de abstractizare, care este nivelul

extern. Astfel, structura BD se prezintă sub diferite machete, referite, uneori şi ca sub-scheme, scheme

externe sau imagini, în funcţie de nevoile fiecărui utilizator sau grup de utilizatori.

Luând în considerare cele trei nivele de abstractizare, schematizarea unui sistem de lucru cu o

bază de date se poate face ca în figura 1.3.

Definirea structurii

interne de stocare

(Schema internă)

Baza de date memorată pe disc

Imagine globală

(nivel global)

Interfaţă dintre nivelele

fizic şi global

Schema

conceptuală

(globală)

Interfaţă A

dintre nivelele

global şi extern

Interfaţă B

dintre nivelele

global şi extern

Imagine A

(nivel extern)

Imagine B

(nivel extern)

Schemă externă

ASchemă externă

B

AplicaţieComenzi

autonome Aplicaţie AplicaţieComenzi

autonome

Utilizator A1 Utilizator B1 Utilizator B2 Utilizator B3Utilizator A2

SISTEM DE

GESTIUNE A

BAZEI

DE DATE

Figura 1.3. Schematizare a unui sistem de lucru cu o bază de date

Page 8: BD1 2010 FotacheM Integral

8 Baze de date I

Posibilitatea modificării structurii la un nivel, fără a afecta structura nivelului sau nivelurilor

superioare, se numeşte autonomie a datelor stocate în bază, analizabilă pe două paliere.

Autonomia fizică reprezintă posibilitatea modificării arhitecturii bazei la nivel intern, fără ca

aceasta să necesite schimbarea schemei conceptuale şi rescrierea programelor pentru exploatarea bazei

de date. Asemenea modificări sunt necesare uneori pentru ameliorarea performanţelor de lucru (viteză

de acces, mărimea fişierelor etc.). Tot autonomia fizică este cea care asigură portarea bazei de date de

pe un sistem de calcul pe altul fără modificarea schemei conceptuale şi a programelor.

Autonomia logică presupune posibilitatea modificării schemei conceptuale a bazei (modificare

datorată necesităţii rezolvării unor noi cerinţe informaţionale) fără a rescrie programele de exploatare.

Autonomia logică a datelor este mai greu de realizat decât autonomia fizică, deoarece programele de

exploatare sunt dependente, în foarte mare măsură, de structura logică a datelor pe care le consultă şi

actualizează, în ciuda existenţei dicţionarului de date. Fireşte, un element important îl reprezintă şi

anvergura modificării schemei conceptuale.

Datele stocate într-o BD prezintă, într-o măsură mai mare sau mai mică, următoarele

caracteristici:

partajabilitate – disponibilitate pentru un mare număr de utilizatori şi aplicaţii;

persistenţă – existenţă permanentă, din momentul preluării în bază până în momentul

actualizării sau ştergerii;

securitate – protejarea de accesul neautorizat, atât în ceea ce priveşte citirea şi copierea, cât şi

modificarea şi ştergerea;

validitate – referită şi ca integritate sau corectitudine – priveşte gradul de adecvare dintre

datele din bază şi realitatea, procesele pe care le reflectă aceste date;

consistenţă – ori de câte ori diverse aspecte ale proceselor sau fenomenelor reale sunt preluate

în bază sub forma a doua sau mai multor entităţi sau atribute, aceste entităţi/atribute trebuie să

fie în concordanţă unele ce celelalte, să respecte relaţiile existente între aspectele

proceselor/fenomenelor reale;

nonredundanţă - pe cât posibil, o entitate din realitate ar trebui să aibă un singur corespondent

în baza de date;

independenţă – priveşte autonomia logică şi fizică evocate mai sus.

1.4 MODELE DE ORGANIZARE A DATELOR ÎN BD

Nucleul unei baze de date îl reprezintă dicţionarul de date ce conţine structura bazei, structură

care se materializează prin instrucţiuni scrise cu ajutorul unui limbaj de definire a datelor (DDL).

Analiza, proiectarea şi implementarea structurii (schemei) bazei se realizează utilizând un model de

date. Un asemenea model reprezintă un ansamblu de instrumente conceptuale care permit descrierea

datelor, relaţiilor dintre ele, a semanticii lor, ca şi a restricţiilor la care sunt supuse.

Modelul datelor este o reprezentare a obiectelor lumii reale şi a evenimentelor asociate lor.

Presupune un demers de abstractizare care se concentrează pe aspectele esenţiale ale organizaţiei/apli-

caţiei, furnizând conceptele de bază şi notaţiile care vor permite utilizatorilor bazelor de date să

comunice clar şi rapid informaţiile şi cunoştinţele lor despre datele organizaţiei.

Page 9: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 9

O grupare "tradiţională" a modelelor utilizate în bazele de date delimitează trei categorii:

modele logice bazate pe obiect, modele logice bazate pe înregistrare şi modele fizice. Din punctul

nostru de vedere, interesează numai nivelele conceptual şi extern de abstractizare a datelor; de aceea,

vom prezenta, în linii mari, numai reprezentanţii principali ai primelor două categorii.

Modelul ierarhic. Primele produse-software (Sisteme de Gestiune a Bazelor de Date - SGBD)

lucrau bu baze de date ierarhice. Structura datelor este prezentată sub forma unui arbore - vezi figura

1.4 -, partea superioară a arborelui fiind rădăcina (arborele este văzut "cu verdele în jos"). Un nod-tată

poate avea mai multe noduri-fii. Un fiu nu poate exista independent de tatăl său. Legătura

(reprezentată prin linie) se face exclusiv între tată şi fii. Orice fiu poate fi şi tată, deci poate avea, la

rândul său, fii.

Astfel, în clasa a IX-a A (profil Uman) există doi elevi: Pop I. Vasile ce are numărul matricol

4545 şi domiciliază pe strada Primăverii nr. 22, iar al doilea, Ion V. Viorel, are matricolul 4550 şi

locuieşte pe strada Corupţiei nr. 13 bis. Vasile a luat la fizică (profesor Cernat Dan, născut pe 21

aprilie 1968) un 5 pe 8 februarie şi un 9 pe 15 februarie. La istorie (profesor Pal Dana, născută pe 12

noiembrie 1975) stă ceva mai bine, având un 8 (căpatat pe 21 februarie), un 7 (pe 28 februarie) şi un 8

(pe 7 martie). În acelaşi mod poate fi intepretată figura pentru Ion V. Viorel, cu menţiunea specială că

istoria nu e punctul lui forte.

Figura 1.4. Arbore de structură specific modelului ierarhic

Modul în care înregistrările se înlănţuie presupune folosirea unor pointeri (un soi de adrese

fizice) ce nu sunt reprezentaţi în figură (din considerente umanitare). Aflarea informaţiilor din baza de

date presupune navigarea între înregistrăr cu ajutorul pointerilor. În plus, modificarea structurii bazei

de date presupune actualizarea înlănţuirii pointerilor, ceea ce extrem de laborios. A altă problemă a

acestui model o reprezintă imposibilitatea reprezentării cazului în care un copil este, pardon,

"rezultatul" a mai mulţi taţi. Situaţia nu este atât de scandaloasă cum v-aţi închipuit, şi aceasta doarece

modelul ierarhic nu face nici o referire la mama fiului.

Figura 1.4 este relevantă pentru gradul de redundanţă impus de reprezentarea ierarhică. Codul,

denumirea, numărul de ore ale fiecărei discipline, precum şi datele despre profesor, apar pentru fiecare

elev, deşi acestea sunt comune la nivel de an de studii (numele disciplinei, numărul de ore) sau la nivel

de clasă (profesorul titular).

Page 10: BD1 2010 FotacheM Integral

10 Baze de date I

Modelul reţea. Este o dezvoltare a modelului ierarhic, prin care se pot reprezenta şi situaţiile

în care un fiu "posedă" mai mulţi taţi. Înregistrările sunt privite în BD ca o colecţie de grafuri cu o

structură asemănătoare celei din figura 1.5. Navigarea se face tot prin pointeri.

Figura. 1.5. Graf specific reprezentării utilizând modelul reţea

Modelul reţea elimină o serie de redundanţe specifice modelului ierarhic. Spre exemplu, cazul

ilustrat în figura 1.4 poate fi reprezentat după logica modelului reţea ca în figura 1.5. Se observă că

fiecare disciplină (şi profesorul care o predă la clasa respectivă) apare o singură dată, fiind legată prin

linii de toate notele obţinute de fiecare dintre elevi.

Modelul relaţional a fost următorul în ordinea cronologică şi rămâne cel care domină copios

piaţa bazelor de date şi la acest moment, motiv pentru care cea mai mare parte a acestui curs îi este

dedicată.

Modelul obiectual. Începând cu anii ‟60, în programare şi, ceva mai târziu, în analiză şi

proiectare, orientarea pe obiecte (OO) a avut un succes uriaş, reuşit să depăşească metodologiile

structurate. Pe baza acestui succes, s-a crezut că şi în materie de baze de date, modelul obiectual îl va

surclasa pe cel relaţional. Rezultatele sunt însă deprimante pentru suporterii OO-ului, piaţa SGBD OO

fiind sub 6% din valoarea totală a pieţii bazelor de date.

Modelul relaţional-obiectual. Este un model mai recent ce încearcă să valorifice deopotrivă

atuurile relaţionalului cu orientarea pe obiecte. Deşi privit mai degrabă cu neîncredere în cercurile

teoreticienilor, acest model se impune încet-încet datorită marilor producători de software dedicat

bazelor de date. Ca şi în cazul modelului „curat OO”, tehnologia relaţional-obiectuală nu face...

obiectul cursului de faţă, fiind studiată la discipla Baze de date avansate din ciclul II (specializarea

Informatică economică).

Page 11: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 11

Capitolul 2

MODELUL RELAŢIONAL

Obiective:

I. Explicarea noţiunilor fundamentale ale modelului raţional:

relaţie/tabelă, domeniu, atribut/coloană, tuplu/linie

II. Discutarea restricţiilor implementabile în bazele de date relaţionale:

restricţia de domeniu, atomicitarea, nenulitatea, unicitatea, restricţia

referenţială, regulile de validare la nivel de atribut şi înregistrare

III. Exemplificarea schemei şi conţinutului unei baze de date relaţionale

Page 12: BD1 2010 FotacheM Integral

12 Baze de date I

Modelul relaţional de organizare a datelor s-a conturat în două articole publicate în 1969 şi

1970 de către E.F. Codd, matematician la centrul de cercetări din San Jose (California) al firmei

IBM2. În acel moment, tehnologia bazelor de date era centrată pe modelele ierarhic şi reţea, modele ce

depind într-o mai mare măsură de organizarea internă a datelor pe suportul de stocare (celebrii pointeri

pe care i-am pomenit acum câteva pagini). Codd a propus o structură de date tabelară, independentă de

tipul de echipamente şi software de sistem pe care este implementată, structură "înzestrată" cu o serie

de operatori pentru extragerea datelor. Deşi puternic matematizat, modelul relaţional este relativ uşor

de înţeles. Faţă de modelele ierarhice şi reţea, modelul relaţional prezintă câteva avantaje:

propune structuri de date uşor de utilizat;

ameliorează independenţa logică şi fizică;

pune la dispoziţia utilizatorilor limbaje ne-procedurale;

optimizează accesul la date;

îmbunătăţeşte integritatea şi confidenţialitatea datelor;

ia în calcul o largă varietate de aplicaţii;

abordează metodologic definirea structurii bazei de date.

Un model de date are trei piloni: componenta structurală, adică modul în care, efectiv, la nivel

logic, datele sunt stocate în bază, componenta de integritate, adică regulile ce pot fi declarate pentru

datele din bază şi o componentă manipulatorie, adică modul în care obţinem informaţii din bazele de

date (ceea ce presupune o serie de operatori aplicabili uneia sau mai multor relaţii). Modelului

relaţional îi este asociată teoria normalizării, care are ca scop prevenirea comportamentului aberant al

relaţiilor în momentul actualizării lor, eliminarea datelor redundante şi înlesnirea înţelegerii legăturilor

semantice dintre date (vezi capitolul 3). Prezentul capitol va fi dedicat componentei structurale şi celei

de integritare din modelul relaţional.

Deşi puternic contestat, şi cu neajunsurile sale, modelul relaţional de organizare a bazelor de

date rămâne cel mai utilizat. Cu foarte puţine excepţii, toate aplicaţiile software realizate pentru bănci,

buticuri, universităţi (ordinea este, în ciuda aparenţelor, pur întâmplătoare) sunt realizate cu

produse/instrumente ce gestionează baze de date relaţionale.

2.1 STRUCTURĂ

Modelul relaţional al datelor se poate defini printr-o serie de structuri de date (relaţii alcătuite

din tupluri), operaţii aplicate asupra structurilor de date (selecţie, proiecţie, joncţiune etc.) şi reguli de

integritate care să asigure consistenţa datelor (chei primare, restricţii referenţiale s.a.).

La modul simplist, o bază de date relaţională (BDR) poate fi definită ca un ansamblu de relaţii

(tabele); fiecare tabelă (sau tabel), alcătuită din linii (tupluri), are un nume unic şi este stocată pe

suport extern (de obicei disc). La intersecţia unei linii cu o coloană se găseşte o valoare atomică

(elementară). O relaţie conţine informaţii omogene legate de anumite entităţi, procese, fenomene:

CĂRŢI, STUDENŢI, LOCALITĂŢI, PERSONAL, FACTURI etc. Spre exemplu, în figura 2.1 este

reprezentată tabela CLIENŢI.

2

Extrase din lucrarea [Codd70] se găsesc la adresa http://www.acm.org/classics/nov95/. De asemenea, o excelentă analiză a articolelor lui Codd este în [Date98].

Page 13: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 13

Figura 2.1. Relaţia (tabela) CLIENŢI

În teoria relaţională se foloseşte termenul relaţie. Practica, însă, a consacrat termenul tabelă

(engl. table). Un tuplu sau o linie este o succesiune de valori de diferite tipuri. În general, o linie

regrupează informaţii referitoare la un obiect, eveniment etc., altfel spus, informaţii referitoare la o

entitate: o carte (un titlu sau un exemplar din depozitul unei biblioteci, depinde de circumstanţe), un/o

student(ă), o localitate (oraş sau comună), un angajat al firmei, o factură emisă etc. Figura 2.2 conţine

al doilea tuplu din tabela CLIENŢI, tuplu referitor la forma MODERN SRL. Linia de mai sus este

alcătuită din patru valori ce desemnează: codul, numele, adresa şi codul poştal al localităţii şi adresei

referitoare la clientul MODERN SRL.

Figura 2.2. Un tuplu al tabelei CLIENŢI

Teoretic, orice tuplu reprezintă o relaţie între clase de valori (în cazul nostru, între patru clase

de valori); de aici provine sintagma baze de date relaţionale, în sensul matematic al relaţiei, de

asociere a două sau mai multe elemente. Fireşte, toate tuplurile relaţiei au acelaşi format (structură),

ceea ce înseamnă că în tabela CLIENŢI, din care a fost extrasă linia din figura 2.2, fiecare linie este

constituită dintr-o valoare a codului, o valoare a numelui, o valoare a adresei, şi o valoare a codului

poştal. Ordinea tuplurilor nu prezintă importanţă din punctul de vedere al conţinutul informaţional al

tabelei.

Fiecare atribut este caracterizat printr-un nume şi un domeniu de valori pe care le poate lua.

Domeniul poate fi definit ca ansamblul valorilor acceptate (autorizate) pentru un element component

al relaţiei:

într-o tabelă destinată datelor generale ale angajaţilor, pentru atributul Sex, domeniul este

alcătuit din două valori: Femeiesc şi Bărbătesc;

pentru atributul Regiune, domeniul, deşi limitat, este ceva mai mare; valorile autorizate pot

fi: Dobrogea, Banat, Transilvania, Oltenia, Muntenia, Moldova.

domeniul atributului Jud este alcătuit din indicativele auto (două caractere) ale fiecărui judeţ

(plus Bucureşti).

Page 14: BD1 2010 FotacheM Integral

14 Baze de date I

domeniul atributului Judet este alcătuit din numele fiecărui judeţ (plus Bucureşti).

domeniul unui atribut precum PreţUnitar, care se referă la preţul la care a fost vândut un

produs/serviciu, este cu mult mai larg, fiind alcătuit din orice valoarea cuprinsă între 1 şi

99999999 lei (ceva mai noi).

Fireşte, în funcţie de specificul bazei de date, domeniul poate fi extins sau restrâns după

cerinţe. De exemplu, la regiunile luate în discuţie mai sus, mai pot fi adăugate şi provincii precum:

Bucovina, Maramureş, Crişana.

Şi acum, câteva senzaţii tari (urmează un pic de matematică) ! Dacă notăm cu D1 domeniul

atributului CodClient, cu D2 domeniul atributului NumeClient, cu D3 domeniul pentru Adresa, şi cu

D4 domeniul atributul CodPostal, se poate spune că fiecare linie a tabelei CLIENŢI este un tuplu de

patru elemente, iar relaţia în ansamblu corespunde unui subansamblu din ansamblul tuturor tuplurilor

posibile alcătuite din patru elemente, ansamblu care este produsul cartezian al celor patru domenii.

În general, orice relaţie R poate fi definită ca un subansamblu al produsului cartezian de n domenii Di:

R D x D x D x ...x D 1 2 3 n ,

n fiind denumit gradul sau ordinul relaţiei. Relaţiile de grad 1 sunt unare, cele de grad 2 - binare, ...,

cele de grad n - n-are. Această definiţie pune în evidenţă aspectul constant al relaţiei, de independenţă

în timp (se spune că în acest caz relaţia este definită ca un predicat).

O a doua definiţie abordează o relaţie R ca un ansamblu de m-uplete (m-tupluri) de valori:

R = { t , t , ..., t , ..., t }1 2 k m , unde t (d ,d , ..., d ,...,dk k1 k2 ki kn ) în care:

dk1 este o valoare în D1, dk2 este o valoare în D2, … , dkn este o valoare în Dn;

n - reprezintă ordinul lui R;

m - cardinalitatea lui R.

Pentru un (mic) plus de claritate, vezi figura 2.3.

Figura 2.3. Ilustrarea celei de-a doua definiţii a unei relaţii

Reprezentarea sub formă de tabelă, deci ca ansamblu de tupluri, pune în evidenţă aspectul

dinamic, variabil al relaţiei. Abordarea predicativă (prima definiţie) sau ansamblistă (a doua definiţie)

reprezintă un criteriu important de delimitare a limbajelor de interogare a bazelor de date relaţionale.

Reţinem corespondenţa noţiunilor relaţie-tabelă, tuplu-linie şi atribut-coloană.

Page 15: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 15

Numărul de tabele pe care le conţine o bază de date, atributele “adunate” în fiecare tabelă,

domeniul fiecăruia dintre atribute prezintă diferenţe majore de la o bază la alta, chiar dacă uneori

reflectă acelaşi tip de procese. Intrăm astfel în sfera proiectării bazelor de date, a dependenţelor şi

normalizării.

Relaţia CLIENŢI conţine informaţii despre firmele cărora compania noastră le vinde

produsele pe care producem şi/sau comercializăm. Fiecare linie se referă la un singur client. În figura

2.1 pe a treia linie a tabelei apare o valoarea curioasă notată NULL. Valoarea NULL este considerată o

metavaloare şi indică faptul că, în acel loc, informaţia este necunoscută sau inaplicabilă (în acest caz

nu cunoaştem adresa clientului Modern SRL). Valoarea NULL este diferită, însă de valorile 0 sau

spaţiu. Uneori, importanţa sa este (din păcate) majoră în expresii şi funcţii, după cum ştiu cei cu

oarecare experienţă în limbajului SQL.

În încheiere, principalele caracteristici ale unei relaţii sunt sistematizate după cum urmează:

În cadrul unei baze de date, o relaţie prezintă un nume distinct de al celorlalte relaţii.

Valoarea unui atribut într-un tuplu oarecare conţine a singură valoare (o valoare atomică sau

elementară).

Fiecare atribut are un nume distinct.

Orice valoare a unui atribut face parte din domeniul pe care a fost definit acesta.

Ordinea dispunerii atributelor în relaţie nu prezintă importanţă.

Fiecare tuplu este distinct, adică nu pot exista două tupluri identice.

Ordinea tuplurilor nu influenţează conţinutul informaţional al relaţiei.

2.2 RESTRICŢII

De ce ne interesează restricţiile într-o bază de date ? Termenul de restricţie este oarecum

iritant, atât pentru studenţi, cât şi pentru profesori, deoarece semnalează existenţa unor constrângeri

instituite şi oarecum obligatorii, şi, de vreme ce sunt impuse, însemnă că nu sunt prea plăcute (decât,

în cel mai bun caz, pentru cel care le-a instituit). Partea cea mai enervantă este că respectarea

restricţiilor este (supra)vegheată de o anumită autoritate înzestrată cu anumite instrumente de

constrângere, de la bastoane de cauciuc, la creşterea şi scăderea impozitelor, salariilor, banilor de

buzunar etc.

Ei bine, în bazele de date, restricţiile sunt ceva mai acceptabile. Cei care lucrează cu bazele de

date sunt foarte interesaţi în declararea restricţiilor, pentru că, odată definite, de respectarea lor se va

îngriji sistemul de gestiune a bazelor de date (adică programele de lucru cu bazele de date). Esenţial

este că, ajutaţi de restricţii, putem creşte gradul de corectitudine şi de încredere al datelor din bază. În

cele ce urmează vor fi prezentate pe scurt cele mai importante restricţii definibile într-o bază de date

relaţională: restricţia de domeniu, de atomicitate, de unicitate, referenţială şi restricţiile-utilizator.

2.2.1 Restricţia de domeniu

După cum am văzut în paragraful anterior, un atribut este definit printr-un nume şi un

domeniu. Orice valoare a atributului trebuie să se încadreze în domeniul definit. Există mai multe

moduri de percepţie a acestei restricţii.

Page 16: BD1 2010 FotacheM Integral

16 Baze de date I

O parte din informaticieni substituie domeniul tipului atributului: numeric, şir de caractere,

dată calendaristică, logic (boolean) etc. şi, eventual, lungimii (numărul maxim de poziţii/caractere pe

care se poate “întinde” un atribut). După cum se observă, este luat în calcul numai aspectul sintactic al

domeniului. Faptul că indicativul auto al unui judeţ (vezi plăcuţele de înmatriculare) poate fi una din

valorile: IS, TM, B etc. reprezintă o restricţie de comportament sau, mai simplu, o restricţie definită de

utilizator.

Cea de-a doua categorie priveşte domeniul deopotrivă sintactic şi semantic. Astfel, domeniul

sintactic al atributului Jud (indicativul judeţului) este un şir de două caractere, obligatoriu litere (sau o

literă şi un spaţiu, pentru Bucureşti), şi, chiar mai restrictiv, literele sunt obligatoriu majuscule. Din

punct de vedere semantic, indicativul poate lua una din valorile: IS, TM …

Majoritatea SGBD-urilor permit definirea tuturor elementelor ce caracterizează domeniul

(sintactic şi semantic) atributului Jud prin declararea tipului şi lungimii atributului şi prin aşa-numitele

reguli de validare la nivel de câmp (field validation rule). Sunt însă şi produse la care domeniul poate

fi definit explicit, sintactic şi semantic, dându-i-se un nume la care vor fi legate atributele în momentul

creării tabelelor.

2.2.2 Atomicitate

Conform teoriei bazelor de date relaţionale, orice atribut al unei tabele oarecare trebuie să fie

atomic, în sensul imposibilităţii descompunerii sale în alte atribute. Implicit, toate domeniile unei baze

de date sunt musai atomice (adică elementare). În aceste condiţii, se spune că baza de date se află în

prima formă normală sau prima formă normalizată (1NF).

Astăzi, atomicitatea valorii atributelor a devenit o ţintă predilectă a “atacurilor duşmănoase” la

adresa modelului relaţional, datorită imposibilităţii înglobării unor structuri de date mai complexe,

specifice unor domenii ca: proiectare asistată de calculator, baze de date multimedia etc. Mulţi autori,

dintre care merită amintiţi cu deosebire Chris J. Date şi Hugh Darwen, se opun ideii de atomicitate

formulată de Codd.

La drept vorbind, singurul lucru cert despre atomicitate este relaticitatea acesteia. Să luăm un

exemplu de atribut compus (non-atomic) - Adresa. Fiind alcătuită din Stradă, Număr, Bloc, Scară, Etaj,

Apartament, discuţia despre atomicitatea adresei pare de prisos, iar descompunerea sa imperativă.

Trebuie însă să ne raportăm la obiectivele bazei. Fără îndoială că pentru BD a unei filiale CONEL sau

ROMTELECOM, sau pentru poliţie, preluarea separată a fiecărui element constituent al adresei este

foarte importantă. Pentru un importator direct, însă, pentru un mare en-grossist sau pentru o firmă de

producţie lucrurile stau într-o cu totul altă lumină. Partenerii de afaceri ai acestora sunt persoane

juridice, iar adresa interesează numai la nivel general, caz în care atributul Adresa nu este considerat a

fi non-atomic.

Alte exemple de atribute ce pot fi considerate, în funcţie de circumstanţe, simple sau compuse:

DataOperaţiuniiBancare (Data + Ora), BuletinIdentitate (Seria+Număr), NrÎnmatriculareAuto (privit

global, sau pe cele trei componente: număr, judeţ, combinaţie trei de litere).

O relaţie (tabelă) în 1NF nu trebuie să conţină atribute care se repetă ca grupuri (grupuri

repetitive). Într-o altă formulare, toate liniile unei tabele trebuie să conţină acelaşi număr de atribute.

Fiecare celulă a tabelei (intersecţia unei coloane cu o linie), altfel spus, valoarea unui atribut pe o linie

Page 17: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 17

(înregistrare), trebuie să fie atomică. Detalii despre atomicitate şi grupuri repetitive găsiţi în [Fotache

2005]. Noi o să reluăm această discuţie şi în capitolul 3.

2.2.3 Nenulitate

Modelul relaţional acceptă ca, atunci când nu se cunoaşte valoarea unui atribut pentru o

anumită entitate, sau când pentru acel obiect, entitate, persoană etc. atributul este inaplicabil, să se

folosească (meta)valoare NULL. După cum am discutat, celui de-al treilea client din figura 2.1 nu i

cunoaşte adresa. Dacă am avea o tabelă PRODUSE cu atributele CodProdus, DenumireProdus, UM,

Culoare, este posibil ca, pentru anumite sortimente, cum ar tricouri, cămăşi, jachete, atributul să fie

important, în timp ce pentru altele, precum vodcă, cafea, lapte atributul culoare să nu furnizeze nici o

informaţie, adică să nu fie aplicabil. Iar dacă laptele poate fi doar alb, vodca chiar că nu are culoare

(sunt foarte mulţi specialişti în acest domeniu, îi puteţi întreba !).

Într-o bază de date relaţională avem posibilitatea de a le impune unora dintre atribute (sau

tuturora) să aibă întotdeauna valori specificate, altfel spus, le interzicem valorile nule, în timp cel altor

atribute li se pot permite valori nule. Ca regulă, atributele importante, ce ţin de identificarea sau

caracterizarea unei entităţi, proces, fenomen, precum şi cele implicitate în calculul unor informaţii

importante, sunt declarate NOT NULL, iar atributele fără importanţă deosebită pot fi mai relaxate.

2.2.4 Unicitate

După cum am discutat în primul paragraf al acestui capitol, într-o relaţie nu pot exista două

linii identice (două linii care prezintă aceleaşi valori pentru toate atributele). Mai mult, majoritatea

relaţiilor prezintă un atribut, sau o combinaţie de atribute, care diferenţiază cu siguranţă un tuplu de

toate celelalte tupluri ale relaţiei. Cheia primară a unei relaţii (tabele) este un atribut sau un grup de

atribute care identifică fără ambiguitate fiecare tuplu (linie) al relaţiei (tabelei). După Codd, există trei

restricţii pe care trebuie să le verifice cheia primară:

unicitate: o cheie identifică un singur tuplu (linie) al relaţiei.

compoziţie minimală: atunci când cheia primară este compusă, nici un atribut din cheie nu

poate fi eliminat fără distrugerea unicităţii tuplului în cadrul relaţiei; în cazuri limită, o cheie

poate fi alcătuită din toate atributele relaţiei.

valori non-nule: valorile atributului (sau ale ansamblului de atribute) ce desemnează cheia

primară sunt întotdeauna specificate, deci ne-nule şi, mai mult, nici un atribut din compoziţia

cheii primare nu poate avea valori nule; această a treia condiţie se mai numeşte şi restricţie a

entităţii.

Joe Celko inventariază patru proprietăţi dezirabile pentru o cheie3:

Familiaritate: valorile cheii să fie uşor de înţeles pentru utilizatori.

Stabilitate: valorile cheii nu trebuie să fie volatile.

Minimalitate

Simplitate: sunt de preferat chei scurte şi simple.

3 [Celko99], p.247

Page 18: BD1 2010 FotacheM Integral

18 Baze de date I

Tot el atrage atenţia ca cele patru proprietăţi pot intra, uneori, în conflict. O cheie stabilă poate să se

dovedească complexă şi dificil de gestionat. Identificarea cheii primare pentru o relaţie face parte

(împreună cu stabilirea tabelelor prin gruparea atributelor, stabilirea celorlalte restricţii) din procesul

de proiectare a bazei de date. Uneori, precum în cazul tabelei JUDEŢE, stabilirea cheii primare nu

ridică probleme. Cum fiecare judeţ are un indicativ auto unic, rezultă că atributul Jud candidează la

postul de cheie a relaţiei. La fel se poate spune şi despre denumirea judeţului. Rezultă că relaţia

JUDEŢE prezintă două chei candidate: Jud şi Judet. Alegerea unei dintre cheile candidat are în vedere

criterii precum lungimea, uşurinţa în reţinere şi/sau editare. Fiind mai scurt, desemnăm atributul Jud

drept cheie primară, situaţie în care Judet devine cheie alternativă.

În tabela CLIENŢI cheia primară este simplă - CodClient, CodClient reprezintă un număr unic

asociat fiecărei firme căreia i-am făcut vânzări. Există însă suficiente cazuri în care cheia primară este

compusă din două, trei s.a.m.d. sau, la extrem, toate atributele relaţiei. Să luăm spre analiză o relaţie

PERSONAL care conţine date generale despre angajaţii firmei. Fiecare tuplu al relaţiei se referă la un

angajat, atributele fiind: Nume, Prenume, DataNaşterii, Vechime, SalariuTarifar.

- atributul Nume nu poate fi cheie, deoarece chiar şi într-o întreprindere de talie mijlocie, este

posibil să existe doi angajaţi cu acelaşi nume.

- dacă apariţia a două persoane cu nume identice este posibilă, atunci apariţia a două persoane

cu acelaşi Prenume este probabilă.

- nici unul din aceste atributele DataNaşterii, Vechime, SalariuTarifar nu poate fi "înzestrat"

cu funcţiunea de identificator.

În acest caz, se încearcă gruparea a două, trei, patru s.a.m.d. atribute, până când se obţine

combinaţia care va permite diferenţierea clară a oricărei linii de toate celelalte. Combinaţia

Nume+Adresă pare, la primele două vederi, a îndeplini "cerinţele" de identificator. Ar fi totuşi o

problemă: dacă în aceeaşi firmă (organizaţie) lucrează împreună soţul şi soţia ? Ambii au, de obicei,

acelaşi nume de familie şi, tot de obicei, acelaşi domiciliu. Este adevărat, cazul ales nu este prea

fericit. Dar este suficient pentru “compromiterea” combinaţiei.

Următoarea tentativă este grupul Nume+Prenume+Adresă, combinaţie neoperantă dacă în

organizaţie lucrează tatăl şi un fiu (sau mama şi o fiică) care au aceleaşi nume şi prenume şi domiciliul

comun. Ar rămâne de ales una dintre soluţiile (Nume+Prenume+Adresă+Vechime) sau

(Nume+Prenume+Adresa+DataNaşterii).

Oricare din cele două combinaţii prezintă riscul violării restricţiei de entitate, deoarece este

posibil ca, la preluarea unui angajat în bază, să nu i se cunoască adresa sau data naşterii, caz în care

atributul respectiv ar avea valoarea NULL. Dificultăţile de identificare fără ambiguitate a angajaţilor au

determinat firmele ca, la angajare, să aloce fiecărei persoane un număr unic, număr denumit Marcă.

Prin adăugarea acestui atribut la cele existente, pentru relaţia PERSONAL problema cheii primare este

rezolvată mult mai simplu. Actualmente, sarcina este simplificată şi prin utilizarea codului numeric

personal (CNP), combinaţie de 13 cifre care prezintă avantajul că rămâne neschimbată pe tot parcursul

vieţii persoanei.

Pe lângă noţiunile cheie-candidat, cheie primară, cheie alternativă, modelul relaţional

utilizează şi sintagma supercheie. După Celko4 o supercheie poate fi definită ca un set de coloane ce

îndeplineşte condiţia de cheie (unicitate), dar care conţine cel puţin un subset care este, el însuşi, cheie.

4 [Celko99], p.52

Page 19: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 19

Cu alte cuvinte, din cele trei condiţii ale cheii primare, o supercheie o îndeplineşte numai pe prima

(unicitate), fără a avea însă compoziţia minimală şi fără a pune problema restricţiei de entitate (valorile

nule ale atributelor componente).

Domeniul unui atribut care este cheie primară într-o relaţie este denumit domeniu primar.

Dacă într-o relaţie există mai multe combinaţii de atribute care conferă unicitate tuplului, acestea sunt

denumite chei candidate. O cheie candidată care nu este identificator primar este referită ca şi cheie

alternativă.

2.2.5 Restricţia referenţială

O bază de date relaţională este alcătuită din relaţii (tabele) aflate în legătură. Stabilirea

legăturii se bazează pe mecanismul cheii străine şi, implicit, a restricţiei referenţiale. Figura 2.4

prezintă o relaţie în care sunt implicate tabelele FACTURI şi CLIENŢI.

Figura 2.4. O restricţie referenţială între FACTURI (tabelă-copil) şi CLIENŢI (părinte)

Atributul CodClient joacă un rol de “agent de legătură” între tabelele CLIENŢI şi FACTURI.

Pentru relaţia CLIENŢI, atributul CodClient este cheie primară, în timp ce în tabela FACTURI,

CodClient reprezintă coloana de referinţă sau cheia străină, deoarece numai pe baza valorilor sale se

poate face legătura cu relaţia părinte CLIENŢI.

Cheile străine sau coloanele de referinţă sunt deci atribute sau combinaţii de atribute care pun

în legătură linii (tupluri) din relaţii diferite. Tabela în care atributul de legătură este primară se

numeşte tabelă-părinte (în cazul nostru, CLIENŢI), iar cealaltă tabelă-copil.

Legat de noţiunea de cheie străină apare conceptul de restricţie referenţială. O restricţie de

integritate referenţială apare atunci când o relaţie face referinţă la o altă relaţie. Când două tabele

(relaţii), T1 şi T2, prezintă atributul sau grupul de atribute notat CH, care, pentru T1, este cheie

primară, iar pentru T2 cheie străină, dacă în T2 se interzice apariţia de valori nenule ale CH care nu

există în nici un tuplu din T1, se spune că între T2 şi T1 s-a instituit o restricţie referenţială.

Instituirea restricţiei referenţiale între tabela CLIENŢI (părinte) şi FACTURI (copil) permite

cunoaşterea, pentru fiecare client, a denumirii localităţii şi a judeţului în care-şi are sediul. Dacă în

FATURI ar exista vreo linie în care valoarea atributului CodClient ar fi, spre exemplu 9988, este clar

că acea linie ar fi orfană (nu ar avea linie corespondentă în tabela părinte CLIENŢI).

Page 20: BD1 2010 FotacheM Integral

20 Baze de date I

Observaţii

1. Pentru mulţi utilizatori şi profesionişti ai bazelor de date, denumirea de "relaţional" desemnează faptul că o

bază de date este alcătuită din tabele puse în legătură prin intermediul cheilor străine. Aceasta este, de fapt, a

doua accepţiune a termenului de BDR, prima, cea "clasică", având în vedere percepţia fiecărei linii dintr-o

tabelă ca o relaţie între clase de valori.

2. Majoritatea SGBD-urilor prezintă mecanisme de declararea şi gestionare automată a restricţiilor referenţiale,

prin actualizări în cascadă şi interzicerea valorilor care ar încălca aceste restricţii.

3. Respectarea restricţiilor referenţiale este una din cele mai complicate sarcini pentru dezvoltatorii de aplicaţii

ce utilizează baze de date. Din acest punct de vedere, tentaţia este de a “sparge” baza de date în cât mai

puţine tabele cu putinţă, altfel spus, de a avea relaţii cât mai “corpolente”. Gradul de fragmentare al bazei

ţine de normalizarea bazei de date, care, ca parte a procesului de proiectare a BD, se bazeză pe dependenţele

funcţionale, multivaloare şi de joncţiune între atribute.

2.2.6 Restricţii-utilizator

Restricţiile utilizator mai sunt denumite şi restricţii de comportament sau restricţii ale

organizaţiei. De obicei, aceste restricţii iau forma unor reguli de validare la nivel de atribut, la nivel de

linie/tabelă sau a unor reguli implementate prin declanşatoare (triggere).

O restricţie la nivel de atribut poate preveni introducerea în baza de date a unor valori din alte

intervale decât cele stabilite, în alte formate decât cele acceptate etc. Forma clasică a unei restricţii la

nivel de atribut este o expresie în care apar constante, funcţii-sistem şi, nu în ultimul rând, atributul

respectiv. La orice editare a atributului cu pricina (declanşată în cazul inserării unei linii în tabela din

care face parte, sau la modificarea sa) expresia este evaluată şi dacă rezultatul evaluării este TRUE

(adevărat), atunci inserarea/modificarea este permisă, iar dacă rezultatul este FALSE, atunci

inserarea/modificarea este blocată.

Expresia care defineşte o restricţie la nivel de înregistrare poate conţine două sau mai multe

atribute şi este evaluată la inserarea sau modificarea oricărei linii din tabelă. Tabela FACTURI

conţine, printre altele două atribute “valorice”, unul pentru păstrarea valorii totale (inclusiv TVA)

facturii respective şi un altul care indică “doar” cuantumul TVA colectate pentru factură. Majoritatea

produselor şi serviciilor comercializate la noi în ţară au un procent al taxei pe valoarea adăugată de

19%. Există, însă, şi produse la care procentul poate fi 9% sau chiar scutite de TVA (0%). De aceea,

TVA colectată pentru o factură este mai mică poate fi egală sau decât 19% din valoarea fără tva. Să

punem sub formă de formulă: ValoareaTotală = ValoareaFărăTVA + TVA. Dacă factura conţine

numai produse cu 19%: ValoareaTotală = ValoareaFărăTVA + 0.19 * ValoareaFărăTVA, sau

ValoareaTotală = 1.19 * ValoareaFărăTVA. Dar tabela noastră are doar atributele ValoareTotală şi

TVAColectată, aşa că înlocuim ValoareaFărăTVA prin diferenţa celorlalte două. Scriem:

ValoareTotală = 1.19 * (ValoareTotală – TVAColectată), şi după calcule de matematică superioară,

TVAColectată * 1.19 = 0.19 * ValoareTotală, altfel spus TVAColectată = ValoareTotală * 0.19 / 1.19.

Există şi alte tipuri de restricţii a căror validare presupune “citirea” unor date aflate în tabele

diferite. Spre exemplu, se poate institui o regulă care interzice emiterea unei noi facturi (o nouă

vânzare) dacă datoriile firmei client sunt mai mari de 200000 RON, iar directorul acesteia nu este

membru în partidul/partidele de guvernământ. Acestea reclamă întrebuinţarea unor proceduri speciale,

numite declanşatoare (triggere). Curioşii n-au decât să urmeze specializarea Informatică Economică.

Page 21: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 21

2.3 SCHEMA ŞI CONŢINUTUL UNEI BAZE DE DATE RELAŢIONALE

Aşa cum era prezentat în primul capitol, există două aspecte complementare de abordare a

bazelor de date: schema (structura, intensia) şi conţinutul (instanţierea, extensia). Pentru o BD

relaţională conţinutul unei relaţii este reprezentat de ansamblul tuplurilor ce o alcătuiesc la un moment

dat. Schema unei baze de date conţine denumiri ale tabelelor, numele, tipul şi lungimea atributelor,

restricţii de unicitate, de non-nulitate, restricţii la nivel de atribut, linie şi alte eventuale tipuri de

restricţii de comportament, precum şi restricţii referenţiale. La acestea se adaugă cele privind

drepturile utilizatorilor, definiţia şi restricţiile tabelelor virtuale, indecşi etc. Foarte importante în

lumea profesioniştilor dezvoltării de aplicaţii cu bazele de date sunt procedurile stocate – programe de

forma funcţiilor, procedurilor, pachetelor şi mai ales declanşatoarelor (triggerelor) – care, după cum le

spune şi numele sunt memorate în schema bazei de date şi fac parte integrantă din aceasta.

Schema unei baze de date poate fi reprezentată grafic, avantajul principal fiind o mai bună

sugestivitate. Iată câteva reguli pentru reprezentarea grafică a unei BDR5:

O tabelă se reprezintă pe două linii, pe prima fiind scris numai numele relaţiei iar pe cea de-a doua

numele atributelor. Ordinea prezentării atributelor nu are importanţă. Totuşi, în scopul uşurării

înţelegerii schemei, coloanele care desemnează cheia primară se dispun succesiv (bineînţeles, atunci

când sunt mai multe). La fel şi coloanele care constituie o cheie străină. În general, cheia primară este

plasată la marginea stângă a tabelei (prima sau primele coloane).

Numele coloanelor ce alcătuiesc cheia primară se subliniază cu o linie continuă, iar cele care

alcătuiesc identificatorii secundari se subliniază cu linie punctată.

Numele unei coloane facultative este scris între paranteze.

Dacă o cheie străină este alcătuită din mai multe coloane, se utilizează acolada pentru a le grupa.

O restricţie referenţială este reprezentată printr-o săgeată care pleacă de la numele coloanei de

referinţă (sau de la vârful acoladei, în cazul unui grup de referinţă) şi are vârful în atributele tabelei la

care se face referinţa (tabela părinte).

În capitolele care vor urma, va fi utilizată cu precădere o bază de date “martor”, denumită VÎNZĂRI, a

cărei schemă simplificată este cea din figura 2.5. Discuţia tabelelor, atributelor şi restricţiilor legate de această

bază de date se află în Anexa 1 (vezi portalul disciplinei).

Pe lângă tabele, atribute şi restricţii, dicţionarul de date al unei BD relaţionale conţine multe

alte tipuri de obiecte, dintre care vom zăbovi foarte puţin asupra tabelelor virtuale (view-uri) şi

procedurilor stocate.

O tabelă virtuală stabileşte o legătură semantică între relaţii statice şi/sau alte relaţii dinamice,

nefiind definită explicit, prin tupluri proprii, ca o relaţie de bază (statică), ci printr-o expresie

relaţională. Conţinutul (instanţierea) relaţiei virtuale depinde, la un moment oarecare dat, de conţinutul

tabelelor de bază din care derivă. Pentru a înţelege mai bine diferenţa, explicaţia se poate rezuma

astfel: tabela virtuală este cea pentru care pe disc se memorează numai schema, nu şi conţinutul.

5 [Hainaut94], pp. 24-25

Page 22: BD1 2010 FotacheM Integral

22 Baze de date I

Figura 2.5. Schema simplificată a bazei de date VÎNZĂRI

Tabelele virtuale oferă oricărui utilizator al unei baze de date posibilitatea prezentării datelor

în funcţie de nevoile sale specifice. De asemenea, raţiuni de securitate şi confidenţialitate a anumitor

informaţii pot conduce la izolarea unor date faţă de utilizatorii neautorizaţi, lucru deplin posibil prin

intermediul imaginilor. Pornind de la aceleaşi tabele de bază, se pot crea un mare număr de tabele

virtuale, în funcţie de situaţie. Tabelele virtuale constituie suportul creării schemelor externe. Odată

definită, o tabelă virtuală poate fi referită ca o tabelă de bază oarecare. De asemenea, ca şi relaţiile de

bază, şi "imaginile" pot fi actualizate, ceea ce atrage modificarea tabelelor statice din care derivă. În

aceste situaţii apar o serie de probleme. Este clar că modificarea conţinutului tabelelor de bază

presupune modificarea conţinutului tabelei (sau tabelelor) derivate. Însă ce informaţii pot fi modificate

în tabelele de bază, pornind de la modificările tabelei virtuale ? Soluţiile implementate în SGBDR-

urile actuale diferă de la caz la caz.

O procedură stocată este o secvenţă de program (cod) care face parte integrantă din baza de

date. Avantajele utilizării procedurilor stocate decurg din faptul că acestea sunt parte din structura

(schema) bazei, fiind păstrare în dicţionarul de date (catalogul sistem). Există mai multe tipuri de

proceduri stocate: funcţii-utilizator pentru validarea tabelelor, funcţii pentru calculul unor valori

implicite, proceduri/funcţii de validare la nivel de linie sau tabelă, funcţii/proceduri de calcul a unor

expresii complexe etc.

Declanşatorul (trigger) este un tip special de procedură stocată care este executată automat

când un eveniment predefinit (inserare, actualizare sau ştergere) modifică o tabelă. Utilitatea

declanşatoarelor este evidentă la formularea unor restricţii mai complexe decât “suportă” comenzile

Page 23: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 23

CREATE/ALTER TABLE, actualizarea automată a unor atribute calculate, jurnalizarea modificărilor

suferite de baza de date, păstrarea integrităţii referenţiale etc.

Există diferenţe sensibile între SGBD-uri şi în ceea ce priveşte sintaxa declanşatoarelor,

deoarece acestea sunt redactate în extensiile procedurale ale SQL care prezintă diferenţieri majore de

la un producător la altul. Poate fi evocată, spre exemplu, diversitatea declanşatoarelor în Oracle, DB2

etc. faţă de Visual FoxPro.

Astfel, dacă în VFP există numai trei tipuri de declanşatoare, pentru adăugare, pentru

modificare şi pentru ştergere, în standardul SQL şi SGBD-urile puternice (ex. Oracle), pentru fiecare

din cele trei operaţiuni există o primă separare între declanşatoare lansate înaintea operaţiei

(actualizării) şi cele după operaţiune. La această delimitare se mai adaugă şi cea dintre triggere la nivel

de linie, ce intră în acţiune la fiecare inserare/modificare/ştergere a unei linii şi cele la nivel de

comandă care se execută o singură dată, indiferent câte linii afectează comanda de actualizare

respectivă (row versus statement).

Page 24: BD1 2010 FotacheM Integral

24 Baze de date I

CAPITOLUL 3

PROIECTAREA SCHEMEI BAZELOR DE DATE FOLOSIND NORMALIZAREA

Obiective:

I. Conştientizarea nevoii de normalizare

II. Expunerea cadrului general al normalizării prin descompunere şi prin sinteză

III. Definirea dependenţelor funcţionale, inclusiv a celor parţiale şi tranzitive

IV. Discutarea noţiunilor de relaţie universală şi a condiţiilor necesare pentru prima

formă normalizată

V. Prezentarea detaliată a metodologiei de aducere a unei baze de date în formele

normale 2, 3 şi Boyce-Codd

VI. Definirea dependenţelor multi-valoare şi celei de-a patra forme normale

V. Înţelegerea folosirii cheilor surogat şi dependenţelor de incluziune

VI. Ilustrarea întregului demers al normalizării pentru mai multe cazuri practice

VII. Conştientizarea câtorva dificultăţi şi capcane ale normalizării

Rezultate aşteptate: A. Înţelegerea modului în care poate fi adaptată normalizarea la proiectarea

schemei bazelor de date

B. Formarea capacităţii de a proiecta schema oricărei baze de date, pornind de la

cerinţele unei aplicaţii reale

Page 25: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 25

3.1 CE ESTE NORMALIZAREA ?

Dintre multiplele metodologii şi instrumente de proiectare a structurii unei baze de date, în

acest capitol (şi curs) vom trata doar normalizarea. Motivul principal este acela că normalizarea

lucrează strict cu terminologia relaţională, fără a mai defini noţiuni specifice metodologiilor de tip

Entităţii-Relaţii (E/R), Object-Role Modeling etc: clasă de entităţi/relaţii, cardinalităţi, specializare,

generalizare etc.

Totuşi, normalizarea este un demers complex. Chiar dacă pe parcursul acestui capitol voi

încerca să fiu cât mai limpede cu putinţă, cred că este necesară recurgerea la materialul adiţional din

care este extras prezentul material (pentru cei neatenţi, tocmai sunteţi în mijlocul unui moment

publicitar)6.

3.1.1 Nevoia de normalizare

Unul dintre cele mai bune argumente în favoarea normalizării ţine de punerea în evidenţă a

ceea ce s-ar întâmpla în absenţa sa. Să luăm un prim exemplu, cel din figura 3.1 care este dedicat

stocării datelor privitoare la studenţii Facultăţii de Economie şi Administrarea Afacerilor (sau oricare

alta), mai ales în ceea ce priveşte situaţia şcolară a acestora. Fiecare student are un identificator unic -

Matricol, este înscris la o specializare într-un an de studii şi susţine examenele la disciplinele din

planul de învăţământ. Orice disciplină are alocat un număr de credite prin planul de învăţământ al

specializării. Fiecare student poate susţine un examen de mai multe ori până îl promovează (sau este

exmatriculat). Astfel, Zăineanu Ion a picat examenul la Baze de date I în prima sesiune (pe 29 ianuarie

2004), dar l-a luat în restanţe (pe 12 februarie).

STUDENŢI_EXAMENE Matricol NumePrenume An Specializare CodDisc

EL13455 Popovici I Vasile 3 Informatică economică AI3501

EL13456 Zăineanu W Ion 3 Informatică economică AI3501

EL13457 Abălaşei R Zicu 3 Informatică economică AI3501

EL13455 Popovici I Vasile 3 Informatică economică AI3502

EL13456 Zăineanu W Ion 3 Informatică economică AI3502

EL13457 Abălaşei R Zicu 3 Informatică economică AI3502

EL13456 Zăineanu W Ion 3 Informatică economică AI3501

EL13457 Abălaşei R Zicu 3 Informatică economică AI3502

EL13458 Şpagă M Michael 3 Informatică economică AI3503

DenumireDisc NrCredite DataExamen Nota

Baze de date I 6 29/01/2004 8

Baze de date I 6 29/01/2004 4

Baze de date I 6 29/01/2004 9

Programare vizuală şi RAD 7 01/02/2004 10

Programare vizuală şi RAD 7 01/02/2004 8

Programare vizuală şi RAD 7 01/02/2004 4

Baze de date I 6 12/02/2004 8

Programare vizuală şi RAD 7 15/02/2004 9

Analiza sistemelor informaţionale 6 04/02/2004 7

Figura 3.1 Relaţie supusă normalizării

6 Fotache, M. – Proiectarea bazelor de date. Normalizare şi postnormalizare. Implementări SQL şi Oracle, Editura

Polirom, Iaşi, 2005

Page 26: BD1 2010 FotacheM Integral

26 Baze de date I

Deoarece fiecare linie se referă la un examen susţinut la o anumită dată de un student, cheia

primară a relaţiei este combinaţia (Matricol, CodDisc, DataExamen).

Redundanţe

Este lesne de observat că relaţia de mai sus conţine redundanţe. Astfel, dacă un student

susţine, pe parcursul primelor n semestre de studii, 25 de examene, atunci matricolul, numele, anul şi

specializarea sunt prezente în toate cele 25 de linii. Dacă pentru o disciplină s-au consemnat în baza de

date 1500 de examinări, atunci nu numai codul, ci şi denumirea şi numărul de credite alocat disciplinei

apar de acelaşi număr de ori.

Cu cât baza de date este mai mare, cu atât risipita de spaţiu este mai importantă. Chiar dacă

spaţiul de stocare nu mai este o problemă din punctul de vedere al costului, obezitatea unei tabele

precum cea de mai sus poate atrage serioase probleme de viteză în exploatare (timpi de aşteptare la

preluarea noilor note etc.).

Anomalii la inserare

Să luăm în discuţie studenţii din anul I. După examenul de admitere, ce are loc în lunile iulie

şi septembrie aceştia sunt înmatriculaţi. Dacă, însă, baza de date are schema relaţiei de mai sus,

preluarea este imposibilă până în momentul primului examen al studentului respectiv. Aceasta

deoarece în cheia primară sunt incluse şi atributele CodDisc şi DataEx, iar modelul relaţional

interzice valori nule pentru atributele-cheie (restricţia de entitate). Ori, la data înmatriculării, studenţii

din anul I nu au nici un examen susţinut (asta ar mai trebui !), iar prima sesiune e abia în ianuarie

viitor, aşa că şi CodDisc şi DataEx sunt în acel moment NULL.

Anomalii la modificare

Presupunem că într-o furtunoasă şedinţă de catedră se decide ca disciplina Programare

vizuală şi RAD să fie redenumită Programare I, titulatură sub care vă apărea şi în foile matricole ale

studenţilor din actualul an III care vor absolvi, mai devreme sau mai târziu, specializarea Informatică

economică. În baza de date există câteva sute de înregistrări referitoare la studenţi examinaţi la această

disciplină, şi toate trebuie modificate. Dacă, dintr-un motiv sau altul, modificarea se face pe numai o

parte dintre liniile cu pricina, putem afirma că datele îşi pierd consistenţa. După O'Neil & O'Neil,

avem de-a face cu o anomalie de modificare într-o relaţie atunci când modificarea valorii unui atribut

atrage obligativitatea actualizării aceleaşi valori pe mai multe linii7.

Anomalii la ştergere

Anomaliile la ştergere se manifestă atunci când prin eliminarea unei linii dintr-o tabelă se

pierd involuntar nu numai informaţiile despre entitatea reflectată pe linia respectivă, ci şi alte

informaţii. Astfel, dacă ştergem ultima linie din relaţia de mai sus, cea care conţine nota examenului la

Analiza sistemelor informaţionale susţinut de studentul Şpagă Michael pe 4 februarie 2004, se pierd

nu numai datele despre examenul respectiv, ci şi toate informaţiile despre studentul Şpagă M. Aceasta

deoarece aceasta era singura linie în care apărea studentul cu pricina.

7 [O'Neil & O'Neil 01 ] p.357

Page 27: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 27

3.1.2 Definirea normalizării

Proiectarea bazelor de date relaţionale (BDR) presupune definirea atributelor, gruparea lor în

tabele, stabilirea legăturilor dintre ele, a mecanismelor de integritate şi securitate, fiind un proces

dificil în care cunoştinţele teoretice, experienţa, inteligenţa, intuiţia şi creativitatea proiectanţilor se

îmbină într-un mod mai mult sau mai puţin armonios. Structura finală a BD trebuie să asigure un

echilibru între, pe de o parte, obţinerea unui volum maxim de informaţii într-un interval de timp cât

mai scurt şi, pe de altă parte, eliminarea anomaliilor de stocare şi actualizare, toate în condiţiile unui

grad apreciabil de securitate şi integritate.

Văzută ca rigoare (matematică sau nu), artă (în sensul de curat… interpretabilă, dar şi de

bazată pe intuiţie, creatoare), “meserie” (deprinsă prin imitaţie şi desăvârşită prin experienţă), ceea ce

se poate spune cu siguranţă despre normalizare este, vorba lui C. J. Date, că nu reprezintă un panaceu.

Există două mari abordări ale normalizării, descompunerea şi sinteza. Ambele discută despre

relaţia iniţială (universală) şi o serie de forme normalizare ale bazei de date. Însă, în timp ce prin

decompunere relaţia iniţială este „spartă” (descompusă) în relaţii tot mai mici, prin sinteză se porneşte

de la dependenţele dintre atributele din bază şi se construieşte schema bazei direct într-o formă

normală acceptabilă.

Teoretic, schema unei BD poate conţine o singură relaţie (tabelă) ce grupează toate atributele

bazei. Aceasta este relaţia universală. Dincolo de avantajul compactării, relaţia universală ridică

serioase probleme privind redundanţa datelor şi generează anomalii importante la adăugarea,

modificarea sau ştergerea unor linii (tupluri), după cum văzut în frugalul exemplu din paragraful 3.1.1.

Ca şi consecinţă directă, este necesară spargerea bazei în mai multe tabele care sunt legate prin

restricţii de integritate referenţială. Este tocmai obiectivul central al normalizării. Spargerea relaţiei nu

trebuie făcută oricum, deoarece apare riscul pierderilor de informaţii. De asemenea, un număr prea

mare de tabele necesită un efort sporit pentru controlul bazei şi respectarea restricţiilor. În plus, trebuie

să ţinem seama de faptul că obţinerea multor informaţii dintr-o bază de date fragmentată este posibilă

prin operaţiunea de joncţiune, mare consumatoare de resurse. Normalizarea îşi propune ca principale

obiective:

minimizarea spaţiului necesar stocării datelor;

minimizarea riscului apariţiei de date inconsistente în cadrul BD;

minimizarea anomaliilor ce pot apărea la actualizare (inserarea datelor, dar mai ales

modificare şi ştergere).;

ameliorarea structurii bazei, reprezentarea diverselor conexiuni dintre atributele bazei.

diminuarea nevoii de reorganizare periodică a modelului.

Obiect al numeroase studii, nu se poate afirma că există o unanimitate de idei şi instrumente

privind normalizarea. Importanţa normalizării nu trebuie absolutizată, deoarece adesea aceasta vine

uneori în contradicţie cu parametri extrem de importanţi ai aplicaţiilor de lucru cu bazele de date:

viteză de acces, securitate mărită, resurse hard/soft disponibile, reducerea traficului pe reţea etc. Se

poate spune că o BD riguros normalizată nu este neapărat una optimă.

Teoria clasică a normalizării este construită în jurul a cinci forme care în literatura noastră sunt

referite ca normale sau normalizate. Codd, părintele modelului relaţional, a definit iniţial trei forme

normale, notate prin 1NF, 2NF şi 3NF. Întrucât, într-o primă formulare, definiţia 3FN ridica ceva

probleme, Codd şi Boyce au elaborat o nouă variantă, cunoscută sub numele Boyce-Codd Normal

Page 28: BD1 2010 FotacheM Integral

28 Baze de date I

Form (BCNF). Deşi este vorba, în principiu, de o formulare mai riguroasă a aceleaşi 3FN, BCNF este

prezentată separat în majoritatea lucrărilor.

Relaţie universală - R

Există grupuri repetitive ?

Atomizarea atributelor

Relaţia R are cheie primară ?

Descompunere în relaţii ce au cheie primară

1 FN

Există DF parţiale ?

Aducerea relaţiilor în 2FN

Inceput

Există DF tranzitive ?

Aducerea relaţiilor în 3FN

3 FN

da

da

da

da

nu

nu

nu

nu

2FN

Există DF multivaloare ?

Aducerea relaţiilor în 4FN

4 FN

Există dependenţe de joncţiune ?

Aducerea relaţiilor în 5FN

5 FN

da

da

nu

nu

Toţi determinanţii sunt chei candidate ?

Aducerea relaţiilor în BCFN

BCFN

da nu

Sfârşit

Figura 3.2. Etapele clasice ale normalizării bazelor de date relaţionale

Formele 4 şi 5, legate de numele lui Ronald Fagin, sunt tratate mai cu reţinere, ca să nu spun

pudoare, în literatura consacrată analizei şi proiectării bazelor de date. Ba chiar unele lucrări cu tentă

mai pragmatică se opresc declarat la 3FN, pe care o consideră suficientă în elaborarea BDR.

Fundamentul normalizării BDR îl constituie dependenţele dintre atribute. Primele trei forme

normale pot fi determinate pe baza dependenţelor funcţionale elementare (totale) şi tranzitive. Forma a

patra (4FN) se bazează pe dependenţele multivaloare, în timp ce a cincea formă normală (5FN) pe

Page 29: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 29

dependenţele de joncţiune. Problema este că dependenţele multivaloare, şi (mai ales) cele de joncţiune

sunt dificil de identificat şi rar întâlnite în practică.

Normalizarea BDR poate fi imaginat ca un proces prin care, pornindu-se de la relaţia iniţială R

(universală), se realizează descompunerea succesivă a acesteia în sub-relaţii după succesiunea din

figura 3.28. Relaţia R poate fi ulterior re-construită din cele n relaţii obţinute în urma normalizării, prin

operaţii de joncţiune aplicate asupra acestora.

Nu putem încheia acest crochiu al normalizării fără a insista asupra faptului că, esenţialmente,

ingredientul acesteia este de natură semantică. Normalizarea reflectă relaţii care se manifestă între

atribute ale bazei. Este imposibil de normalizat o relaţie fără a cunoaşte fenomenul, procesul pe care îl

reflectă.

3.1.3 Descompunere fără pierdere de informaţii

Descompunerea succesivă a relaţiei universale trebuie să se bazeze exclusiv pe dependenţele

între atribute, altminteri este posibilă pierderea de informaţii. Pentru detalii, vezi [Fotache 2005],

paragraful 2.5 (pp.44-46)

3.1.4 Punctul de pornire - relaţia universală

Prima etapă a normalizării constă în identificarea informaţiilor ce trebuie stocate şi gestionate

cu ajutorul bazei de date. Este momentul în care la aplicaţiile complexe intră în scenă analiştii de

sistem, cei care observă cum se derulează procesele, operaţiunile ce constituie obiectul aplicaţiei/bazei

de date, discută cu toate categoriile de utilizatori, încearcă să identifice soluţii anterioare de la aceeaşi

organizaţie sau de la organizaţii similare pentru a putea anticipa problemele cele mai dificile etc.

Pentru aplicaţiile mari munca analiştilor şi proiectanţilor este extrem de laborioasă şi consumatoare de

timp şi nervi. Este unul dintre motivele pentru care această categorie profesională are salarii mari şi

este destul de "curtată". Noi vom încerca să ne plasăm într-o zonă mai "aerisită", fără a cădea in

derizoriu.

Noţiunea de relaţie universală este unul dintre destulele ingrediente ale modelului relaţiona

care încep prin a părea jenant de banal, şi din care, în final, fiecare înţelege ce vrea, fără a ajunge la un

punct comun, ci doar la un „nor de puncte” de vedere comun. Noi o să considerăm relaţia universală

drept relaţia alcătuită din toate atributele identificate ca fiind relevante pentru aplicaţie şi toate

tuplurile posibile cu valorile acestor atribute9. Prin urmare, schema relaţiei universale conţine toate

atributele bazei de date.

Baza de date VÎNZĂRI

Pe parcusul acestui capitol şi următoarelor ne propunem să elaborăm (şi) schema unei prime

baze de date prin care să poată fi gestionate informaţiile relevante despre vânzările unei firme

comerciale, de producţie etc. Pentru o mai bună vizualizare, se poate recurge la o formă tabelară de

prezentare a datelor, în care coloanele ar fi: numele atributului, aşa cum este folosit în baza de date

(ceva mai scurt), numele întreg al atributului, tipul atributului, lungimea, explicaţii suplimentare,

restricţii etc.

8 Vezi şi [Oprea99], p.343

9 Din acest punct de vedere, de plasăm mai aproape de accepţiunea lui Chris Date ([Date04], p.195)

Page 30: BD1 2010 FotacheM Integral

30 Baze de date I

Tabelul 3.1 este ceva mai modest, având doar două coloane, dedicate numelui atributului şi

unor explicaţii sumare. Atributele care interesează în mod deosebit sunt cele legate de:

clienţi: codul, denumirea, adresa, codul poştal, localitateă, judeţul (inclusiv regiunea), codul

fiscal, numărul de telefon;

produse: codul, denumirea, unitatea de măsură, grupa (textile, încălţăminte, băuturi

răcoritoare, băuturi spirtoase etc.), procentul de TVA aplicat;

facturi: numărul, data întocmirii, clientul, observaţii (note despre clauze din factură), apoi

produsele vândute în factura respectivă: linia pe care apare fiecare produs, cantitatea şi preţul

facturat.

încasări: codul unic asociat fiecărei încasări, date despre documentul pe baza căruia se face

încasarea (ordin de plată, cec etc.), numărul facturii încasate, tranşa încasată din factura

respectivă.

Tabel 3.1. VÎNZĂRI - dicţionarul datelor (simplificat)

Atribut Descriere

Jud Indicativul auto al judeţului (2 caractere)

Judeţ Denumirea judeţului

Regiune Regiunea ţării din care face parte judeţul

CodPost Codul poştal al unei adrese

Localitate Denumirea oraşului sau satului

Comuna Denumirea comunei din care face parte satul curent

CodCl Codul firmei client

DenCl Denumirea firmei-client

CodFiscal Codul fiscal al firmei client

StradaCl Strada pe care se află sediul clientul

NrStradaCl Numărul la care se afla adresa clientului

BlocScApCl Eventualele informaţii suplimentare despre adresa clientului: bloc, scară, etaj, apartament

TelefonCl Telefonul firmei client

CodPr Codul produsului / sortimentului comercializat

DenPr Denumirea produsului

UM Unitatea de măsură a produsului/sortimentului

Grupa Grupa sortimentală (Bere, Ciocolată, Cafea etc.) în care se încadrează produsul curent

ProcTVA Procentul TVA aplicat în mod obişnuit produsului

NrFact Numărul facturii emise

DataFact Data întocmirii facturii

Obs Observaţii privitoare la factură

Linie Linia curentă (corespunzătoare unui produs) dintr-o factură

Cantitate Cantitatea vândută din produs în factură (pe linia curentă)

PreţUnit Preţul unitar de vânzare (fără TVA) al produsului în factură (pe linia curentă)

CodÎnc Codul încasării

DataÎnc Data încasării (în care banii intră în casierie sau în contul bancar)

CodDoc Codul documentului de încasare

NrDoc Numărul documentului de încasare

DataDoc Data la care a fost întocmit documentul de încasare

Tranşă Tranşa primită din factură prin încasarea (documentul) curentă

Una dintre cele mai importante observaţii ce se cuvine a fi făcute în acest moment ţine de

faptul că o serie de informaţii foarte importante, precum:

valoarea fără TVA a unui produs facturat (de pe o linie a fiecărei facturi);

TVA calculată pe linia unei facturi (pentru un produs vândut);

valoarea cu TVA a unei linii (produs) dintr-o factură;

valoarea fără TVA a unei facturi;

TVA colectată aferentă unei facturi;

valoarea totală (cu TVA) a unei facturi;

valoarea totală a încasărilor pentru o factură;

valoarea totală a vânzărilor către fiecare client;

valoarea totală a încasărilor de la un client;

Page 31: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 31

restul de plată pentru fiecare client (diferenţa vânzări - încasări);

valoarea vânzărilor pentru fiecare localitate;

valoarea vânzărilor pentru fiecare judeţ;

valoarea vânzărilor pentru fiecare regiune (Moldova, Banat etc.);

şi multe altele au fost eliminate din dicţionarul de date, deoarece pot fi calculate pornind de la cele

prezente în dicţionar. Vom vedea ceva mai târziu că atunci când este vorba de date accesate în mod

frecvent, pentru calcularea cărora este necesar un consum de resurse important, se poate recurge la

introducerea controlată a unor atribute redundante.

O factură conţine mai multe linii şi poate fi încasată în una, două sau mai multe tranşe.

Deoarece există un interval între momentul întocmirii de către client a documentului de plată

(DataDoc) şi momentul intrării efective a banilor în contul (sau casieria) firmei (DataÎnc), se

folosesc ambele atribute.

Baza de date FILMOGRAFIE

Pentru un centru de închirieri video se pune problema constituirii unei baze de date cu

informaţii despre filmele aflate pe casetele sau DVD-urile oferite (legal) spre închieriere. Fiecare film

are un cod unic (identificator), iar atributele principale sunt cele din tabelul 3.2.

Tabel 3.2. FILMOGRAFIE - dicţionarul datelor (simplificat)

Atribut Descriere

IdFilm Codul unic al filmului

TitluOriginal Titlul în engleză, franceză etc., aşa cum apare la lansarea filmului

TitluRO Traducerea românească a titlului original

AnLans Anul lansării

Producători Producătorul sau producătorii filmului

Regizori Regizorul sau regizorii filmului

Distribuţie Actorii şi rolurile interpretate în film

Genuri Genul/genurile la care se încadrează filmul (horror, comedie etc.)

Premii Numele premiului - tipul (Oscar, Leul de argint, Ursul de aur etc.), categoria (cel mai bun film, cel mai bun actor în rol principal) anul premiului şi, eventual, numele actorului.

Un film poate fi produs şi regizat de una, două sau mai multe persoane. Pentru fiecare actor

distribuit în film interesează şi personajul sau personajele (dacă actorul e în dublu rol) interpretate. O

peliculă se poate încadra la mai multe categorii (dramă, comedie) simultan. Dacă un premiu se referă

la regie, scenariu etc., interesează numele, tipul şi anul decernării, iar dacă se referă la o performanţă

actoricească, la informaţiile anterioare se adaugă şi numele actorului laureat. Practic, două tupluri din

această relaţie s-ar prezenta că în figura 3.3.

IdFilm TitluOriginal TitluRO AnLans Producători Regizori

11899 As Good As It Gets Mai bine nu se poate 1997 James Brooks, Bridget Johnson, Kristi Zea

James Brooks

12345 Bicentennial Man Omul bicentenar 1999 Michael Barnathan, Chris Columbus, Gail Katz

Chris Columbus

Distribuţie Genuri Premii

Jack Nicholson (Levin Udall) Helen Hunt (Carol Connelly) Greg Kinnear (Simon Bishop) Cuba Gooding Jr. (Frank Sachs)

comedie dramă romantic

Oscar - cel mai bun actor în rol principal (Jack Nicholson) - 1998 Oscar - cea mai bună actriţă în rol principal (Helen Hunt) - 1998 Globul de aur - cea mai bună imagine - 1998 Globul de aur - cel mai bun actor într-o comedie/musical (Jack Nicholson) - 1998 Globul de aur - cea mai bună actriţă într-o comedie/musical (Helen Hunt) - 1998

Robin Williams (Andrew Martin) Embeth Davidtz (Little Miss

SF dramă

Page 32: BD1 2010 FotacheM Integral

32 Baze de date I

Amanda Martia / Portia Charney) Sam Neil (Richard Martin) Oliver Platt (Rupert Burns) Kiersten Warren (Galatea)

romantic

Figura 3.3. O descompunere a relaţiei universale FILMOGRAFIE

Aceste două relaţii universale, ca şi altele, vor fi supuse, în capitolele următoare, normalizării

şi tuturor discuţiilor şi problemelor legate de acest demers.

3.2 PRIMA FORMĂ NORMALIZATĂ

Prima formă normalizată (1NF) este, în general, tratată cu superficialitate de majoritatea

practicienilor în materie de proiectare a bazelor de date. Aceasta deoarece principala cerinţă –

atomicitatea valorilor – este un deziderat uşor de îndeplinit, cel puţin la prima vedere. Cele mai

sensibile chestiuni în aducerea relaţiilor în 1NF sunt legate de atomicitate, eliminarea grupurilor

repetitive, precum şi de stabilirea cheii primare, cheie primară care devine o problemă delicată atunci

când se porneşte de la o relaţie cu zeci sau sute de atribute.

Pe de altă parte, noţiunea de atomicitate este una destul de alunecoasă. Deşi în primele lucrări

Codd nu formulează o definiţie riguroasă a atomicităţii, timp de aproape trei decenii majoritatea

autorilor din "zona" relaţionalului au considerat ca pe o primă "poruncă" a unei relaţii (tabele)

necesitatea ca valoarea oricărui atribut pe orice linie (tuplu) să fie una scalară (atomică,

nedecompozabilă). De câtva timp, însă, Chris Date şi alţi relaţionişti încearcă că depăşească limitele

atomicităţii, înlocuind domeniile cu tipuri care pot fi atât scalare, cât şi compozite (definite de

utilizator).

Teoria normalizării nu face parte, propriu-zis, din modelul relaţional. Dintre toate formele

normalizate, doar prima are caracter de obligativitate. O bază de date este normalizată dacă toate

relaţiile (tabelele) care o alcătuiesc se află măcar în 1NF. Celelalte forme normale sunt dezirabile

pentru diminuarea redundanţelor, spaţiului ocupat pe disc sau anomaliilor manifestate la actualizare,

însă nu sunt obligatorii.

După Codd, o relaţie este în prima formă normală dacă… nici unul dintre domeniile sale nu

conţine elemente care sunt, la rândul lor, seturi (ansambluri)10

. Se cuvine de adăugat remarca lui Date

care crede ca ar fi fost mai nimerit ca, în locul termenului domenii, Codd să-l fi folosit pe cel de

atribute. Actualmente, mulţi "relaţionişti" preferă termenul tip celui de domeniu11

.

Unele lucrări12

definesc o relaţie aflată în 1NF ca acea relaţie în care fiecare atribut prezintă

numai valori atomice, adică toate atributele sunt ne-decompozabile; după R.Riordan, o relaţie este în

1NF dacă domeniile pe care sunt definite atributele relaţiei sunt scalare13

. În ediţia a treia a cărţii

dedicate sistemelor de baze de date, Connoly şi Begg sunt mai tranşanţi: o relaţie în 1NF este o relaţie

în care intersecţia oricărei linii cu oricare coloană conţine o valoare şi numai una14

. Astfel, 1NF

respinge ideea de set de valori, tuplu de valori sau combinaţia acestora ca valoare a unui singur atribut

pe un oricare tuplu (linie) a relaţiei.

10

[Codd72] 11

[Date04] 12

[Date86], [Miranda&Busta90], [Connoly s.a.96], [Dollinger98] 13

[Riordan99]

Page 33: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 33

Alţi autori formează explicit pentru 1NF şi restricţia: toate atributele ce compun relaţia să fie

în dependenţă funcţională faţă de cheia relaţiei, ceea ce este, oarecum, o tautologie, întrucât una

dintre "poruncile" valabile pentru orice tabelă stipulează negru pe alb că într-o relaţie nu pot exista

două tupluri identice sau, altfel spus, într-o relaţie trebuie să existe măcar un atribut sau combinaţie de

atribute ale căror valori să deosebească orice tuplu de toate celelalte sau orice relaţie posedă o cheie

primară.

Formularea care a generat cele mai multe confuzii este legată de noţiunea de grupuri

repetitive15

: O relaţie în 1NF nu trebuie să conţină grupuri repetitive.

De obicei, noţiunea de grup repetitiv este raportată direct la cea de atomicitate. Diferenţierea

atomicitate – grup repetitiv apare în [Lungu s.a.95], pentru care un atribut atomic este unul simplu (ne-

compus): „O relaţie este în 1NF dacă domeniile pe care sunt definite atributele relaţiei sunt constituite

numai din valori atomice (elementare). În plus, un tuplu nu trebuie să conţină atribute sau grupuri de

atribute repetitive”16

. Din păcate, autorii se opresc aici cu definiţia, păstrând o remarcabilă discreţie

vis-a-vis de ceea ce consideră grup repetitiv. La fel stau lucrurile şi la dna. Ileana Popescu17

.

Un plus de claritate aduce definiţia lui Toby Teorey, pentru care o relaţie este în 1NF dacă şi

numai dacă toate coloanele conţin numai valori atomice; aceasta înseamnă că nu există grupuri

(coloane) repetitive în cadrul vreunei linii a tabelei. Imediat autoarea precizează că un grup repetitiv

apare într-o tabelă atunci când unui atribut multi-valoare îi sunt permise două sau mai multe valori

reprezentate în cadrul unei aceleaşi linii18

. Sau, după cum spunea Chris Date până în ediţia a şasea a

celei mai cunoscute cărţi a sa, un grup repetitiv reprezintă o coloană care conţine câteva valori în

fiecare linie, sau, altfel spus, un număr diferit de valori pe linii diferite19

.

În concluzie, aducerea unei relaţii în 1NF presupune discutarea următoarelor elemente:

raportul atribut simplu/atribut compus;

grupuri repetitive de atribute (pe orizontală);

grupuri repetitive de valori în cadrul fiecărei celule a tabelei.

Atribute simple şi atribute compuse

Primele vizate de “stigmatul” neatomicităţii sunt atributele compuse. Cu ani buni în urmă, în

cursurile de profil, exemplul clasic al unui atribut compus era DataCalendaristică, alcătuit, cum

altfel, din câmpurile: Ziuă, Lună, An. Era vremea înfloritoare a COBOLului şi FORTRANului, când

nu existau mecanisme de declarare şi gestionare a variabilelor şi atributelor de tip DATE; verificarea

corectitudinii datei calendaristice cădea în sarcina programatorului ce trebuia să scrie o rutină specială.

Astăzi, nu există SGBD sau mediu de programare care să nu prezinte tipul de dată DATE prin

care se gestionează simultan toate cele trei elemente; prin funcţii de conversie se pot obţine: ziua

(DAY), luna (MONTH), anul (YEAR), ba chiar calcula numărul de zile, luni, ani dintre două date

14

[Connoly & Begg 02], p.388 15

[Date86], [Pratt&Adamski91], [Oprea99]), [Lungu s.a.95] 16

[Lungu s.a. 95], p.169 17

[Popescu 01], p.78 18 [Teorey99], p.99 19

Preluare din ediţia a opta a cărţii ([Date 04], p.153), unde Date se autocitează pentru a demonstra că înţelesese greşit noţiunea de domeniu (tip) şi, în consecinţă greşise atunci când a formulat definiţia 1NF.

Page 34: BD1 2010 FotacheM Integral

34 Baze de date I

calendaristice şi alte minunăţii. Ca să nu mai vorbim de tipul DATETIME şi acesta omniprezent în

produsele software ale zilelor noastre.

Un alt exemplu celebru privind atribute compuse (non-atomice) este Adresa, discutat în

capitolul 2. Fiind alcătuită din Stradă, Număr, Bloc, Scară, Etaj, Apartament, plus CodPoştal

(după noul sistem de codificare) discuţia despre atomicitatea adresei pare de prisos, iar descompunerea

sa imperativă. Din nou însă trebuie să ne raportăm la obiectivele bazei. Fără îndoială că, pentru BD a

unei filiale CONEL sau ROMTELECOM, sau pentru poliţie, preluarea separată a fiecărui element

constituent al adresei este vitală. Interesează, spre exemplu, ce abonaţi sunt pe strada Tranziţiei sau

câte cereri sunt depuse de cetăţeni de pe străzile Lascăr Catargi, Titu Maiorescu şi Marina Scupra.

Sau, un cioranian ce lucrează în cadrul Poliţiei, ar putea fi interesat să afle dacă gradul de sinucidere al

persoanelor ce locuiesc la etajul trei este mai mare decât cel al persoanelor de la parter.

Cu totul altfel stau lucrurile pentru un importator direct, pentru un mare en-grossist sau pentru

o firmă de producţie. Partenerii de afaceri ai acestora sunt persoane juridice, iar adresa interesează

numai la nivel general. Este drept că imediat după Revoluţie deveniseră mari furnizori naţionali de

cupru şi aluminiu şi persoane fizice (vezi cazul bulibaşei din Ciurea, dar asta-i altă poveste…).

În relaţia universală FILMOGRAFIE, al cărei dicţionar de date simplificat este cel din tabelul

3.2, cel puţin două atribute sunt compuse şi trebuie "sparte", Distribuţie şi Premii. Astfel, în

Distribuţie pot fi delimitate măcar două informaţii, Actor şi Rol. În cazul premiilor, ar trebui

detaliată discuţia prin introducerea atributelor:

denumire premiu (Oscar, Globul de aur etc.);

locul atribuirii (Hollywod, Cannes, Berlin, Veneţia, Bucureşti etc.);

categoria (regie, scenariu, actor în rol principal etc.)

anul decernării;

numele actorului, dacă categoria este pentru interpretare.

Astfel, noua formă a dicţionarului de date este cea din tabelul 3.3.

Tabel 3.3. FILMOGRAFIE - dicţionarul datelor (simplificat) - versiunea 2

Atribut Descriere

IdFilm Codul unic al filmului

TitluOriginal Titlul în engleză, franceză etc, aşa cum apare la lansare filmului

TitluRO Traducerea românească a titlului original

AnLans Anul lansării

Producători Producătorul sau producătorii filmului

Regizori Regizorul sau regizorii filmului

Rol Rolul din film

Actor Actorul care interpretează rolul/filmul curente

Genuri Genul/genurile la care se încadrează filmul (horror, comedie etc.)

DenPremiu Numele premiului - tipul (Oscar, Leul de argint, Ursul de aur etc.)

LocDecernare Locul în care se organizează festivitatea sau festivalul

Categorie Categoria premiului (pentru ce anume se acordă premiul)

AnPremiu Anul decernării

Concluzionând în termeni rabinici, ambele variante, descompunerea sau nedescompunerea

atributelor compuse, sunt valabile de la caz la caz, alegerea soluţiei fiind în concordanţă cu specificul

bazei de date (şi inspiraţia proiectantului).

Page 35: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 35

Despre grupuri repetitive şi urmările lor

O relaţie (tabelă) în 1NF nu trebuie să conţină atribute care se repetă ca grupuri. Într-o altă

formulare, toate liniile unei tabele trebuie să conţină acelaşi număr de atribute. Fiecare celulă a tabelei

(intersecţia unei coloane cu o linie), altfel spus, valoarea unui atribut pe o linie (înregistrare), trebuie

să fie atomică. După [Connoly s.a.96], un grup repetitiv este un atribut sau grup de atribute dintr-o

tabelă care apare cu valori multiple pentru o singură apariţie a cheii primare a tabelei ne-normalizate.

Să luăm exemplul tabelei BIBLIOTECĂ din figura 3.4. Tabela gestionează informaţii despre

cărţile existente în depozitul bibliotecii Facultăţii de Economie şi Administrarea Afacerilor (FEAA).

De remarcat că în blibliotecă există două exemplare ale cărţii cu ISBN-ul 973-683-709-2 şi trei

exemplare din cea dedicată Visual FoxPro. Prima carte a fost scrisă de patru autori şi îi sunt asociate

opt cuvinte-cheie, iar a doua are un singur autor. Relaţia nenormalizată conţine trei grupuri repetitive:

Cote, Autori şi CuvinteCheie.

BIBLIOTECĂ ISBN Titlu Cote Autori

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale

III-13421, III-13422, III-13423

Marin Fotache, Ioan Brava, Cătălin Strâmbei, Liviu Creţu

973-683-709-2 SQL. Dialecte DB2, Oracle şi Visual FoxPro

III-10678, III-10679 Marin Fotache

Editura LocSediuEd AnApariţie CuvinteCheie

Polirom Iaşi 2002 baze de date, SQL, proceduri stocate, FoxPro, formulare, orientare pe obiecte, client-server, web

Polirom Iaşi 2001 baze de date, algebră relaţională, SQL

Figura 3.4. Relaţia universală nenormalizată BIBLIOTECĂ

În paragrafele 3.3.1 (pp.55-56), 3.3.2 (pp.57-61) şi 3.3.3 (pp.61-67) din [Fotache 2005] sunt

prezentate câteva soluţii de eliminare a grupurilor repetitive:

adăugarea de atribute suplimentare (grupuri repetitive pe orizontală);

adăugarea unui număr minim de linii – o valoare atomică pentru fiecare atribut compozit;

adăugarea unui nr. de linii egal cu suma valorilor elementare ale atributelor de tip grup repetitiv

adăugarea unui număr maxim de tupluri egal cu produsul valorilor elementare ale atributelor de tip

grup repetitiv (vezi figura 3.5).

BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 (fragment) ISBN Titlu Cotă Autor

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Marin Fotache

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Ioan Brava

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Cătălin Stâmbei

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Liviu Creţu

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Marin Fotache

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Ioan Brava

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Cătălin Stâmbei

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale III-13421 Liviu Creţu

... (încă 88 de linii pentru cartea de Visual FoxPro, plus 6 pentru cea de SQL )

Editura LocSediuEd AnApariţie CuvântCheie

Polirom Iaşi 2002 baze de date

Polirom Iaşi 2002 baze de date

Polirom Iaşi 2002 baze de date

Polirom Iaşi 2002 baze de date

Polirom Iaşi 2002 SQL

Polirom Iaşi 2002 SQL

Polirom Iaşi 2002 SQL

Polirom Iaşi 2002 SQL ...

Figura 3.5. Completarea cu tupluri - soluţia 3

Page 36: BD1 2010 FotacheM Integral

36 Baze de date I

eliminarea grupurilor de repetitive prin contruirea de noi relaţii.

Trebuie precizat, însă, că nu întotdeauna grupurile repetitive reprezintă o problemă majoră.

Spre exemplu, într-o relaţie gen PERSOANE_CONTACT atributul Telefoane poate avea valori ne-

atomice, deoarece pot exista mai multe numere atât la birou, cât şi mobile (mulţi manageri au câte un

aparat pe fiecare reţea - Orange, Vodafone, Zapp)...

3.3 DEPENDENŢE FUNCŢIONALE

Problema stabilirii dependenţelor dintre atribute este fundamentală în procesul elaborării

bazelor de date relaţionale. Aducerea structurii bazei într-o variantă care minimizează redundanţele şi

anomaliile manifestate la adăugarea/modificarea/ştergerea unor linii din tabele presupune parcurgerea

unui proces de normalizare a relaţiilor, normalizare care este centrată tocmai pe conceptul de

dependenţă dintre atributele bazei.

3.3.1 Definţii

Dependenţa funcţională reprezintă o generalizare a conceptului de cheie primară20

. Desemnăm

prin Data2 un atribut sau combinaţie de atribute dintr-o relaţie oarecare şi prin Data1 un alt atribut

sau grup de atribute din aceeaşi relaţie. Se spune că Data2 este în dependenţă funcţională (sau

depinde funcţional) de Data1 atunci când cunoaşterea unei valori pentru Data1 permite

determinarea (cunoaşterea) a maximum o valoare pentru Data2. Data1 se numeşte sursa sau

determinantul dependenţei funcţionale, iar Data2 reprezintă destinaţia (scopul sau determinatul)

dependenţei funcţionale.

Formal, dacă notăm relaţia R {A1, A2,...., An} - unde R este numele relaţiei, Ai sunt atributele

relaţiei R - şi prin X şi Y două subansambluri din {A1, A2,...., An}, se spune că există o dependenţă

funcţională între X şi Y (simbolizată X Y) dacă şi numai dacă :

fiecare apariţie (valoare) a lui X poate fi asociată unei singure apariţii a lui Y,

două apariţii identice ale lui X nu pot fi asociate decât aceleiaşi apariţii a lui Y.

Într-o prezentare şi mai explicită, Date21

defineşte dependenţa funcţională după cum urmează: dată

fiind o relaţie R, subansamblul de atribute Y din R depinde funcţional de subansamblul X (tot din R),

dacă şi numai dacă, ori de câte ori două tupluri din R prezintă aceeaşi valoare pentru X, obligatoriu

valoarea celor două tupluri este identică şi pentru Y.

Se spune despre o dependenţă că este trivială dacă şi numai dacă destinaţia este un subset al

sursei: dacă Y X, atunci X Y.

Revenim asupra relaţiei universale VÎNZĂRI prezentată în tabelul 3.1. Cunoaşterea numărului

facturii permite determinarea cu exactitate a codului clientului, deoarece o factură este (conform legii

şi metodologiei în vigoare) întocmită pentru un singur client. Se poate scrie:

NrFact DataFact; NrFact CodCl; NrFact Obs

20

Vezi şi [Garcia-Molina s.a.02], [Date86] 21

[Date86], p.365

Page 37: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 37

dar şi

NrFact (DataFact, CodCl); NrFact (DataFact, CodCl, Obs)

Situaţia de mai sus se încadrează în cazurile în care sursa (determinantul) este cheia primară a

relaţiei. Implicit, în orice relaţie R există dependenţele funcţionale: cheia lui R celelalte atribute

ale lui R.

Luăm un alt exemplu: un cod poştal nu poate desemna adrese din oraşe şi comune diferite. Se

poate scrie, deci: CodPoştal Localitate şi CodPoştal Judeţ.

Observaţii

1. Uneori, începătorii au probleme în ceea ce priveşte "sensul" dependenţelor funcţionale. Dependenţa

funcţională CodPoştal Localitate nu înseamnă că o localitate are un singur cod poştal, ci,

invers, că unui cod poştal îi corespunde o singură localitate.

2. dependenţă funcţională în care scopul (destinaţia) este alcătuit dintr-un singur atribut se

numeşte dependenţă funcţională canonică sau dependenţă funcţională în formă canonică22

.

Două atribute nu sunt în dependenţă funcţională atunci când cunoaşterea unei valori oarecare

a primului atribut:

fie nu permite cunoaşterea nici uneia dintre valorile celui de-al doilea (nu există nici un

"raport" între cele două atribute); în acest caz dependenţa ne-funcţională echivalează cu

inexistenţa vreunei dependenţe între cele două atribute;

fie permite cunoaşterea mai multor valori ale celui de-al doilea atribut.

În relaţia VÎNZĂRI între atributele DataFact şi CodCl nu există dependenţă funcţională. O

valoare a atributului DataFact nu furnizează nici o informaţie cu privire la valoarea de pe aceeaşi

linie a atributului CodCl. Se scrie: DataFact / CodCl. De asemenea, deoarece pentru un client

se pot întocmi mai multe facturi (aferente fiecărei vânzări): CodCl / NrFact. Într-o factură se

poate consemna vânzarea mai multor produse/servicii; prin urmare, unei valori a NrFact, îi corespund

mai multe valori ale atributului CodPr. Se notează: NrFact / CodPr.

Să examinăm în continuare tabela de mai jos.

NotaContabilă Data Operaţiune ContDebitor ContCreditor Suma

150 30.11.2006 1 300 401 10000000

150 30.11.2006 1 4426 401 1800000

150 30.11.2006 2 401 5311 7000000

150 30.11.2006 2 401 5121 4800000

151 01.12.2006 1 5121 700 5250000

Figura 3.6. Tabela LINII_ARTICOLE_CONTABILE

- o notă contabilă este alcătuită din mai multe articole contabile:

(NotaContabilă, Data) / Operaţiune

- un articol contabil (înregistrare) poate fi compus, adică alcătuit din mai multe corespondenţe

ContDebitor = ContCreditor:

(NotaContabilă, Data, Operaţiune) / ContDebitor

(NotaContabilă, Data, Operaţiune) / ContCreditor

(NotaContabilă, Data, Operaţiune, ContDebitor) / ContCreditor

22

[Saleh94], p.76. Vezi şi [Garcia-Molina s.a 01], p.89

Page 38: BD1 2010 FotacheM Integral

38 Baze de date I

(NotaContabilă, Data, Operaţiune, ContCreditor) / ContDebitor

Atunci când firmele îşi centralizează operaţiunile, utilizând de la o lună la alta aceleaşi numere pentru

notele contabile: NotaContabilă / Data.

Pe lângă aceste două exemple, am mai putea aminti faptul că localităţi cu aceeaşi denumire

există în mai multe judeţe. Spre exemplu, localitatea Vînători există în judeţele Vrancea, Galaţi, Iaşi,

Neamţ etc. Aceasta înseamnă că: Localitate / Judeţ

Dependenţele funcţionale reprezintă legături semantice între atribute ale bazei de date. Au fost

propuşi algoritmi de identificare şi gestionare a dependenţelor funcţionale într-o relaţie. Cea mai mare

parte a acestora sunt total inutili, pentru că, efectiv, nimeni nu poate stabili o dependenţă fără a

cunoaşte regula sau restricţia care se ascunde în spatele acesteia, altfel spus, fără a cunoaşte temeinic

realitatea modelată. Cu atât mai mult calculatorul care are mari probleme în a lucra la nivel semantic,

ca să nu mai vorbim de cel pragmatic.

3.3.2 Dependenţe funcţionale cu sursa compusă

Există dependenţe funcţionale care prezintă în partea stângă (sursa dependenţei funcţionale

sau determinantul) două sau mai multe atribute. Acestea sunt dependenţele funcţionale cu sursa

compusă. Astfel, în relaţia VÎNZĂRI:

- o factură conţine mai multe linii: NrFact / Linie

- pe o factură pot apărea mai multe produse: NrFact / CodPr

- un produs apare, de obicei, pe mai multe facturi: CodPr / NrFact

- pentru fiecare factură, o linie este unică: (NrFact, Linie) CodPr; (NrFact, Linie)

Cantitate; (NrFact, Linie) PreţUnit

- dacă stabilim că un produs nu poate apărea pe o factură decât o singură dată, atunci sunt valabile DF:

(NrFact, CodPr) Linie; (NrFact, CodPr) Cantitate; (NrFact, CodPr)

PreţUnit

În tabela LINII_ARTICOLE_CONTABILE din figura 3.6 stabilirea dependenţelor funcţionale

depinde de modul în care firma îşi organizează evidenţa contabilă. Astfel, marea majoritate a firmelor

îşi centralizează operaţiunile, pe tipuri (intrări de materiale, consumuri de materiale, operaţiuni legate

de casierie, conturile din bancă etc.), utilizând, de la o lună la altă, aceleaşi numere pentru notele

contabile (ex: nota 1 - încasări prin casierie, nota 10 - pentru înregistrarea amortizării etc.). În acest

caz, există dependenţa funcţională: (1) (NotaContabilă, Data, Operaţiune, ContDebitor,

ContCreditor) Suma. Dacă unele firme, cu un volum de activitate mai redus, ar conta fiecare

document primit/întocmit, generând astfel o nouă notă contabilă, dependenţele funcţionale s-ar putea

scrie sub forma: (2) NotaContabilă Data; (3) (NotaContabilă,Operaţiune, ContDe-

bitor, ContCreditor) Suma.

Fireşte, este valabilă şi dependenţa (4) (NotaContabilă, Operaţiune, ContDebitor,

ContCreditor) Data

Tabela BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 din figura 3.5, aflată în prima formă

normală, prezintă ca şi cheie primară combinaţia (Cota, Autor, CuvântCheie). De aceea, se pot

inventaria următoarele cinci dependenţe funcţionale, în care destinaţii sunt atributele necheie.

Page 39: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 39

(1) (Cota, Autor, CuvântCheie) ISBN

(2) (Cota, Autor, CuvântCheie) Titlu

(3) (Cota, Autor, CuvântCheie) Editura

(4) (Cota, Autor, CuvântCheie) LocSediuEd

(5) (Cota, Autor, CuvântCheie) AnApariţie

Alte elemente luate în discuţie pentru stabilirea DF:

- o carte poate fi scrisă de mai mulţi autori: ISBN / Autor

- un autor scrie mai multe cărţi: Autor / ISBN şi despre mai multe subiecte: Autor /

CuvântCheie

- într-o carte sunt tratate mai multe subiecte: ISBN / CuvântCheie

- pentru o carte sunt în depozit mai multe exemplare: ISBN / Cota

Cota este codul care identifică în mod unic o carte aflată în depozit, aşadar:

(6) Cota ISBN (7) Cota Titlu (8) Cota Editura

(9) Cota LocSediuEd (10) Cota AnApariţie

Fiecare titlu de carte este identificat fără ambiguitate de ISBN:

(11) ISBN Titlu (12) ISBN Editura (13) ISBN LocSediuEd

(14) ISBN AnApariţie

O editură are un singur sediu central: (15) Editura LocSediuEd.

Prin urmare, dintre cele 15 DF inventariate, primele cinci au sursa compusă, celelalte sursa

simplă.

În baza de date VÎNZĂRI, dat fiind numărul de atribute şi specificul aplicaţiei, în acest caz

discuţiile sunt ceva mai laborioase:

- fiecare judeţ are un nume şi un indicativ unice:

(1) Jud Regiune (2) Judeţ Regiune (3) Jud Judeţ

(4) Judeţ Jud

- pe baza discuţiei de la exemplul anterior (CODURI_NOI_V2):

(5) CodPost Localitate (6) CodPost Comuna (7) CodPost Jud

(8) CodPost Judeţ

- fiecare firmă-client este identificată în mod unic atât prin cod (CodCl):

(9) CodCl DenCl (10) CodCl CodFiscal (11)CodCl StradaCl

(12) CodCl NrStradaCl (13) CodCl BlocScApCl

(14) CodCl TelefonCl (15) CodCl CodPost (16) CodCl Localitate

(17) CodCl Comuna (18) CodCl Jud (19) CodCl Judet

(20) CodCl Regiune

- cât şi prin cod fiscal (CodFiscal):

(21) CodFiscal DenCl (22) CodFiscal CodCl

(23) CodFiscal StradaCl (24) CodFiscal NrStradaCl

(25) CodFiscal BlocScApCl (26) CodFiscal TelefonCl

(27) CodFiscal CodPost (28) CodFiscal Localitate

Page 40: BD1 2010 FotacheM Integral

40 Baze de date I

(29) CodFiscal Comuna (30) CodFiscal Jud

(31) CodFiscal Judet (32) CodFiscal Regiune

- elementul care identifică un produs este codul acestuia:

(33) CodPr DenPr (34) CodPr UM (35) CodPr Grupa

(36) CodPr ProcTVA

- o factură se întocmeşte pentru un singur client:

(37) NrFact CodCl (38) NrFact CodFiscal

- o factură se întocmeşte o singură dată: (39) NrFact DataFact (40)NrFact Obs

- o factură conţine mai multe linii: NrFact / Linie

- fiecare linie dintr-o factură se referă la vânzarea unui produs (CodPr), indicând cantitatea şi preţul

unitar la care s-a făcut vânzarea:

(41) (NrFact, Linie) Cantitate (42) (NrFact, Linie) PreţUnit

(43) (NrFact, Linie) CodPr (44) (NrFact, Linie) DenPr

(45)(NrFact, Linie) UM (46) (NrFact, Linie) ProcTVA

- într-o factură, un produs apare pe o singură linie:

(47) (NrFact, CodPr) Cantitate (48) (NrFact, CodPr) PreţUnit

(49) (NrFact, CodPr) Linie

- o încasare are la bază un document primar (Ordin de plată, Chitanţă etc.):

(50) CodÎnc DataÎnc (51) CodÎnc CodDoc

(52) CodÎnc NrDoc (53) CodÎnc DataDoc

- documentele de încasare pot prezenta numere similare de la un client la altul; spre exemplu, Ordinul

de plată cu nr. 101 poate fi emis de mai mulţi clienţi (bineînţeles, este vorba de trei documente diferite

care au însă acelaşi număr, poate chiar şi dată): (CodDoc, NrDoc) / DataDoc; (CodDoc,

NrDoc) / NrFact; (CodDoc, NrDoc) / CodCl

- o factură este încasată în mai multe tranşe: NrFact / CodÎnc

- o încasare poate achita mai multe facturi: CodÎnc / NrFact

- într-o încasare, o tranşă se referă la o singură factură: (54) (CodÎnc, NrFact) Tranşă,

nu însă şi (CodÎnc, Tranşă) NrFact, deoarece, într-o aceeaşi încasare pot exista două tranşe

egale (ex. 5 000 000 lei) referitoare la două facturi diferite.

3.3.3 Dependenţe funcţionale simetrice şi redundante

Deseori, o bază de date sau relaţie prezintă mai multe chei candidat. Alegerea dintre acestea a

cheii primare are în vedere, pe lângă unicitate, compoziţie minimală şi valori nenule ale oricărui

atribut component, şi elemente ce ţin de facilitatea utilizării: uşurinţa reţinerii (de către utilizator),

lungime cât mai mică, constanţă în timp etc.

În procesul normalizării, problemele ridicate de aceste dependenţe reciproce dintre cheile

candidat trebuie tratate cu prudenţă. De cele mai multe ori, este recomandat ca dintre dependenţele ale

unei relaţii să fie eliminate toate cele în care sursa este o cheie candidat şi păstrarea numai celor în care

determinantul este atributul cheie-primară.

Page 41: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 41

Revenim la cele 54 de dependenţe din relaţia universală VÎNZĂRI tratate în urmă cu câteva

pagini. Este evident că dependenţele (3), Jud Judeţ, şi (4), Judeţ Jud, sunt simetrice.

Deşi corecte, şi dependenţele (1) - Jud Regiune - şi (2) - Judeţ Regiune - "se calcă

pe picior”. Pentru comoditate, putem alege atributul Jud ca identificator "de bază" al unui judeţ,

eliminând (pe nedrept), pe lângă (4), toate DF în care sursa simplă este atributul Judeţ iar destinaţiile

identice cu cele ale dependenţelor în care sursă este Jud. Ce-i drept, efortul nu este chiar

supraomenesc, deoarece este o singură dependenţă de acest tip, (2).

La fel se pune problema şi pentru perechile de atribute CodCl şi DenCl şi CodCl şi

CodFiscal. Toate identifică un acelaşi tip de entitate - o firmă client. Investim CodCl drept

"reprezentant unic" al unui client. DenCl nefiind luat prea mult în seamă în setul de DF, avem de

eliminat doar DF în care CodFiscal este sursă simplă: (21) - (32). În privinţa perechii CodPr şi

DenPr nu avem a ne epuiza, deoarece, aproape intenţionat, au fost omise din setul DF cele în care

DenPr apare ca sursă.

Atenţie ! Nu facem acelaşi lucru în relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 cu atributele Cota şi

ISBN, deoarece primul se referă la un exemplar "fizic" al unei cărţi din bibliotecă, în timp ce al doilea se referă

la un titlu (carte publicată la o editură).

Tipul acesta de redundanţă e uşor de identificat şi eliminat, chiar dacă nu se leagă vizibil de

nici o teoremă sau lemă ce constituie fundamentul normalizării. Setul DF conţine însă şi un alt tip de

redundanţă/circularitate. Astfel, deoarece fiecare linie dintr-o factură se referă la un produs vândut la

un preţ unitar şi într-o anumită cantitate, sursele DF în care destinaţiile sunt Cantitate, PreţUnit

(plus DenPr, UM şi ProcTVA) pot fi atât (NrFact, Linie), cât şi (NrFact, CodPr). Procedând

analog situaţiei de mai sus (dar cu mai multă strângere de inimă), decidem ca fiecare linie/produs

dintr-o factură să fie identificat de sursa (NrFact, Linie). Aşa că eliminăm dependenţele în care

sursă este "concurentul", adică (NrFact, CodPr) şi destinaţiile sunt identice: (47), (48) şi (49).

3.3.4 Graful dependenţelor funcţionale

Într-un graf al dependenţelor funcţionale, acestea sunt reprezentate prin săgeţi ce leagă sursa şi

destinaţia. La dependenţele funcţionale simple săgeata uneşte cele două atribute. Când sursa este

compusă, se foloseşte un conector care leagă, în prima instanţă, atributele-determinant, iar săgeata

uneşte acest conector cu atributul destinaţie.

Dependenţele discutate în paragraful anterior pentru relaţia BIBLIOTECA_TU-

PLURI_NOI_SOLUŢIA_3 (figura 3.5) pot fi reprezentate sub formă de graf ca în figura 3.7.

Figura 3.7. Reprezentarea sub formă de graf a DF din relaţia

BIBLIOTECA_TUPLURI_NOI_SOLUŢIA_3

Page 42: BD1 2010 FotacheM Integral

42 Baze de date I

Din baza de date VÎNZĂRI, pentru simplificare, în figura 3.8 sunt reprezentate numai DF (1),

(3), (5)-(7), (9)-(15), (33)-(46), (50)-(54).

Figura 3.8. Graful simplificat al DF din BD VÎNZĂRI

3.4 A DOUA FORMĂ NORMALIZATĂ

Cea de-a doua formă normală (2NF) se bazează pe dependenţele funcţionale totale sau

elementare. Drept care acest paragraf începe cu prezentarea acestui tip de dependenţe funcţionale eşi

se continuă cu discutarea modalităţile de “rupere” a bazei de date în mai multe relaţii aflate în 2NF.

3.4.1 Dependenţe funcţionale totale

Noţiunea de dependenţă funcţională totală a fost introdusă de Melkanoff. O dependenţă

funcţională Data1 Data2 este totală dacă nu există nici un atribut/combinaţie Dată3 ca

subansamblu al Data1, care să verifice dependenţa funcţională Dată3 Dată2. Formal, dacă X

= { Ai, Aj,...., Ak } reprezintă un grup de atribute ale relaţiei R, iar Am un atribut al aceleiaşi relaţii,

unde Am X. Se spune că există o dependenţă funcţională totală între X şi Am dacă:

1. X Am şi 2. nu există nici un subansamblu S de atribute, S X, pentru care S Am .

total

Se notează: X Am

Dependenţa funcţională totală mai este denumită şi elementară, plină, deplină sau completă.

Implicit, toate dependenţele în care sursa este simplă (alcătuită dintr-un singur atribut) sunt dependenţe

funcţionale complete. Problema prezintă importanţă în cazul dependenţei funcţionale cu sursa

compusă. Astfel, în BD VÎNZĂRI, dependenţele : (NrFact, Linie) CodPr; (NrFact, Linie)

Cantitate şi (NrFact, Linie) PretUnit sunt totale, deoarece: NrFact / CodPr;

NrFact / Cantitate; NrFact / PretUnit; Linie / CodPr; Linie / Cantitate;

Linie / PretUnit.

Dacă se utilizează cel de-al doilea sistem de contare, caz în care sunt valabile DF (2)-(4),

atunci pentru LINII_ARTICOLE_CONTABILE din figura 3.6 dependenţa (4) este una parţială

datorită (2).

Page 43: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 43

În relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 din figura 3.5 primele cinci

dependenţe funcţionale: (1) (Cota, Autor, CuvântCheie) ISBN; (2) (Cota, Autor,

CuvântCheie) Titlu; (3) (Cota, Autor, CuvântCheie) Editura; (4) (Cota,

Autor, CuvântCheie) LocSediuEd; (5) (Cota, Autor, CuvântCheie) AnApariţie

sunt parţiale, deoarece: (6) Cota ISBN; (7) Cota Titlu; (8) Cota

Editura; (9) Cota LocSediuEd; (10) Cota AnApariţie

Cu alte cuvinte, un atribut component al determinatului dependenţelor (1)-(5) este, el singur,

sursă într- serie de DF în care destinaţiile sunt identice.

3.4.2 Trecerea relaţiilor în a doua formă normală (2NF)

Începând cu a doua formă normală, relaţiile pot fi decupate (decompuse, sparte) în sub-relaţii,

în scopul diminuării problemelor legate de stocare şi actualizare. În general, atunci când într-o relaţie

R există o DF de tip X Y şi X nu este cheie candidată, atunci R conţine o anumită doză de

redundanţă23

.

Jan Heath a demonstrat24

că orice relaţie alcătuită din trei atribute, notată R(X, Y, Z), în care

există dependenţa funcţională X Y poate fi descompusă în două relaţii R1(X, Y) şi R2(X, Z);

R1 şi R2 reprezentă proiecţiile relaţiile R pe atributele X şi Y, respectiv X şi Z. Esenţial este faptul că

descompunerea să se facă fără pierdere de informaţii, adică R să poată fi "recompusă" prin joncţiunea

tabelelor R1 şi R2. În alţi termeni, la spargerea oricărei relaţii trebuie ca toate dependenţele din

acoperirea minimală să se regăsească în structura noilor relaţii25

. Ca o consecinţă indirectă, se poate

astfel rezolva şi problema cheii primare a relaţiei universale, în cazul încălcării restricţiei de entitate

(un atribut din cheia primară prezintă valori nule).

O relaţie se află în 2NF dacă: 1. Se află în 1NF; 2. Toate DF ce leagă cheia primară la celelalte

atribute sunt DF elementare (totale).

O bază de date este în 2NF când toate relaţiile care o alcătuiesc sunt în 2NF. Problema trecerii

unei relaţii din prima în a doua formă normală se pune numai când cheia primară a relaţiei este

compusă din mai multe atribute. Într-o formulare mai lejeră, se poate spune că o relaţie în 2NF nu

conţine DF parţiale între atributele-cheie şi celelalte atribute.

În general, trecerea de la 1NF la 2NF se poate realiza după următoarea succesiune de paşi:

a) Se identifică posibila cheie primară a relaţiei universale, care conferă unicitate oricărui tuplu din

relaţie, chiar în condiţiile violării restricţiei de entitate;

b) Se inventariază toate dependenţele dintre atributele relaţiei (închiderea DF), cu excepţia celor în

care destinaţia este un atribut component al cheii primare.

c) Se trec în revistă toate dependenţele care au ca sursă (determinant) un atribut sau sub-ansamblu de

atribute din cheia primară.

d) Pentru fiecare atribut (sau sub-ansamblu) al cheii de la pasul c), se creează o relaţie care va avea

drept identificator primar atributul (subansamblul) respectiv, iar celelalte atribute vor fi cele care apar

ca destinaţii în dependenţele de la pasul c).

23

[Date00], p.333 24

Heath, I.J. - Unacceptable File Operations in a Relational Database, Proc.1971 ACM SIGFIDET Workshop on Data Description, Access and Control, nov. 1971

25 Vezi şi [O'Neil & O'Neil 01], p.387

Page 44: BD1 2010 FotacheM Integral

44 Baze de date I

e) Din relaţia iniţială sunt eliminate toate atributele destinaţie (noncheie) din relaţiile "proaspăt"

obţinute, păstrându-se numai atributele cheie ale noilor relaţii (se asigură, astfel, cheile străine care vor

face legăturile către noile relaţii).

f) În „rămăşiţele” relaţiei universale (iniţiale) se verifică dacă cheia primară iniţială respectă restricţia

de entitate, iar dacă nu, se mai elimină dintre atribute, iar, la limită, chiar toată relaţia-„rămăşiţe”.

Important !

Etapele sugerate sunt aplicabile mai ales în situaţiile în care cheia primară a relaţiei universale

încalcă restricţia de entitate, adică atunci când combinaţia ar conferi unicitate, însă unul sau mai multe

dintre atributele cheie ar risca valori nule (vezi discuţia din paragraful 3.4). Este vorba de violare

ruşinoasă a "poruncilor" relaţionale, deoarece, după cum am promis, începând cu 2NF orice relaţie

obţinută va respecta scupulos (şi) restricţia de entitate.

La limită, prin descompuneri, din relaţia universală (iniţială) este posibil să nu rămână decât

atributele cheie. Acum, însă, orice pericol privind nulitatea vreunui atribut cheie va atrage după sine

eliminarea atributului respectiv, sau, la limită a relaţiei respective, după cum vom vedea chiar în acest

paragraf, pentru relaţia VÎNZĂRI.

Relaţia LINII_ARTICOLE_CONTABILE

Pentru relaţia din figura 3.6, dacă se utilizează primul sistem de contare, în care singura

dependenţă dintre cele discutate în paragraful 3.3 este (1), atunci nu se mai pune problema trecerii în

2NF. În al doilea caz, în care DF sunt valabile (2), (3) şi (4), ultima este parţială. Urmăm cei şase paşi:

a) Cheia primară este combinaţia: (NotaContabilă, Operaţiune, ContDebitor, ContCreditor)

b) Inventarierea tuturor DF: (2), (3), (4)

c) Singura DF în care determinantul e parte din cheia primară este (2): NotaContabilă Data

d) Pentru atributul NotaContabilă se constituie relaţie care îl va avea identificator primar, iar cel

de-al doilea atribut Data. Din tabela iniţială se elimină atributul necheie preluat în noua relaţie.

e) Din relaţia iniţială rămâne: R‟ {NotaContabilă, Operaţiune, ContDebitor, ContCreditor,

Suma}

f) În R‟ cheia primară nu periclitează restricţia entităţii, deci R‟ îşi poate păstra structura.

În consecinţă, în 2NF relaţia LINII_ARTICOLE_CONTABILE se descompune în două relaţii,

NOTE_CTB {NotaContabilă, Data} şi ARTICOLE_CTB {NotaContabilă, Operaţiune,

ContDebitor, ContCreditor, Suma} prezentate în figura 3.9.

NOTE_CTB ARTICOLE_CTB

NotaContabilă Data NotaContabilă Operaţiune ContDebitor ContCreditor Suma

150 30.11.2006 150 1 300 401 10000000

151 01.12.2006 150 1 4426 401 1800000

150 2 401 5311 7000000

150 2 401 5121 4800000

151 1 5121 700 5250000

Figura 3.9. Tabela LINII_ARTICOLE_CONTABILE adusă în 2NF

La drept vorbind, nu putem spune că economia de spaţiu şi diminuarea anomaliilor la actualizări sunt

spectaculoase din cale-afară prin aducerea relaţiei în 2NF. Datorită simplităţii, exemplul este însă util.

Relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3

Relaţia ilustrată în figura 3.5 prezintă, dintre cele discutate în paragraful 3.3, dependenţe

parţiale şi, astfel, constituie un bun "obiect" de normalizare în 2NF. Dealtminteri, relaţia abundă în

redundanţe şi anomalii de inserare şi modificare:

Page 45: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 45

înregistrarea recepţiei unui titlu, introducerea datelor despre o carte (titlu) sunt imposibile până

în momentul în care măcar o carte primeşte cotă;

dacă un titlu a fost introdus iniţial eronat, corecţia trebuie operată pe destule linii ale tabelei;

la fiecare achiziţie ulterioară a unei aceleaşi cărţi (ISBN) trebuie introduse din nou ISBN-ul,

titlul, editura etc.

Parcurgem cei şase paşi, după cum urmează:

a) Cheia primară a relaţie iniţiale este: (Cota, Autor, CuvântCheie)

b) Ansamblul DF: (1)-(15)

c) În dependenţele (6)-(10), atributul Cota, component al cheii relaţiei, este sursă simplă

d) Se constituie o relaţie în care Cota este cheie primară, iar celelalte atribute sunt destinaţiile DF (6),

(7), (8), (9) şi (10).

e) Din relaţia iniţială se elimină toate atributele-destinaţie preluate în proaspătă tabelă, rămânând doar

atributele cheie: {Cota, Autor, CuvântCheie}.

f) Nici unul dintre cele trei atribute de la punctul e) nu periclitează restricţia de entitate, aşa încât

relaţia-„rămăşiţă” se păstrează în această formă.

Prin urmare, în a doua formă normalizată, relaţia iniţială (universala) s-ar putea descompune

în: CĂRŢI {Cota, ISBN, Titlu, Editura, LocSediuEd, AnApariţie} şi TITLURI_AU-

TORI_CUVINTECHEIE {Cota, Autor, CuvântCheie} - vezi figura 3.10.

CĂRŢI

Cotă ISBN Titlu Editura LocSediuEd AnApariţie

III-13421 973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale Polirom Iaşi 2002

III-13422 973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale Polirom Iaşi 2002

III-13423 973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale Polirom Iaşi 2002

III-10678 973-683-709-2 SQL. Dialecte DB2, Oracle şi Visual FoxPro Polirom Iaşi 2001

III-10679 973-683-709-2 SQL. Dialecte DB2, Oracle şi Visual FoxPro Polirom Iaşi 2001

TITLURI_AUTORI_CUVINTECHEIE (fragment)

Cota Autor CuvântCheie

III-13421 Marin Fotache baze de date

III-13421 Marin Fotache SQL

III-13421 Marin Fotache proceduri stocate

III-13421 Marin Fotache FoxPro

III-13421 Marin Fotache formulare

III-13421 Marin Fotache orientare pe obiecte

III-13421 Marin Fotache client-server

III-13421 Marin Fotache web

...

Figura 3.10. Relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 în 2NF

Chiar dacă o parte din redundanţe au fost eliminate, mai rămân destule, astfel încât structura de mai

sus va constitui subiect atât pentru a trei formă normalizată (3NF), cât şi pentru cea de a patra (4NF).

Relaţia STUDENŢI_EXAMENE

Senzaţia de deja-vu care vă încearcă este îndreptăţită. Acesta este relaţia din paragraful 3.1.1

(figura 3.1) cu care am început să închinăm osanale normalizării. Este o relaţie simplă care se pretează

la normalizare, manifestând redundanţe şi anomalii la actualizare. Cheia primară fiind combinaţia

(Matricol, CodDisc, DataExamen), automat sunt valabile următoarele DF:

(1) (Matricol, CodDisc, DataExamen) NumePrenume

(2) (Matricol, CodDisc, DataExamen) An

(3) (Matricol, CodDisc, DataExamen) Specializare

(4) (Matricol, CodDisc, DataExamen) DenumireDisc

(5) (Matricol, CodDisc, DataExamen) NrCredite

Page 46: BD1 2010 FotacheM Integral

46 Baze de date I

(6) (Matricol, CodDisc, DataExamen) Nota

STUDENŢI_EXAMENE ar fi în 2NF dacă nici una din aceste şase dependenţe nu ar fi parţială. Fără a

ne întrebuinţa prea serios, e suficient să ne reamintim că matricolul este desemnat să identifice în mod

unic fiecare student. Dintr-un foc avem trei dependenţe: (7) Matricol NumePrenume;

(8)Matricol An; (9)Matricol Specializare.

Şi codul disciplinei are pretenţii de identificare fără ambiguitare a fiecărei "materii" parcurse

de studenţi: (10) CodDisc DenumireDisc; (11) CodDisc NrCredite.

Din dependenţele (7)-(9) construim relaţia STUDENŢI { Matricol, NumePrenume, An,

Specializare}, din (10) şi (11) DISCIPLINE { CodDisc, DenumireDisc, NrCredite} iar din

relaţia iniţială rămâne: EXAMENE { Matricol, CodDisc, DataExamen, Nota} care nu are

probleme cu valorile nule ale vreunui atribut-cheie.

Avem toate motivele să fim mulţumiţi. Redundanţele au fost eliminate. Numele, specializarea

şi anul de studiu al fiecărui student apar acum o singură dată (în tuplul corespunzător studentului din

relaţia STUDENŢI). Tot o singură dată apar şi denumirea unei discipline şi numărul de credite alocat

acesteia (relaţia DISCIPLINE). Lucrurile stau la fel de bine şi în ceea ce priveşte diminuarea

anomaliilor manifestate la editarea datelor. Astfel, privitor la anomaliile de inserare, din momentul

înscrierii în anul I, până la cel al primului examen, studentul poate fi inserat în tabela STUDENŢI,

urmând ca, odată cu prima sa sesiune de examene, să-i fie introduse înregistrări în EXAMENE.

Modificarea denumirii unei discipline sau numărul de credite, sau corectarea unei eventuale erori

strecurate în numele studentului sau al specializării/anului, toate se fac într-un singur loc, pe tuplul

corespunzător din STUDENŢI sau DISCIPLINE, ceea ce înseamnă că am scăpat şi de anomaliile de

modificare. În fine, tot în paragraful 3.1.1 se atrăgea atenţia asupra riscului pierderii unor informaţii

valoroase la ştergerea unei linii din STUDENŢI_EXAMENE (un soi de aruncatul copilului odată cu

albia). Nici acest pericol nu mai este de actualitate în noua structură.

Relaţia VÎNZĂRI

Şi relaţia universală VÎNZĂRI încalcă preceptele relaţionalului (în fapt, am putea să-i spunem

tabelă, pentru a respecta adevărul, tabela fiind asociată cu SQL-ul care este mult prea tolerant cu

elemente ce fac dintr-o tabelă să nu fie o relaţie - unicitate, valori nule, lipsa cheii primare etc.),

întrucât unul din atributele care asigură unicitate fiecărui tuplu din relaţia iniţială, CodÎnc (codul

încasării) are valori nule pentru facturile “proaspăt” emise, de fapt, pentru toate facturile din care nu s-

a încasat nici o leţcaie. Dacă nu s-ar fi pus problema restricţiei de entitate, combinaţia de atribute care

diferenţiază cu siguranţă orice tuplu de celelalte este (NrFact, Linie, CodÎnc). Lucrurile nu sunt

atât de grave, deoarece în setul de DF apar destule surse care sunt subansambluri ale pseudo-cheii

primare. Aşadar, să parcurgem cele şase etape ale deja celebrului „îndrumar” de aducere a unei baze

de date în 2FN.

a) Cheia primară a relaţiei este: (NrFact, Linie, CodÎnc).

b) Dependenţele manifestate în relaţie sunt cele 54 din paragraful 3.3.

c) Dependenţele (50), (51), (52) şi (53) au drept sursă CodÎnc. Cu totul altfel stau lucrurile pentru

NrFact. Dacă iniţial identificasem dependenţele (37), (38), (39) şi (40), datorită tranzitivităţii acestora

li se mai pot adăuga încă 12, adică: (55) NrFact DenCl; (56) NrFact CodFiscal; (57)

NrFact StradaCl; (58) NrFact NrStradaCl; (59) NrFact BlocScApCl; (60)

Page 47: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 47

NrFact TelefonCl; (61)NrFact CodPost; (62)NrFact Localitate; (63) Nr-

Fact Comuna; (64) NrFact Jud; (65) NrFact Judet; (66)NrFact Regiune.

Am încheiat inventarierea DF în care sursele sunt simple şi atribute-cheie şi continuăm cu surse

compuse, alcătuite din două dintre cele trei atribute, la acest „capitol” având dependenţele (54) şi cele

de la (41) la (46).

d) Creăm noile relaţii. Din (50), (51), (52) şi (53) rezultă: ÎNCASĂRI {CodÎnc, DataÎnc, CodDoc,

NrDoc, DataDoc}. Din (37), (38), (39) şi (40), plus (55)-(66) se obţine: FACTURI {NrFact, CodCl,

DataFact, Obs, DenCl, CodFiscal, StradaCl, NrStradaCl, BlocScApCl, TelefonCl,

CodPost, Localitate, Comuna, Jud, Judeţ, Regiune}. Din dependenţa (54) se poate “izola”

relaţia: ÎNCAS_FACT {CodÎnc, NrFact, Tranşa} Pe baza DF (41)-(46) se construieşte: LI-

NII_FACTURI {NrFact, Linie, Cantitate, PreţUnit, CodPr, DenPr, UM, ProcTVA, Grupa}.

e) Din relaţia iniţială rămâne: FACTURI_INCASĂRI {NrFact, Linie, CodÎnc}.

f) În afara celor trei atribute cheie din relaţia iniţială - nimic. Dacă în 1NF eludam problema restricţiei

de entitate, pe motiv că în 2NF lucrurile se rezolvă, acum ne propunem să tranşăm discuţia. Pentru ca

să respecte restricţia de entitate, relaţia FACTURI_ÎNCASĂRI nu poate "primi" tupluri, decât în

momentul unei prime încasări a unei facturi. Dar, de fapt, ceea ce se încasează într-o tranşă nu este o

linie (un produs) dintr-o factură, ci o parte sau întreaga factură, lucrul pe care-l reflectă pe deplin

dependenţa (54) şi relaţia ÎNCAS_FACT. Prin urmare, această ultimă relaţie nu ne este de nici un

folos, şi, pur şi simplu, renunţăm la ea. Senzaţia este oarecum ciudată, mai ales că un asemenea

demers nu este fundamentat prea riguros în teoria atât de matematizată a normalizării.

În concluzie, în 2NF, relaţia universală VÎNZĂRI va avea structura relaţiilor: ÎNCASĂRI,

FACTURI, ÎNCAS_FACT şi LINII_FACTURI.

3.5. A TREIA FORMĂ NORMALIZATĂ ŞI FORMA NORMALĂ

BOYCE-CODD

A treia formă normalizată (3NF) prezintă un interes aparte, deoarece este considerată de mulţi

specialişti drept un minimum acceptabil pentru structura unei baze de date relaţionale. Bazată pe a

doua formă normală şi dependenţele funcţionale tranzitive (de fapt, pe eliminarea lor), 3NF a cunoscut

două definiţii majore, una “originală”, formulată de Codd însuşi (cu variantele sale “folclorice”) şi a

doua, ce acoperă câteva lipsuri ale primeia, numită, după autorii săi, Boyce-Codd Normal Form, în

traducere oarecum liberă forma normală Boyce-Codd, mai pe româneşte, BCNF.

3.5.1 Dependenţe funcţionale directe/tranzitive

O dependenţă funcţională Dată1 Dată2 este directă atunci când nu există o Dată3

care angajează o dependenţă funcţională tranzitivă de genul: Dată1 Dată3 Dată2.

Pentru relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3, DF (7), (8), (9) şi (10) sunt

tranzitive deoarece: Cota ISBN Titlu; Cota ISBN Editura; Cota ISBN

LocSediuEd; Cota ISBN AnApariţie. În plus şi DF (13) este tranzitivă, întrucât

ISBN Editura LocSediuEd.

Page 48: BD1 2010 FotacheM Integral

48 Baze de date I

Pentru relaţia VÎNZĂRI dependenţele tranzitive simple sunt mai numeroase: CodPost

Jud Judeţ; CodPost Jud Regiune; CodCl CodPost Localitate;

CodCl CodPost Comuna; CodCl CodPost Jud Judeţ; CodCl Cod-

Post Jud Regiune; NrFact CodCl DenCl; NrFact CodCl Cod-

Fiscal; NrFact CodCl StradaCl; NrFact CodCl NrStradaCl; NrFact

CodCl BlocScApCl; NrFact CodCl TelefonCl; NrFact CodCl

CodPost, NrFact CodCl CodPost Localitate, …, dar şi (NrFact,Linie)

CodPr DenPr; (NrFact,Linie) CodPr UM; (NrFact,Linie) CodPr

ProcTVA; (NrFact, Linie) CodPr Grupa.

Pe baza noţiunilor prezentate se poate defini închiderea tranzitivă a unui ansamblu de

dependenţe funcţionale. Închidere tranzitivă reprezintă ansamblul dependenţelor totale directe +

dependenţele totale tranzitive. Acoperirea minimală, definită în capitolul anterior, este subansamblul

minimal de dependenţe funcţionale elementare care permit determinarea tuturor celorlalte dependenţe

funcţionale. Cu alte cuvinte: (1) nici o dependenţă funcţională nu este redundantă şi (2) orice

dependenţă funcţională totală face parte din închiderea tranzitivă. Spre deosebire de ceea ce am

discutat în capitolul anterior, acum suntem în măsură să eliminăm din ansamblul DF şi pe cele

tranzitive.

Astfel, pentru relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3, acoperirea minimală este

alcătuită din patru DF: Cota ISBN; ISBN Titlu; ISBN Editura; ISBN An-

Apariţie; Editura LocSediuEd, iar închiderea tranzitivă este următorul ansamblu de

dependenţe: Cota ISBN; Cota Titlu; Cota Editura; Cota LocSediuEd;

Cota AnApariţie; ISBN Titlu; ISBN Editura; ISBN LocSediuEd;

ISBN AnApariţie; Editura LocSediuEd.

Acoperirea minimală poate fi uşor reprezentată prin graful DF. Dependenţele tranzitive sunt

foarte uşor de identificat şi eliminat. Spre exemplu, dacă din figura 3.7 eliminăm săgeţile ce reprezintă

drumul direct dintre două atribute/grupe şi un atribut destinaţie, atâta timp cât între respectivele

atribute/conectori există un “traseu ocolitor”, se obţine graful acoperirii minimale – vezi figura 3.11.

Figura 3.11. Graful DF din acoperirea minimală pentru relaţia

BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3

Este drept, atributele Autor şi CuvîntCheie atârnă inutil în figură, stricând imaginea

grafului. Le-am păstrat însă pentru a nu omite nici un atribut din relaţia universală şi de a indica cheia

primară a relaţiei.

Pentru baza de date VÎNZĂRI acoperirea minimală, adică setul iniţial de DF din care au fost

eliminate dependenţele simetrice (redundante), parţiale şi tranzitive este următorul set de dependenţe

Page 49: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 49

funcţionale reprezentat în figura 3.8: Jud Judeţ; Jud Regiune; CodPost Loca-

litate; CodPost Comună; CodPost Jud; CodCl DenCl; CodCl CodFis-

cal; CodCl StradaCl; CodCl NrStradaCl; CodCl BlocScApCl; CodCl

TelefonCl; CodCl CodPost; CodPr DenPr; CodPr UM; CodPr Grupa;

CodPr ProcTVA; NrFact CodCl ; NrFact DataFact; NrFact Obs; (Nr-

Fact,Linie) Cantitate; (NrFact,Linie) PreţUnit; CodÎnc DataÎnc;

CodÎnc CodDoc; CodÎnc NrDoc; CodÎnc DataDoc; (CodÎnc,NrFact)

Tranşă.

3.5.2 Aducerea relaţiilor în a treia formă normală

Prima definiţie a celei de-a treia forme normale a unei relaţii poate fi formulată relativ simplu

şi incremental, prin raportare la 2NF. O relaţie se află în 3NF dacă: 1. Se găseşte în 2NF; 2. Toate

atributele care nu aparţin cheii primare nu depind funcţional de un alt atribut (ansamblu de atribute)

care nu face parte din cheie. A doua condiţie poate fi exprimată şi în maniera: toate dependenţele

funcţionale care leagă cheia primară de celelalte atribute sunt directe (netranzitive).

O altă formulare a 3NF utilizează tot două condiţii: a) toate atributele ne-cheie din R sunt

independente unele de altele, în sensul că nici unul dintre atributele neparticipante în cheia primară nu

apare în vreo DF în care sursa este constituită dintr-o combinaţie de celelalte atribute ne-cheie; b) toate

atributele ne-cheie din R sunt dependente ireductibil de cheia primară.

Important !

Chris Date este poate singurul care, precaut, la începutul discuţiilor legate de primele trei

forme normale, afirmă că situaţiile luate în considerare pornesc de la premisa că nu există chei

candidat. Premisa este foarte importantă, întrucât atunci când nu se elimină dependenţele simetrice şi

redundante pomenite în capitolul anterior, iar o relaţie are două mai multe chei candidat, sunt şanse

mari să apară complicaţii. Vom reveni la sfârşitul acestui paragraf cu analiza bazei de date VÎNZĂRI

şi vom vedea că şi precauţia lui Date este insuficientă, deoarece, în fapt, problema nu este numai a

cheilor candadidat ale unei relaţii, ci a dependenţelor simetrice şi redundante.

Trecerea din 2NF în 3NF presupune eliminarea DF tranzitive şi se poate face, pentru o relaţie,

în următoarea manieră:

a) Se identifică toate atributele ce nu fac parte din cheie şi sunt surse ale unor dependenţe funcţionale.

b) Pentru toate atributele identificate la punctul a), se constituie câte o relaţie în care cheie primară va

fi atributul respectiv, iar celelate atribute destinaţiile din dependenţele considerate.

Operaţiile a) şi b) se repetă şi pentru relaţiile "proaspăt" obţinute prin decompoziţie. De fiecare dată,

din relaţiile supuse descompunerii se elimină atributele destinaţie ale surselor non-cheie (sună bine,

nu-i aşa ?), păstrându-se atributele sursă, în vederea stabilirii legăturilor între tabele (cheile străine) şi,

implicit, declarării restricţiilor referenţiale.

Relaţia LINII_ARTICOLE_CONTABILE a fost adusă în paragraful 3.4.2 în 2NF prin

descompunerea în două relaţii, NOTE_CTB {NotaContabilă, Data} şi ARTICOLE_CTB

{NotaContabilă, Operaţiune, ContDebitor, ContCreditor, Suma} prezentate în figura 3.9.

Întrucât nici una din cele două relaţii nu conţine dependenţe funcţionale tranzitive, în 3NF baza de

date are aceaşi structură ca şi în 2NF.

Page 50: BD1 2010 FotacheM Integral

50 Baze de date I

Pentru a îndeplini cerinţele 2FN, relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 a fost

ruptă în tabelele CĂRŢI {Cota, ISBN, Titlu, Editura, LocSediuEd, AnApariţie} şi

TITLURI_AUTORI_CUVINTECHEIE {Cota, Autor, CuvântCheie} - vezi figura 3.10. Cea de-a

doua este alcătuită exclusiv din atribute-cheie, deci nu se pune în discuţie 3NF (adică relaţia este deja

în 3NF). Altfel stau lucrurile în CĂRŢI. Relaţia este în 2NF şi conţine dependenţe tranzitive. Există

două atribute din afara cheii primare, ISBN şi Editura, care sunt surse de DF, astfel încât

dependenţele (7), (8), (9), (10) şi (13) sunt tranzitive. Trecerea în 3NF se realizează prin constituirea a

două relaţii pe care o să le numim EDITURI {Editura, LocSediuEd} şi TITLURI {ISBN, Titlu,

Editura, AnApariţie}, iar din CĂRŢI rămâne EXEMPLARE { Cota, ISBN}. În concluzie, în

3NF baza de date BIBLIOTECĂ este alcătuită din patru tabele: EDITURI {Editura, LocSediuEd},

TITLURI {ISBN, Titlu, Editura, AnApariţie }, EXEMPLARE { Cota, ISBN} şi TITLURI_A-

UTORI_CUVINTECHEIE {Cota, Autor, CuvântCheie} – vezi fig. 3.12.

EDITURI TITLURI

Editura LocSediuEd ISBN Titlu Editura AnApariţie

Polirom Iaşi 973-683-889-7 Visual FoxPro.Ghidul dezvoltării aplicaţiilor profesionale Polirom 2002

973-683-709-2 SQL. Dialecte DB2, Oracle şi Visual FoxPro Polirom 2001

EXEMPLARE TITLURI_AUTORI_CUVINTECHEIE (fragment)

Cotă ISBN Cota Autor CuvântCheie

III-13421 973-683-889-7 III-13421 Marin Fotache baze de date

III-13422 973-683-889-7 III-13421 Marin Fotache SQL

III-13423 973-683-889-7 III-13421 Marin Fotache proceduri stocate

III-10678 973-683-709-2 III-13421 Marin Fotache FoxPro

III-10679 973-683-709-2 III-13421 Marin Fotache formulare

III-13421 Marin Fotache orientare pe obiecte

III-13421 Marin Fotache client-server

III-13421 Marin Fotache web

...

Figura 3.12. Relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 în 3NF

Din păcate, ne rămâne pe conştiinţă ultima tabelă, TITLURI_AUTORI_CUVINTECHEIE

care conţine un grad de ridicat de redundanţă a datelor, problemă rezolvată pe baza unui alt tip de

dependenţe - multivaloare (vezi capitolul viitor).

Ultimul exemplu din pagraful 4.4 a adus relaţia universală VÎNZĂRI în a doua formă

normalizată, prin ruperea acesteia în patru tabele: ÎNCAS_FACT, ÎNCASĂRI, LINII_FACTURI şi

FACTURI. Dar, după cum am văzut în paragraful precedent, există o serie de dependenţe tranzitive.

Să examinăm pe rând cele cinci relaţii.

* ÎNCAS_FACT {CodÎnc, NrFact, Tranşa} are un singur atribut ne-cheie, deci nici vorbă de DF

tranzitive.

* ÎNCASĂRI {CodÎnc, DataÎnc, CodDoc, NrDoc, DataDoc} nu prezintă nici un atribut în afara

CodÎnc care să fie sursă de DF.

* Cu LINII_FACTURI {NrFact, Linie, Cantitate, PreţUnit, CodPr, DenPr, UM, ProcTVA,

Grupa} situaţia este diferită. Atributul ne-cheie CodPr este determinant în o serie de dependenţe,

(33)-(36), pe baza acestora construindu-se relaţia PRODUSE {CodPr, DenPr, UM, ProcTVA, Grupa};

din LINII_FACTURI se elimină toate destinaţiile celor patru DF, obţinându-se LINII_FACT

{NrFact, Linie, CodPr, Cantitate, PreţUnit}.

Page 51: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 51

* În tabela FACTURI {NrFact, CodCl, DataFact, Obs, DenCl, CodFiscal, StradaCl,

NrStradaCl, BlocScApCl , TelefonCl, CodPost, Localitate, Comuna, Jud, Judeţ,

Regiune} lucrurile sunt şi mai interesante:

- mai întâi, se cuvine de observat că Jud este sursă a DF (1) şi (3), aşa încât FACTURI se

descompune în JUDEŢE{ Jud, Judeţ, Regiune} şi FACTURI_1 {NrFact, CodCl, DataFact,

Obs, DenCl, CodFiscal, StradaCl, NrStradaCl, BlocScApCl, TelefonCl, CodPost,

Localitate, Comună, Jud};

- dar şi FACTURI_1 prezintă DF; în dependenţele (5), (6) şi (7) CodPost este sursă, aşa încât

FACTURI_1 se descompune în CODURI_POŞTALE {CodPost, Localitate, Comună, Jud} şi

FACTURI_2 {NrFact, CodCl, DataFact, Obs, DenCl, CodFiscal, StradaCl, NrStradaCl,

BlocScApCl, TelefonCl, CodPost};

- în FACTURI_2 a mai rămas un singur atribut ne-cheie ce este sursă în DF (8)-(16), CodCl, astfel

încât şi aceasta se descmpune în CLIENŢI {CodCl, DenCl, CodFiscal, StradaCl, NrStradaCl,

BlocScApCl, TelefonCl, CodPost} şi FACT {NrFact, CodCl, DataFact, Obs}.

În concluzie, în 3NF relaţia iniţială VÎNZĂRI este “spartă” în următoarele opt relaţii:

ÎNCAS_FACT {CodÎnc, NrFact, Tranşa}, ÎNCASĂRI {CodÎnc, DataÎnc, CodDoc, NrDoc,

DataDoc}, PRODUSE {CodPr, DenPr, UM, ProcTVA, Grupa}, LINII_FACT {NrFact, Linie,

CodPr, Cantitate, PreţUnit}, JUDEŢE{ Jud, Judeţ, Regiune}, CODURI_POŞTALE

{CodPost, Localitate, Comună, Jud}, CLIENŢI {CodCl, DenCl, CodFiscal, StradaCl,

NrStradaCl, BlocScApCl, TelefonCl, CodPost} şi FACT {NrFact, CodCl, DataFact, Obs}.

3.5.3 Cheile candidat şi influenţa lor asupra 3NF

Promiteam în deschiderea paragrafului câteva detalii despre implicaţiile cheilor alternative

asupra aducerii unei relaţii în 3NF, implicaţii prea puţin evocate în literatura de profil. Astfel, revenim

la relaţia în 2NF LINII_FACTURI {NrFact, Linie, Cantitate, PreţUnit, CodPr, DenPr, UM,

ProcTVA, Grupa} din baza de date VÎNZĂRI. Dacă luăm în consideraţie că şi denumirea produsului

este unică, altfel spus, nu există două sortimente cu nume absolut identic, rezultă o serie de dependenţe

paralele:

CodPr DenPr DenPr CodPr

CodPr UM DenPr UM

CodPr Grupa DenPr Grupa

CodPr ProcTVA DenPr ProcTVA

Dacă urmăm algoritmul prescris de trecere a relaţiei LINII_FACTURI din 2NF în 3NF, ar

trebui să consituim câte o relaţie pentru fiecare sursă de DF care nu face parte din cheia primară a

relaţiei, iar în relaţia ce rămâne din LINII_FACTURI (LINII_FACT_2) să păstrăm pe post de chei

străine atât CodPr, cât şi DenPr: PRODUSE_1 {CodPr, DenPr, UM, ProcTVA, Grupa},

PRODUSE_2 { DenPr, CodPr, UM, ProcTVA, Grupa} şi LINII_FACT_2 {NrFact, Linie, CodPr,

DenPr, Cantitate, PreţUnit}.

Gogomănia este evidentă, însă, ca în atâtea alte cazuri, fundamentată ştiinţific ! Iar dacă

demostraţia nu a fost suficient de impresionantă, ce ziceţi de relaţia FACTURI în 2FN, în care orice

client poate fi identificat fără ambiguitate atât de CodCl, cât şi de DenCl şi de CodFiscal.

Page 52: BD1 2010 FotacheM Integral

52 Baze de date I

CodCl DenCl

CodCl CodFiscal

CodCl StradaCl

CodCl NrStradaCl

CodCl BlocScApCl

CodCl TelefonCl

CodCl CodPost

CodCl Localitate

CodCl Comuna

CodCl Jud

CodCl Judet

CodCl Regiune

DenCl CodCl

DenCl CodFiscal

DenCl StradaCl

DenCl NrStradaCl

DenCl BlocScApCl

DenCl TelefonCl

DenCl CodPost

DenCl Localitate

DenCl Comuna

DenCl Jud

DenCl Judet

DenCl Regiune

CodFiscal CodCl

CodFiscal DenCl

CodFiscal StradaCl

CodFiscal NrStradaCl

CodFiscal BlocScApCl

CodFiscal TelefonCl

CodFiscal CodPost

CodFiscal Localitate

CodFiscal Comuna

CodFiscal Jud

CodFiscal Judet

CodFiscal Regiune

Aşa că din LINIIFACTURI obţinem în prima fază a trecerii la 3NF nu mai puţin de patru

relaţii, apoi să continuăm cu descompunem celor trei constituite pe baza seriilor de dependenţe de mai

sus s.a.m.d.

Soluţia este cea pe care am sugerat-o în capitolul 4. Pentru identificarea oricărei entităţi

(produs, client, factură) păstrăm doar cheia primară (practic, renunţăm la celelate chei candidat), iar

toate dependenţele ce decurg din calitatea de cheie alternativă nu sunt luate în considerare.

3.5.4 Aducerea relaţiilor în 3NF prin grafuri ale DF

Modul în care a fost prezentată transformarea sucesivă a relaţiei universale iniţiale în 1NF,

2NF şi 3NF prin descompuneri succesive are certe valenţe pedagogice şi aplicabilitate practică.

Descompunerea nu este însă singura manieră de normalizare a unei baze de date relaţionale.

Philip A. Berstein este autorul unui model de normalizare prin sinteza unor relaţii binare

(construite pe baza DF) în relaţii mai mari şi, astfel, aducerea bazei în 3NF26

. În plus, autorul a

demonstrat că schema obţinută conţine un număr minim de relaţii. Deşi s-a demonstrat că algoritmul

este corect, aplicarea sa efectivă ridică destule dificultăţi, deoarece, după cum am mai discutat,

închiderea şi acoperirea minimală ale seturilor de DF chiar şi pentru relaţii nu foarte voluminoase sunt

greu de manevrat. Chris Date reproşează modelului (şi Bernstein acceptă observaţia) că operaţiunile

executate în cadrul algoritmului de sinteză sunt exclusiv de natură sintactică şi nu iau în considerare

latura semantică27

. Însă exemplul luat de Date pentru a pune în dificultate algoritmul de sinteză

constituie o capcană şi pentru varianta descompunerii.

O variantă mult mai directă şi relevantă pentru practicieni aparţine lui Henry Smith care a

publicat în Communications of the ACM un foarte interesant articol în care arată că o structură deplin

normalizată poate fi obţinută pe baza unei riguroase diagrame de DF28

. De fapt, diagramele sale

seamănă mai degrabă cu grafuri, decât forma lor consacrată29

. Smith afirmă că descompunerea clasică,

pornind de la relaţia universală este greoaie, iar construirea relaţiei universale reclamă, de multe ori,

un efort important. Lucrarea lui Smith este cu atât mai tentantă cu cât nu operează cu algoritmi pentru

determinarea închiderilor şi acoperirilor de seturi de DF. Ţinând seama că, în 3NF, relaţiile nu trebuie

să conţină dependenţe parţiale şi tranzitive, se va reprezenta grafic acoperirea minimală.

26

[Berstein76] 27

[Date00], pp. 377-378 28

[Smith85] 29

vezi, spre exemplu, [Date86] sau [Pratt & Adamski91]

Page 53: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 53

De fapt, un avantaj decisiv al grafurilor DF este posibilitatea identificării imediate şi eliminării

atât a dependenţelor parţiale, cât şi a celor tranzitive. În plus, există situaţii în care normalizarea

„clasică” generează probleme, în timp ce, folosind graful, schema obţinută este net superioară.

Astfel, pentru BD BIBLIOTECĂ, graful acoperirii minimale este reprezentat în figura 3.11.

Totuşi, pentru ilustrarea modului de identificare şi eliminare dependenţelor parţiale şi tranzitive

pornim de la graful din stânga figurii 3.13. Spre deosebire de figura „sursă”, săgeţile ce reprezintă

dependenţe parţiale sau tranzitive sunt punctate. Eliminându-le, obţinem graful din dreapta figurii pe

care delimităm câte o relaţie pentru fiecare sursă, simplă sau compusă. Relaţia corespondentă va

conţine sursa, drept cheie primară, plus toate destinaţiile sale directe (şi totale).

Figura 3.13. Cele patru tabele în 3NF pentru relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3

În final, nu ne mai rămâne decât să dăm câte un nume fiecăreia dintre cele patru relaţii:

EDITURI, TITLURI, EXEMPLARE, TITLURI_AUTORI_CUVINTECHEIE.

Cele 11 dependenţe ale relaţiei STUDENŢI_EXAMENE identificate în paragraful 3.4.2 pot fi

lesne reprezentate ca un graf - vezi figura 3.14. Dependenţele punctate sunt parţiale, deoarece

destinaţiile lor sunt legate de surse simple, aşa că în final sunt eliminate din calcul.

Figura 3.14. Aducerea relaţiei STUDENŢI_EXAMENE în 3NF cu ajutorul grafului DF

Pe graf sunt trei surse, aşa încât vom obţine trei relaţii, cu următoarele atribute: {Matricol,

NumePrenume, An, Specializare}, {CodDisc, DenumireDisc, NrCredite} şi {Matricol,

CodDisc, DataExamen, Nota}. Schema seamănă leit cu cea din paragraful 3.4.2.

Pentru BD VÎNZĂRI, prin reprezentarea sub formă de graf, economia de timp pentru aducerea

în 3NF este şi mai spectaculoasă, după cum reiese şi din figura 3.15. Singura problemă ţine de atenţie

în delimitarea tuturor surselor şi dependenţelor lor, ceea ce poate creea oarecare confuzie.

Page 54: BD1 2010 FotacheM Integral

54 Baze de date I

Figura 3.15. Decuparea relaţiilor în 3NF pentru baza de date VÎNZĂRI

Pentru majoritatea cazurilor întâlnite în practică, construirea grafului dependenţelor

funcţionale ce alcătuiesc acoperirea minimală, este una din cele mai simple şi la îndemână modalităţi

de aducere a bazei de date în 3NF. De o manieră asemănătoare se pot determina relaţiile celei de-a

treia forme normale utilizând matricea dependenţelor incluse în acoperirea minimală.

3.5.5 Forma normală Boyce-Codd

Definirea celei de-a treia forme normale, aşa cum a fost formulată iniţial de Codd, ridica o

serie de probleme în anumite circumstanţe, probleme pe care formularea din acest patagraf le rezolvă.

Chris Date este de părere că situaţiile în care definiţia originală a 3NF este insuficientă se traduce prin

trei condiţii simultane30

: a) relaţia are mai multe chei candidate; b) o parte din cheile candidate sunt

compuse; c) cheile compuse au atribute în comun.

Întrucât îndeplinirea simultană a celor trei condiţii se întâlneşte destul de rar în practică, a treia

formă normală, aşa cum a fost formulată în paragraful precedent, este suficientă. Se spune despre o

relaţie că este în forma normală Boyce-Codd (BCNF) dacă: 1. se află deja în 3NF; 2. nu există nici o

DF a cărei sursă să fie un atribut ne-component al cheii, iar destinaţia un atribut din cheie.

Într-o altă formulare, o relaţie R este în BCNF dacă şi numai dacă orice determinant (sursă de

dependenţă funcţională sau supercheie) este o cheie-candidat. Această exprimare are şi avantajul de a

nu lega BCNF de 3NF, unii autori preferând să discute problema normalizării direct în termenii

BCNF, fără a trece prin formele intermediare31

. Numele formei normalizate este discutabil, deoarece

primul care elaborează o definiţie echivalentă a ceea ce reprezintă astăzi BCNF este Ian Heath. În

penultima ediţie a cărţii sale ce a împlinit recent 25 de ani, Chris Date afirmă că ar fi fost mai indicată

titulatura forma normală a lui Heath (Heath Normal Form)32

.

30

[Date86], p.374 31

Vezi, spre exemplu, [Garcia-Molina s.a.] 32

[Date00], p.366

Page 55: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 55

Relaţia PROIECTE

Candace Fleming şi Barbara von Halle exemplifică utilitatea BCNF prin relaţia PROIECTE

din figura 3.1633

în care se manifestă urmăroarele restricţii:

la fiecare proiect există mai mulţi supervizori şi mai mulţi membri;

un membru poate participa la oricâte proiecte;

persoană poate superviza un singur proiect;

un supervizor poate coordona (în cadrul aceluiaşi proiect) mai mulţi membri;

într-un proiect, un membru este coordonat de un singur supervizor.

Figura 3.16. Relaţie aflată în 3NF, nu însă şi în BCNF

În aceste condiţii, se verifică următoarele DF: (1) (ProiectNr, Membru) Supervizor

şi (2) (Supervizor, Membru) ProiectNr.Prin urmare, relaţia are două chei candidat.

Totodată, însă, există şi DF Supervizor ProiectNr, ceea ce face din dependenţa (2) una

parţială. Atributul Supervizor este determinant fără a fi cheie candidat. În aceste condiţii, relaţia nu

este în BCNF. Pentru a o aduce în această formă normalizată este necesară transformarea grafului DF,

aşa cum arată figura 3.17.

Figura 3.17. Transformarea grafului DF pentru relaţia PROIECTE

Cele două relaţii obţinute sunt: SUPERVIZORI {Supervizor, ProiectNr} şi MEMBRI { Super-

vizor, Membru } reprezentate în figura 3.18.

Figura 3.18. Aducerea relaţiei PROIECTE în BCNF

33

Exemplul este preluat şi în alte lucrări, precum [Teorey99]

Page 56: BD1 2010 FotacheM Integral

56 Baze de date I

O prima problemă semnalată de cele două autoare se referă la pierderea unei restricţii de

integritate: la un proiect, un membru are un singur supervizor. Cu alte cuvinte, noua structură a BD

permite ca unui membru să-i fie asociaţi doi supervizori în cadrul aceluiaşi proiect, ceea ce este

incorect în condiţiile date. În plus, exemplului prezentat i se poate reproşa şi rigiditatea, deoarece în

decursul anilor un supervizor poate răspunde de mai multe proiecte, dar acest din urmă neajuns este

mai degrabă unul mărunt.

Profesori, cursuri şi specializări

Cred că, de la C.J. Date încoace, cel mai frecvent întâlnit exemplu pentru ilustrarea formei

normale Boyce-Codd este cel privitor la cursuri-profesori-studenţi. Nu vom face rabat de la “regulă” şi

definim tabela SCP, reprezentată în figura 3.19, în condiţiile în care un profesor poate preda la oricâte

specializări, dar numai un singur curs!

Figura 3.19. Relaţia SCP

Să analizăm dependenţele funcţionale.

- un profesor predă un singur curs: Profesor Curs

- un profesor predă la mai multe specializări: Profesor / Specializare

- un acelaşi curs poate fi ţinut de mai mulţi profesori: Curs / Profesor

- un acelaşi curs poate fi predat la mai multe specializări: Curs / Specializare

- la o specializare, se predau mai multe cursuri (din păcate): Specializare / Curs

- la o specializare predau cursuri mai mulţi profesori: Specializare / Profesor

- la o specializare, un curs este predat de un singur profesor: (Specializare, Curs) Profesor

- un profesor poate ţine cursul la mai multe specializări: (Profesor, Curs) / Specializare

Cuplul de atribute (Specializare, Curs) este cheie primară. Pe baza dependenţei

funcţionale Profesor Curs în BCNF tabela SCP va fi descompusă în două tabele, SP şi PC,

prezentate în figura 3.20.

Figura 3.20. Tabelele SP şi PC aflate în BCNF

Page 57: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 57

3.5.6 Dependenţe tranzitive necanonice

În exemplele de până acum am avut de lucrat cu dependenţe funcţionale în formă canonică –

cele în care destinaţia este alcătuită dintr-un singur atribut. Lucrurile nu sunt grozav de complicate,

identificarea dependenţelor fiind o operaţiune lesnicioasă. Din păcate, în unele situaţii este posibil ca

tranzitivitatea să se stabilească prin dependenţe ale căror destinaţii sunt compuse. La normalizarea

relaţiilor este recomandabil ca, în situaţia unor dependenţe funcţionale de genul: A B, A

C, A D, A E, dependenţe ce derivă din rolul de cheie primară pe care îl îndeplineşte

atributul A, să se verifice dacă nu cumva există fie o dependenţă funcţională simplă, de genul B

D, fie o dependenţă funcţională cu sursa compusă, de genul (B, C) D. Dacă una din cele două

afirmaţii de mai sus este adevărată, atunci dependenţa funcţională A D nu este directă.

Pentru un plus de credibilitate, să reluăm cazul bazei de date destinată evidenţierii rezultatelor

obţinute de studenţi la examene programate în sesiunile de pe parcursul unui an universitar

(STUDENŢI_EXAMENE) căreia îi extindem structura: R {Matricol, NumePren, An, Modul, Spec,

Grupa, CodDisc, DenDisc, NrCredDisc, CodProf, NumeProf, GradProf, DataEx, SalaEx,

Nota}, al cărei dicţionar simplicat este prezentat în tabelul 3.4

Tabel 3.4. Dicţionarul datelor pentru baza de date EXAMENE

Atribut Descriere Tip

Matricol O combinaţie de litere şi cifre ce identifică în mod unic un student CHAR

NumePren Numele şi prenumele studentului CHAR

An Anul de studiu NUMERIC

Modul Modulul de studiu: este o literă ce desemnează un grup de specializări CHAR

Spec Specializarea („C‟ de la contabilitate, „I‟ de la Informatică economică etc.) CHAR

Grupa Grupa de studiu NUMERIC

CodDisc Codul disciplinei din planul de învăţământ al specializării CHAR

DenDisc Denumirea disciplinei CHAR

NrCredDisc Numărul de credite alocat disciplinei NUMERIC

CodProf Codul profesorului CHAR

NumeProf Numele profesorului CHAR

GradProf P - profesor, C - conferenţiar, L - lector, A - asistent, R -preparator, A - din afară (colaborator din afara universităţii)

CHAR

DataEx Data examenului DATE

SalaEx Sala de desfăşurare a examenului CHAR

Nota Nota obţinută NUMERIC

Pentru simplificare, am luat în calcul numai studenţii de la profilul Economic, cursuri de zi. O

linie din R reprezintă rezultatul obţinut de un student la o disciplină într-o sesiune de examinare. Spre

exemplu, un student (matricol EL001103) poate obţine la disciplina Microeconomie (cod AE1001)

nota 4 (scuze) în prima sesiune de examinare – ianuarie 2005 (data examenului: 19 ianuarie 2005) şi

nota 9 în a doua sesiune de examinare – februarie 2005 (data ex: 14/02/2005). Cei mai puţin dispuşi la

efort se pot califica în faze superioare ale competiţiei (sesiunea mai 2005 s.a.m.d.). Acestea fiind

spuse, cheia primară a relaţiei R este, ca şi în cazul STUDENŢI_EXAMENE, (Matricol, CodDisc,

DataEx), pe baza căreia pot fi scrise următoarele DF:

(1) (Matricol, CodDisc, DataEx) NumePren

(2) (Matricol, CodDisc, DataEx) An

(3) (Matricol, CodDisc, DataEx) Modul

(4) (Matricol, CodDisc, DataEx) Spec

(5) (Matricol, CodDisc, DataEx) Grupa

(6) (Matricol, CodDisc, DataEx) DenDisc

Page 58: BD1 2010 FotacheM Integral

58 Baze de date I

(7) (Matricol, CodDisc, DataEx) NrCredDisc

(8) (Matricol, CodDisc, DataEx) CodProf

(9) (Matricol, CodDisc, DataEx) NumeProf

(10) (Matricol, CodDisc, DataEx) GradProf

(11) (Matricol, CodDisc, DataEx) SalaEx

(12) (Matricol, CodDisc, DataEx) Nota

Relaţia R este în prima formă normalizată. După cum am văzut în capitolul precedent, pentru a

demonstra că nu este în 2NF trebuie să găsim în ansamblul DF (1)-(12) măcar o DF parţială. Lucru

destul de simplu, dacă ţinem seamă de următoarele DF în care sursa o constituie unul dintre atributele

componente ale cheii.

Matricolul identifică în mod unic un student înscris la o specializare într-un an de studii: (13)

Matricol NumePren; (14) Matricol An; (15) Matricol Modul;

(16) Matricol Spec; (17) Matricol Grupa.

CodDisc identifică în mod unic fiecare discplină din planul de învăţământ al specializării: (18)

CodDisc DenDisc; (19) CodDisc NrCredDisc.

Pe baza ultimelor şapte DF, relaţia universală R se poate descompune astfel: R1 {Matricol,

NumePren, An, Modul, Spec, Grupa}, R2 { CodDisc, DenDisc, NrCredDisc}, R3 {Matricol,

CodDisc, CodProf, NumeProf, GradProf, DataEx, SalaEx, Nota}.

Cele trei relaţii sunt în 2NF, iar până aici similitudinea cu discuţia pricinuită de relaţia

STUDENŢI_EXAMENE este tulburătoare. Aducerea BD în 3NF echivalează cu eliminarea

dependenţelor tranzitive din R1, R2 şi R3. În R1 singurele DF existente sunt (13)-(17), deci această

relaţie este în 3NF. Nici R2 nu conţine dependenţe tranzitive; în schimb R3 da:

CodProf identifică în mod unic un profesor: (20) CodProf NumeProf;

(21)CodProf GradProf. Pe baza DF (20) şi (21), putem spune că (9) şi (10) sunt tranzitive,

aşa încât rupem R3 în R31 {CodProf, NumeProf, GradProf } şi R32 {Matricol, CodDisc,

CodProf, DataEx, SalaEx, Nota}. Astfel, în 3NF, relaţia universală R are o schemă alcătuită din

patru relaţii, după cum urmează: STUDENTI {Matricol, NumePren, An, Modul, Spec, Grupa},

DISCIPLINE { CodDisc, DenDisc, NrCredDisc }, PROFESORI {CodProf, NumeProf,

GradProf }, NOTE1 {Matricol, CodDisc, CodProf, DataEx, SalaEx, Nota}.

Nici una din cele patru relaţii nu conţine DF tranzitive. Cu toate acestea, schema este departe

de a fi optimă. Faţă de DF discutate, mai există două restricţii neluate în calcul în normalizare:

a. La o serie de curs (seriile de curs se constituie, pentru fiecare disciplină, la nivel de an, modul,

specializare) titular este un singur profesor: (22)(CodDisc, An, Modul, Spec) CodProf

b. Studenţii unei specializări susţin examenul la o disciplină în aceeaşi sală (şi aceeaşi zi): (23) (An,

Modul, Spec, DataEx) SalaEx

Să recapitulăm: în relaţia NOTE există DF: (8) (Matricol, CodDisc, DataEx)

CodProf, dar şi (22) (CodDisc, An, Modul, Spec) CodProf. Deoarece există DF (24):

Matricol An, Modul, Spec rezultă ca (8) este o DF necanonică, aşa încât relaţia NOTE1 s-ar

descompune în: DISC_SPEC_PROFI { CodDisc, An, Modul, Spec, CodProf} şi NOTE2

{Matricol, CodDisc, DataEx, SalaEx, Nota}. Câştigul este considerabil.

Analog se poate proceda cu DF (23), discuţia transferându-se în relaţia NOTE2, în care, din

calitatatea de cheie primară a celor trei atribute există dependenţa: (11) (Matricol, CodDisc,

Page 59: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 59

DataEx) SalaEx dar şi DF (23) (An, Modul, Spec, CodDisc, DataEx) SalaEx. Pe

baza dependenţei (24), relaţia NOTE2 se sparge în SALI_EX {An, Modul, Spec, CodDisc, DataEx,

SalaEx} şi NOTE {Matricol, CodDisc, DataEx, Nota}.

Ajungem, astfel, la o cu totul altă structură a bazei de date, mult mai bună decât precedenta:

STUDENTI {Matricol, NumePren, An, Modul, Spec, Grupa}, DISCIPLINE {CodDisc, DenDisc,

NrCredDisc}, PROFESORI {CodProf, NumeProf, GradProf }, DISC_SPEC_PROFI { CodDisc,

An, Modul, Spec, CodProf}, SALI_EX {An, Modul, Spec, CodDisc, DataEx, SalaEx}, NOTE

{Matricol, CodDisc, DataEx, Nota}

Ca de obicei, dependenţele tranzitive, canonice sau necanonice se observă mult mai lejer pe

graful DF (vezi fig. 3.21). Este suficient să decupăm câte o relaţie pentru fiecare sursă simplă şi

compusă din graf, sursă ce devine automat cheie a relaţiei respective, şi vom obţine structura de mai

sus.

Figura 3.21. Graful DF pentru baza de date EXAMENE

Structura finală îndeplineşte şi cerinţele formei normale Boyce-Codd ca fiecare sursă de DF să

fie cheie candidată. Putem institui şi o regulă: atunci când descompunerea se face urmând filiera

clasică:

stabilirea cheii primare a relaţiei iniţiale (universale), apoi

eliminarea, dintre dependenţele funcţionale ce decurg din calitatea de cheie primară a relaţiei

iniţiale, a celor parţiale, apoi

eliminarea, din toate relaţiile obţinute în pasul anterior, a tuturor dependenţelor tranzitive (în

care surse sunt cheile primare ale respectivelor relaţii),

trebuie verificat dacă în relaţia iniţială mai existau dependenţe cu sursa compusă ce nu apar drept cheie

primară sau candidat în nici una dintre relaţiile finale obţinute.

3.5.7 Adăugarea de atribute în schema unei baze de date

Revenim la un caz “clasat” pentru 3NF, cel al relaţiei BIBLIOTECĂ_TUPLURI_NOI_SO-

LUŢIA_3. Combinaţia care diferenţiază orice linie de toate celelalte este (Cota, Autor,

CuvîntCheie). Intuim însă un aspect pe care dependenţele şi, implicit, normalizarea îl scapă, şi

anume că autorii şi cuvintele cheie se referă la o carte (adică ISBN) şi nu la un exemplar fizic din

depozit (Cota). Deci graful DF nu ar trebui să arate ca în figura 3.13, ci precum în figura 3.22. Din

Page 60: BD1 2010 FotacheM Integral

60 Baze de date I

păcate, acesta este un aspect pe care îl intuim, dar e mai greu de demonstrat “ştiinţific” prin

normalizare. Un argument în plus pentru a nu considera normalizarea drept un proces infailibil, ci doar

un ghid în construirea unei scheme a BDR cât mai bune.

Figura 3.22. Autorii şi cuvintele cheie corespund titlurilor, nu exemplarelor din cărţi

Pe baza grafului de mai sus, din schema bazei de date în 3NF se schimbă ultima tabelă,

TITLURI_AUTORI_CUVINTECHEIE2 {ISBN, Autor, CuvântCheie}. Conţinutul celor patru

relaţii este acum cel din figura 3.23.

EDITURI Editura LocSediuEd

Polirom Iaşi

TITLURI ISBN Titlu Editura AnApariţie

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale

Polirom 2002

973-683-709-2 SQL. Dialecte DB2, Oracle şi Visual FoxPro Polirom 2001

EXEMPLARE Cotă ISBN

III-13421 973-683-889-7

III-13422 973-683-889-7

III-13423 973-683-889-7

III-10678 973-683-709-2

III-10679 973-683-709-2

TITLURI_AUTORI_CUVINTECHEIE2 ISBN Autor CuvântCheie

973-683-889-7 Marin Fotache baze de date

973-683-889-7 Marin Fotache SQL

973-683-889-7 Marin Fotache proceduri stocate

973-683-889-7 Marin Fotache FoxPro

973-683-889-7 Marin Fotache formulare

973-683-889-7 Marin Fotache orientare pe obiecte

973-683-889-7 Marin Fotache client-server

973-683-889-7 Marin Fotache web

973-683-889-7 Ioan Brava baze de date

973-683-889-7 Ioan Brava SQL

973-683-889-7 Ioan Brava proceduri stocate

973-683-889-7 Ioan Brava FoxPro

973-683-889-7 Ioan Brava formulare

973-683-889-7 Ioan Brava orientare pe obiecte

973-683-889-7 Ioan Brava client-server

973-683-889-7 Ioan Brava web

973-683-889-7 Cătălin Strâmbei baze de date

973-683-889-7 Cătălin Strâmbei SQL

973-683-889-7 Cătălin Strâmbei proceduri stocate

973-683-889-7 Cătălin Strâmbei FoxPro

973-683-889-7 Cătălin Strâmbei formulare

973-683-889-7 Cătălin Strâmbei orientare pe obiecte

973-683-889-7 Cătălin Strâmbei client-server

973-683-889-7 Cătălin Strâmbei web

Page 61: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 61

973-683-889-7 Liviu Creţu baze de date

973-683-889-7 Liviu Creţu SQL

973-683-889-7 Liviu Creţu proceduri stocate

973-683-889-7 Liviu Creţu FoxPro

973-683-889-7 Liviu Creţu formulare

973-683-889-7 Liviu Creţu orientare pe obiecte

973-683-889-7 Liviu Creţu client-server

973-683-889-7 Liviu Creţu web

973-683-709-2 Marin Fotache baze de date

973-683-709-2 Marin Fotache algebră relaţională

973-683-709-2 Marin Fotache SQL

Figura 3.23. O 3NF mai bună pentru relaţia BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3

Se observă cu ochiul liber o diminuare a volumului ultimei tabele, astfel încât ne-am

încumetat să reprezentăm toate liniile. Rămâne, în schimb, un grad de redundanţă chiar şi în forma

“comprimată” a tabelei.

Ca element de improvizaţie, putem încerca să adăugăm în BIBLIOTECĂ_TU-

PLURI_NOI_SOLUŢIA_3 câteva atribute care să „atragă” în dependenţe atributele Autor şi

CuvîntCheie. Astfel, pornim de la faptul că, pe coperta unei cărţi, autorii sunt trecuţi într-o anumită

ordine, deloc întâmplătoare, iar la extragerea informaţiilor despre cărţi această ordine trebuie

respectată. Prin urmare, introducem în schemă atributul OrdineCopertă, împreună cu DF: (ISBN,

OrdineCopertă) Autor. De asemenea, pentru a putea extrage cărţile legate de unul sau mai

multe cuvinte cheie în ordinea relevanţei, introducem atributul Revelanţă ce indică nivelul la care

este tratată într-o carte o anumită noţiune/sintagmă (cuvînt cheie): (ISBN, CuvîntCheie)

Relevanţă. Pe baza acestor două noi dependenţe, graful din figura 3.22 se modifică după cum este

sugerat în figura 3.24.

Din noul graf, relaţiile construite sunt aproape impecabile: EDITURI {Editura, LocSediuEd},

CĂRŢI {ISBN, Titlu, AnApariţie, Editură}, AUTORI_CĂRŢI {ISBN, OrdineCopertă, Autor},

CĂRŢI_CUVINTE_CHEIE {ISBN, CuvîntCheie, Relevanţă}, EXEMPLARE_CĂRŢI {Cota, ISBN}.

Figura 3.24. Noul graf pentru relaţia BIBLIOTECĂ (cu două atribute noi)

3.6 DEPENDENŢE MULTIVALOARE ŞI A PATRA FORMĂ NORMALĂ

Formelele normalizate 4 şi 5 constituie cea mai dificilă parte din procesul de normalizare a

unei baze de date. Aceasta este vestea rea. Vestea bună este că rareori proiectanţii bazei trebuie să

ajungă în acest stadiu al discuţiilor, mai ales în ceea ce priveşte 5NF, deoarece majoritatea cazurilor

practice pot fi rezolvate onorabil prin aducerea bazei în 3NF/BCNF sau, uneori, în 4NF.

Page 62: BD1 2010 FotacheM Integral

62 Baze de date I

Dependenţele funcţionale discutate până în acest paragraf sunt relativ uşor de identificat şi

explicat. În prezentul capitol vor fi prezentate un tip de dependenţe ceva mai dificil – depedenţele

multi-valoare. Întrucât incidenţa practică a dependenţelor de joncţiune şi celei de-a cincea forme

normale este una nesemnificatică, în acest curs acestea vor fi trecute sub tăcere.

3.6.1 Dependenţe multivaloare

Dependenţa multivaloare sau multivalorică şi, implicit, a patra formă normală au fost

introduse la câţiva ani după dependenţele funcţionale şi primele trei forme normale, prin 1977 de către

Ronald Fagin34

. Fie relaţia R {A1, A2,...., An} şi X, Y şi Z trei subansambluri de atribute ale

ansamblului {A1, A2,...., An}. Există o dependenţă multi-valoare (DMV) între X şi Y dacă şi numai

dacă: (a) la fiecare apariţie (valoare) a lui X poate fi asociată una sau mai multe apariţii (valori) ale lui

Y; (b) această asociaţie nu depinde de apariţiile lui Z. Se notează X Y sau X Y | Z şi

se spune că atributul Y este multidependent faţă de atributul X sau că atributul X multidetermină pe

Y.

Cea mai rezonabilă definiţie a DMV o furnizează C.J. Date. Fie relaţia R {A1, A2,...., An} şi

X, Y şi Z trei subansambluri ale ansamblului {A1, A2,...., An}. Există o dependenţă multi-valoare

între X şi Y în următoarea situaţie: dacă (x,y,z) şi (x,y',z') sunt două tuplete ale relaţiei R, atunci

(x,y',z) şi (x,y,z') aparţin, de asemenea, lui R. Într-o formulare uşor schimbată, Elmasri şi Navathe

definesc DMV de maniera următoare: dacă în R există două tupluri t1 şi t2 pentru care t1[X] = t2[X],

atunci există în R alte două tupluri, t3 şi t4, care satisfac următoarele condiţii (notăm prin Z toate

celelalte (decât X şi Y) atribute din R, adică (R - (X U Y))):

t3[X] = t4[X] = t1[X] = t2[X]

t3[Y] = t1[Y] şi t4[Y] = t2[Y]

t3[Z] = t2[Z] şi t4[Z] = t1[Z]35

.

După cum afirmam mai sus, aspectul contra-intuitiv al definiţiei DMV ţine de faptul că X este

independent de Y, ceea ce s-ar traduce prin “X oarecare, Y oarecare”. O bună explicaţie în acest sens

o găsim în [Dollinger98]36

: o valoare dată a lui X se găseşte în R în combinaţie (formează tupluri) cu

fiecare pereche de valori (y,z) din produsul cartezian al mulţimilor Yx (valorile lui y care apar în

combinaţie cu un x dat) şi Zx. Aceasta înseamnă ca mulţimile Yx şi Zx sunt independente între ele.

Cele mai tentante exemple de DMV sunt cele teoretice, aşa cum este cel din figura 3.25. În

această relaţie există nu există nici una din dependenţele funcţionale: X / Y; X / Z ; Y

/ X; Y / Z; Z / X; Z / Y.

34

[Fagin77] 35

[Elmasri & Navathe 00], p. 514 36

[Dollinger98], p.163

Page 63: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 63

Figura 3.25. Exemplu teoretic de relaţie în care există DMV

La o examinare mai atentă se observă dependenţele multi-valoare X Y (sau X

Y | Z) şi X Z (sau X Z | Y); aceasta deoarece în condiţiile în care există

tuplurile (x1,y1,z1) şi (x1,y2,z2), există şi tuplurile (x1,y1,z2) şi (x1,y2,z1).

Cel mai dureros aspect al dependenţelor multivaloare ţine de faptul că, în realitate, acestea

trebuie identificate de către cei care fac analiza şi proiectarea bazei de date, pe baza legăturilor

semantice dintre atributele bazei, şi nu pe baza unui caz simplist (în cazul nostru, o tabelă cu şapte

linii).

Profesori, cursuri & capitole

Cazul următor este aproape identic celui prezentat de C.J. Date în ediţiile sale ale lucrării An

Introduction to Database Systems37

. Relaţia are trei atribute: CPC {Curs, Profesor, Capitol}38

. La

o facultate "oarecare", pot exista mai mulţi profesori titulari ai unei aceleaşi discipline. Dacă

presupunem că toţi profesorii cad de acord asupra unei programe unice pentru disciplina respectivă,

diferenţiat fiind numai modul de "tratare" şi ponderea fiecărui capitol, putem imagina o situaţie ca în

figura 3.26.

Analizăm dependenţele dintre atribute:

- Un acelaşi curs poate fi "ţinut" de mai mulţi profesori: Curs / Profesor

- Un curs este alcătuit din mai multe capitole: Curs / Capitol

- Fiecare profesor poate preda mai multe cursuri: Profesor / Curs şi, implicit, Profesor

/ Capitol

Figura 3.26. Relaţia CPC în care există DMV

37

Spre exemplu, [Date86], pp. 381-382 38

La C.J.Date relaţia era CTX {Curs, Profesor, LucrareBibliografică}

Page 64: BD1 2010 FotacheM Integral

64 Baze de date I

- Un acelaşi capitol poate apărea în cursuri diferite. De exemplu, un capitol dedicat Poştei Electronice

poate apărea şi în cursul Bazele informaticii Economice şi în cursul de Birotică. Capitolul Modelul

Entitate-Asociaţie este în planul cursului de Baze de date, dar şi al cursului Analiza sistemelor

informatice (pentru modelul conceptual al datelor). Prin urmare, Capitol / Curs iar Capitol

/ Profesor.

Numai în condiţiile unei programe unice, la nivelul facultăţii, pentru fiecare curs există

dependenţele multi-valoare: Curs Profesor; Curs Capitol.

C.J. Date face o observaţie foarte bine venită: într-o asemenea relaţie, pentru a introduce trei

profesori şi patru capitole ar fi suficiente patru linii, şi nu 12 (3*4). Aceasta naşte însă câteva

probleme: cum se realizează corespondenţa profesor-capitol, adică ce capitole a elaborat fiecare

profesor ? Numărul capitolelor fiind mai mare ca al autorilor, pentru acele linii, ce profesori apar ?

Cum este interpretată fiecare linie în acest caz ? Cum se fac actualizările ?

Relaţia COMENZI_PRODUSE_MATERIALE

O întreprindere manufacturieră foloseşte pentru confecţionarea unei unităţi de produs finit o

serie de materii prime şi materiale, în conformitate cu fişa tehnologică. Producţia este pe comenzi. Se

poate imagina o tabelă care să conţină informaţiile cu privire la ce materiale sunt necesare onorării

unei anumite comenzi, COMENZI_PRODUSE_MATERIALE – vezi figura 3.27.

Figura 3.27. Relaţia COMENZI_PRODUSE_MATERIALE

În această tabelă, este imperios necesar ca, atunci când un produs apare într-o comandă, să se

introducă câte o linie pentru fiecare material ce intră în fabricaţia produsului respectiv. Prin urmare,

există dependenţele multi-valoare: Produs Comandă | Material.

Din nou despre bibliotecă

Cel mai convingător exemplu de dependenţă multi-valoare este cel din relaţia

BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 pe care o preluăm aşa cum am lăsat-o în figurile 3.22

(graful) şi 3.23 (conţinutul în 3NF-BCNF). În relaţia TITLURI_AUTORI_CUVINTECHEIE2 fiecare

linie se referă la un autor şi un subiect (cuvânt cheie) pentru o carte (ISBN). Practic, pe baza acestei

tabele suntem în măsură să obţinem informaţii de genul:

ce cărţi a scris un anume autor,

Page 65: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 65

despre ce subiecte a scris un anume autor,

care sunt autorii unei cărţi,

ce subiecte sunt tratate într-o carte,

care sunt cărţile în care apare tratat un anume subiect,

care sunt autorii care au scris despre un subiect dat.

Pentru ca nici una dintre aceste informaţii să nu se piardă, există un preţ adecvat. Dacă, pentru

o carte, apare o nouă sintagmă (cuvânt cheie), în relaţie trebuie introdus nu un tuplu, ci n, unde n este

egal cu numărul autorilor acelei cărţi. Pentru o carte cu 5 autori şi 10 cuvinte cheie, în relaţia TI-

TLURI_AUTORI_CUVINTECHEIE2 trebuie introduse nu mai puţin de 50 de linii.

Pe baza faptului că pentru o carte, un autor trebuie să apară în combinaţie cu toate

sintagmenele tratate şi, vice-versa, orice sintagmă apare în combinaţie cu toţi autorii cărţii, există

DMV: ISBN Autor | CuvântCheie şi ISBN CuvântCheie | Autor.

3.6.2 A patra formă normală

Deşi mai exotică decât primele trei şi BCNF, a patra formă normală (4NF) işi are importanţa

sa. Margaret Wu39

afirma că, în timp ce majoritatea universitarilor şi autorilor din zona normalizării

tinde să neglijeze 4NF şi 5NF, un studiu întreprins într-o serie de firme ce foloseau (la începutul anilor

`90) aplicaţii cu baze de date a scos la iveală că mai mult de 20% dintre schemele bazelelor de date

analizate violau 4FN. Prin comparaţie, Wu nu a identificat nici un caz de violare a 5NF.

În lucrarea menţionată în paragraful anterior, Ronald Fagin formulează o teoremă conform

căreia o relaţie R cu trei atribute A, B şi C poate fi descompusă fără pierdere de informaţii în două

proiecţii ale sale: R1{A, B} şi R2 {A, C} dacă şi numai dacă există dependenţă multivaloare A

B | C. Aceasta este teorema care ne va permite aducerea unei relaţii în 4NF.

O relaţie este în 4NF dacă şi numai dacă: (a) Este în BCNF; (b) Toate dependenţele care se

manifestă în cadrul său sunt dependenţe funcţionale, altfel spus, eventualele dependenţe multi-valoare

conţinute sunt, de fapt, dependenţe funcţionale.

Vom lua pe rând exemplele prezentate în paragraful anterior şi vom îndeplini formalitatea

aducerii lor în 4NF. Începem cu cel mai îndrăgit exemplu, primul. Relaţia XYZ din figura 3.25

prezintă, aşa cum am discutat, dependenţa multi-valoare X Y | Z, în virtutea căreia o putem

descompune în două: XY {X, Y} şi XZ {X, Z} ca în figura 3.28.

Figura 3.28. Aducerea relaţiei XYZ în 4NF

Pe baza dependenţelor multivaloare prezentate în paragraful precedent, relaţia CO-

MENZI_PRODUSE_MATERIALE (figura 3.27) se descompune ca în figura 3.29.

39

[Wu92]

Page 66: BD1 2010 FotacheM Integral

66 Baze de date I

Figura 3.29. Aducerea relaţiei COMENZI_PRODUSE_MATERIALE în 4NF

Ceva mai interesantă este situaţia relaţiei BIBLIOTECĂ_TUPLURI_NOI_SOLUŢIA_3 care,

după cum am văzut în paragraful precedent, conţine o dependenţă multi-valoare. Prin eliminarea

acesteia (dependenţei) vom aduce relaţia în 4NF. Deşi rareori întâlnită în literatura de specialitate,

reprezentarea grafică a DMV în graful dependenţelor nu ridică probleme deosebite, mai ales atunci

când cele trei atribute (X, Y şi Z din definiţiile noastre) sunt simple. Iată, în figura 3.30, care ar fi o

posibilă reprezentare şi modul de decupare al relaţiilor în 4NF din graf.

Figura 3.30. Graf ce conţine deopotrivă DF şi DMV

Conform obiceiului, se va constitui câte o relaţie pentru fiecare sursă de dependenţă

funcţională (Cota, ISBN şi Editura). La aceste relaţii se va adăugă câte o relaţie pentru fiecare

destinaţie de dependenţă multi-valoare (Autor şi CuvîntCheie). Schema finală a bazei de date,

precum şi conţinutul relaţiilor sunt ilustrate în figura 3.31.

EDITURI Editura LocSediuEd

Polirom Iaşi

TITLURI ISBN Titlu Editura AnApariţie

973-683-889-7 Visual FoxPro. Ghidul dezvoltării aplicaţiilor profesionale

Polirom 2002

973-683-709-2 SQL. Dialecte DB2, Oracle şi Visual FoxPro Polirom 2001

EXEMPLARE Cotă ISBN

III-13421 973-683-889-7

III-13422 973-683-889-7

III-13423 973-683-889-7

III-10678 973-683-709-2

III-10679 973-683-709-2

Page 67: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 67

TITLURI_AUTORI ISBN Autor

973-683-889-7 Marin Fotache

973-683-889-7 Ioan Brava

973-683-889-7 Cătălin Strâmbei

973-683-889-7 Liviu Creţu

973-683-709-2 Marin Fotache

TITLURI_CUVINTECHEIE

ISBN CuvântCheie

973-683-889-7 baze de date

973-683-889-7 SQL

973-683-889-7 proceduri stocate

973-683-889-7 FoxPro

973-683-889-7 formulare

973-683-889-7 orientare pe obiecte

973-683-889-7 client-server

973-683-889-7 web

973-683-709-2 baze de date

973-683-709-2 algebră relaţională

973-683-709-2 SQL

Figura 3.31. 4NF pentru relaţia BIBLIOTECĂ

Iată că, după atâta amar de capitole şi discuţii, am reuşit să rezolvăm de o manieră acceptabilă

problema schemei bazei de date destinate gestionării informaţiilor revelante despre cărţile aflate în

biblioteca unei facultăţi/universităţi. Este, cred, inutil să mai intrăm în delalii vis-a-vis de reducerea

volumului bazei, ca şi cvasi-dispariţia oricărei forme de anomalie la inserarea, modificarea sau

ştergerea unei linii în oricare dintre cele cinci tabele.

3.6.3 Un mini-caz practic - baza de date FILMOGRAFIE

Discuţiile despre această bază de date au rămas suspendate încă de pe la începutul capitolului.

În paragraful 3.2 semnalam necazurile pricinuite de valorile nule ale cheii primare în condiţiile în care

o parte dintre premii era acordată la nivel de film (Oscar pentru regie, Oscar pentru cel mai bun film

etc.), iar altă parte la nivel de actor pentru prestaţia dintr-un film (Oscar pentru cel mai bun

actor/actriţă în rol principal/secundar). La acel moment singura soluţia era constituirea a două tabele

dedicate premiilor.

Pentru ceea ce urmează considerăm atributele din tabelul 3.3. (paragraful 3.2) între care

încercăm să determinăm dependenţele funcţionale şi multivaloare. Astfel, atributul IdFilm identifică

fără ambiguitate orice film, aşa că avem următoarea serie de DF: (1) IdFilm TitluOriginal;

(2) IdFilm TitluRO; (3)IdFilm AnLans.

Deoarece un film poate fi încadrat la mai multe genuri (vezi figura 2.5) şi avea mai mulţi

producători, regizori, actori şi premii, IdFilm / Gen; IdFilm / Producător; IdFilm

/ Regizor; IdFilm / Actor; IdFilm / DenPremiu.

Un premiu se decernează într-o localitate (ex. Oscarul la Hollywood, Leul de argint la

Veneţia, Ursul de aur la Berlin etc.): (4)DenPremiu LocDecernare

Premiile au, de obicei, mai multe categorii (ex. Oscarul are categoriile: Cel mai bun film, Cel

mai bun film străin, Efecte speciale, Regie etc.) şi se acordă de mulţi ani: DenPremiu /

Categorie; DenPremiu / AnPremiu.

Ne punem problema reflectării prin dependenţe a distribuţiei într-un film. După cum am văzut

mai sus (de fapt, ştiam de mult), într-un film sunt distribuiţi mai mulţi actori; însă şi un actor poate

Page 68: BD1 2010 FotacheM Integral

68 Baze de date I

juca în multe filme - Actor / IdFilm. Am avea de ales între dependenţele (IdFilm, Actor)

Rol (poate era mai nimerit ca atributul să se numească Personaj) sau (IdFilm, Rol)

Actor. Dumneavoastră pe care aţi alege-o ? Ei bine, prima n-ar fi chiar indicată, deoarece există filme

în care un actor joacă un dublu rol (vă mai amintiţi Zmeura de aur pentru dublul rol al lui Leonardo di

Caprio din filmul Masca de fier, în care îl interpretează pe regele Ludovic al XIV-lea şi pe fratele

regelui, unul "original", iar celălalt "mascatul" ?). În acest situaţii valorile grupului (IdFilm, Actor)

sunt identice, în timp ce valorile atributul Rol sunt diferite, ceea ce infirmă dependenţa. Privitor la cea

de-a doua dependenţă, pericolul invalidării ei ar apărea în situaţia când într-un film acelaşi rol este

jucat de mai mulţi actori. Din fericire rolurile nu sunt tocmai aşa. Chiar şi în filmele biografice, sau

marile saga de familie, diferitele etape din viaţa unui personaj (copil, adolescent etc.) sunt marcate de

roluri diferite. Aşa că numărăm a cincea dependenţă a relaţiei, (5) (IdFilm, Rol) Actor.

Cel puţin la fel de interesant stau lucrurile şi cu premiile. Faptul că Globul de aur

(DenPremiu) pentru Cea mai bună imagine (Categorie) a fost decernat în 1988 (AnPremiu)

filmului Mai bine nu se poate (titlul original - As Good As It Gets), film ce are codul (IdFilm) 11899

în baza noastră de date, ne-ar determina să scriem dependenţa în forma: (DenPremiu, Categorie,

AnPremiu) IdFilm.

Ce ne facem, însă, când un premiu la aceeaşi categorie este acordat simultan la două filme ?

Nu e cazul Oscarurilor, dar, spre exemplu, câte o menţiune specială din partea juriului (Categorie)

la ediţia din 2007 (AnPremiu) a festivalului Ursul de aur (Denpremiu) ar pute fi acordată filmelor cu

identificatorii 7898843, 8967883 şi 9312299. Dependenţa este aruncată în aer. Soluţia este însă pe-

aproape: un film nu poate lua un acelaşi premiu, la aceeaşi categorie, în ani diferiţi, aşa că schimbăm

poziţia a două atribute: (6) (DenPremiu, Categorie, IdFilm) AnPremiu.

Dependenţa de mai sus nu rezolvă problema premiilor acordate pentru prestaţiile actoriceşti, în

care laureat este un actor pentru prestaţia dintr-un film. Am putea alege între varianta (DenPremiu,

Categorie, AnPremiu, IdFilm) Actor şi (DenPremiu, Categorie, AnPremiu, Actor)

IdFilm. Prima variantă este minată dacă într-un an un premiu (la aceeaşi categorie) este

acordat simultan pentru doi actori care joacă în acelaşi film. Situaţia nu pare a fi prea frecventă, dar

nici imposibilă, dacă ne gândim la festivalurile mai puţin poleite decât Oscar, Palme D'Or etc. A doua

variantă are şi ea un competitor, croit după analogia cu dependenţa (6): (DenPremiu, Categorie,

IdFilm, Actor) AnPremiu. Iniţial eram tentat de această ultimă formă, deoarece era similară

celei care reflecta celelalte tipuri de premii. Există, însă, o categorie de premii ce crează o serie de

dureri de cap: premiile pentru întreaga carieră. Acestea nu sunt legate de un film anume, ci răsplătesc

o carieră, mai lungă sau mai scurtă, de cineast. Nefiind legată de un film, sursa dependenţei, pe baza

căreia vor constitui o relaţie, ar conţine în aceste situaţii o valoare NULL (pentru IdFilm), ceea ce e

inadmisibil pentru o componentă a cheii primare. Cu toate acestea, vom păstra această dependenţă,

întrucât premiul pentru întreaga carieră nu se acordă numai actorilor, ci şi regizorilor (teoretic şi altor

profesiuni legate de industria/arta cinematografică), iar dacă optam pentru a doua variantă tot nu

scăpam de valori nule, e drept, pentru un atribut necheie. Ca să tranşăm discuţia, întrucât baza de date

se referă la informaţii despre filme, nu ne propunem să preluăm premiile individuale, aşa că ne

declarăm mulţumiţi de dependenţa: (7) (DenPremiu, Categorie, IdFilm, Actor) AnPremiu

Page 69: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 69

Au rămas neluate în seamă trei atribute, Producător, Regizor şi Gen. Intuiţia ne spune că

este imposibil de stabilit vreo dependenţă între ele, chiar dacă se referă la acelaşi film. Pe de altă parte,

cele trei atribute par cât se poate de independente unele de altele. Verificăm, mai întâi, cerinţa DMV

pentru primele două. Dacă filmul X cu id-ul 234556 are, printre regizori, pe Reg1 şi Reg2 şi cel puţin

doi producători, Prod1 şi Prod2, atunci trebuie să verificăm că, dacă atributele ar apărea într-o relaţie

de tip R {IdFilm, Producător, Regizor}, oricare dintre tuplurile următoare sunt posibile:

RELx

IdFilm Producător Regizor

... ... ...

234556 Prod1 Reg1

234556 Prod2 Reg2

234556 Prod1 Reg2

234556 Prod2 Reg1

... ... ...

Figura 3.32. Raportul dintre atributele Producător şi Regizor

Prezenţa ultimelor două tupluri în relaţia din figura 3.32 pare a fi agreată, nu însă şi necesară.

Schema de mai sus, însă, trebuie să furnizeze răspunsul corect la întrebarea Care sunt filmele în care

producătorul Prod1 a lucrat cu regizorul Reg1 ? Faptul că, pentru a-şi păstra viabilitatea

informaţională, relaţia trebuie să conţină toate cele patru tupluri constituie motiv suficient pentru a

"trâmbiţa" dependenţa multivalorică: IdFilm Producător | Regizor.

Pe acelaşi calapod, pornind de la răspunsul la întrebarea Ce genuri de filme a produs/regizat

un anumit cineast ?, putem demonstra că: IdFilm Producător | Gen sau IdFilm

Regizor | Gen. Forma finală a grafului dependenţelor funcţionale şi multivalorice ar putea fi cea

din figura 3.33.

Figura 3.33. Graful dependenţelor pentru BD FILMOGRAFIE

Urmează partea cea mai frumoasă - decuparea relaţiilor din graf. Sunt două surse simple de

DF - DenPremiu şi IdFilm şi trei surse compuse - (IdFilm, Rol), (IdFilm, DenPremiu,

Categorie) şi (IdFilm, Actor, DenPremiu, Categorie), aşa că cinci relaţii vor fi obţinute din

dependenţele funcţionale. La acestea se mai adaugă trei, corespunzătoare dependenţelor multi-valoare.

Iată, deci, schema finală: PREMII_DENUMIRI {DenPremiu, LocDecernare}, FILME {IdFilm,

TitluOriginal, TitluRO, AnLans}, DISTRIBUŢIE {IdFilm, Rol, Actor}, PREMII_FILME

{IdFilm, DenPremiu, Categorie,AnPremiu}, PREMII_INTERPRETARE {IdFilm, Actor ,

DenPremiu, Categorie, AnPremiu}, PRODUCĂTORI {IdFilm, Producător}, REGIZORI

{IdFilm, Regizor}, FILME_GENURI {IdFilm, Gen}.

Page 70: BD1 2010 FotacheM Integral

70 Baze de date I

3.7 CHEI SUROGAT

Folosind limbajul de lemn al tehnologiei informaţionale, putem spune că bazele de date

stochează persistent şi gestionează varii informaţii privitoare la fenomene, procese, tranzacţii,

persoane etc. din mai toate domeniile de activitate, în funcţie de aplicaţiile pentru care sunt proiectate

şi implementate. Una din problemele de căpătâi în lucrul cu aceste entităţi, procese, tranzacţii etc. ţine

de posibilitatea identificării lor fără ambiguitate.

3.7.1 Nevoia de surogate (în bazele de date)

Închipuiţi-vă ce-ar însemna ca firma noastră să vireze câteva sute de milioane de lei în contul

unui client, plătind astfel o factură, iar contul respectiv să fie confundat cu un altul, al altui client. Sau

ce s-ar întâmpla dacă la plata "pe card" a salariilor plătite s-ar produce niscaiva confuzii: cei mai

oropsiţi s-ar oripila văzând cât câştigă şefii (confirmând luminoasa teză "impozita-i-ar naiba !" a unor

preşedinţi retardaţi istoric şi economic), iar şefii ar fi cel puţin la fel de oripilaţi, dacă nu chiar îngroziţi

de mânia populară/proletară.

Aşa că vedem în jurul nostru tot soiul de "ingrediente" menite a face diferenţieri sau, altfel

spus, a identifica fiecare persoană, carte, factură, plată etc. Privind retrospectiv, în relaţia

STUDENŢI_EXAMENE - figura 3.1, paragraful 3.1.1 - fiecare student este identificat de matricolul

său, iar disciplinele au un cod (CodDisc) unic prin care evităm confuzia ce s-ar putea genera prin

folosirea exclusivă a denumirii disciplinei (respectiv, numelui studentului). De asemenea, valoarea

atributului NrFact este unică pentru orice factură emisă de firma noastră şi trimisă unui client (baza

de date VÎNZĂRI). ISBN-ul este un cod unic asociat, la nivel mondial, unei cărţi tipărite, în timp ce

cota identifică într-o bibliotecă un exemplar al unei cărţi. Codul numeric personal (CNP-ul) este cel

mai bun mod de a identifica o persoană, în timp ce codul fiscal este un element de încredere când ne

propunem să identificăm un client.

Din contră, în paragraful 3.1.4, în dicţionarul bazei de date VÎNZĂRI (tabelul 3.1), am apelat

atribute cu oarecare doză de artificialitate – CodCl (codul fiecărui client), CodPr (codul fircărui

produs), CodDoc (codul documentului) ca să nu mai vorbim de CodÎnc (codul fiecărei încasari).

Aceste atribute sunt exemple de cheie surogat. Dealtminteri, noţiunea de surogat desemnează un

produs artificial sau sintetic care este utilizat drept substitut pentru un produs natural. O cheie surogat

este o cheie artificială sau sintetică utilizată ca substitut al unei chei "naturale".

De cele mai multe ori, cheile surogat simplifică graful dependenţelor funcţionale şi, implicit,

sarcina demarcării relaţiilor. Astfel, revenind la relaţia LINII_ARTICOLE_CONTABILE din figura

3.6 (paragraful 3.3.1), ştiind că o notă contabilă are mai multe operaţiuni care, la rândul lor, conţin mai

mai multe corespondenţe ContDebitor - ContCreditor, introducem două atribute care să preia

explicaţiile/comentariile despre note, respectiv, operaţiuni - ExplicaţiiNotă şi ExplicaţiiOp.

Din paragraful 3.3.1 ştim că, în funcţie de modul de contare, configuraţia dependenţelor poate fi

sensibil diferită. Pentru uniformizarea celor două variante de numerotare a notelor contabile, putem

recurge la două atribute de tip cheie surogat. IdNotăContabilă va fi un număr unic asociat fiecărei

note contabile, iar IdOperaţiune va identifica orice operaţiune, indiferent de nota contabilă din care

face parte. Graful ia configuraţia din figura 3.34.

Page 71: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 71

Figura 3.34. Două chei surogat pentru LINII_ARTICOLE_CONTABILE

Schema obţinută prin decuparea dependenţelor din graf este: NOTE_CONTABILE

{IdNotăContabilă, NrNotă, Data, ExplicaţiiNotă}, OPERAŢIUNI {IdOperaţiune, NrOp,

IdNotăContabilă, ExplicaţiiOp} şi DETALII_OPERAŢIUNI {IdOperaţiune, ContDebi-

tor, ContCreditor, Suma}.

3.7.2 Puncte de vedere privind cheile surogat

Părerile despre cheile surogat sunt, fireşte, împărţite. Există autori care fac distincţia între chei

surogat definite/controlate de utilizatori şi chei surogat gestionate exclusiv de sistem (SGBD). Pentru

Codd, cheile primare definite şi controlate de utilizator folosite ca surogate permanente pentru entităţi,

ar prezenta trei dificultăţi la întrebuinţare40

:

Valorile cheilor surogat trebuie uneori schimbate. De exemplu, atunci când două

companii fuzionează, angajaţii trebuie reuniţi într-o sigură tabelă, şi nu este exclus ca,

datorită suprapunerilor, unele mărci să fie modificate.

Două relaţii ar putea avea chei surogat utilizator definite pe domenii diferite, chiar

dacă entităţile pe care le identifică sunt aceleaşi; de exemplu, o firmă poate fi

deopotrivă client şi furnizor al companiei noastre. În tabela FURNIZORI firma ar

putea fi identificată prin CodFurnizor, iar în tabela CLIENŢI prin CodClient. Deşi

firma este aceeaşi, ea este identificată prin două chei surogat diferite.

Uneori este necesară preluarea informaţiei despre o entitate înaintea atribuirii unei

chei surogat utilizator pentru aceasta sau, pe de altă parte, păstrarea valorii cheii

surogat chiar şi după ce entitatea nu mai constituie subiect al aplicaţiei. Cazurile tipice

sunt cele ale candidaţilor pentru angajarea pe un post (li se va atribui o marcă doar

dacă vor fi angajaţi, atribuirea petrecându-se după angajare), sau cele ale angajaţilor

pensionaţi.

Cele trei dificultăţi sunt suficiente lui Codd pentru a justifica folosirea cheilor surogat sistem,

pe care utilizatorul nu le poate nici modifica, iar uneori nici măcar vizualiza. Astfel, fiecare entitate ar

avea propria cheie surogat, unică la nivelul întregii baze de date. Două chei surogat sunt identice dacă

şi numai dacă desemnează aceeaşi entitate. Codd se gândea chiar şi la o comandă specială de fuzionare

(COALESCE) a două chei surogat care desemnează, în fapt, aceeaşi entitate. Mai mult, în articolul său

din 1979 publicat în ACM Transactions on Database Systems, autorul pledează pentru un model

relaţional extins în care, printre multe altele, unul dintre domenii să servească drept sursă pentru toate

cheile surogat din bază.

40

[Codd79]

Page 72: BD1 2010 FotacheM Integral

72 Baze de date I

La întrebarea "Ce e mai preferabil, să folosim cheile primare "naturale" sau cele surogat ?" nu

există un răspuns universal sau, chiar dacă ar exista, am evita să-l dăm. Atunci când atributul/atributele

ce identifică entitatea sunt disponibile şi stabile în timp, soluţia "naturală" este mai la îndemână: CNP-

ul pentru persoane, ISBN-ul pentru cărţi, CodFiscal pentru firme. Când apar probleme de

identificare, iar unicitatea este asigurată de un mare grup de atribute, sau când atributul/atributele

potenţiale chei nu prezintă stabilitate pe termen lung, atunci soluţia "surogat" este cea mai indicată. La

exemplele din acest paragraf, mai putem adăuga:

Seria şi numărul de buletin/carte de identitate - deşi identifică fiecare persoană, peste

un număr de ani, datorită schimbării buletinului/cărţii de identitate, combinaţia îşi

schimbă valoarea, iar dacă s-au făcut deja arhivări, vor apărea complicaţii.

Matricol - în multe şcoli, licee şi universităţi s-a practicat un sistem simplu de

atribuire a numărului matricol, astfel încât, după câţiva ani, un matricol era "reciclat".

Căutarea în baza de date arhivă a liceului ar ridica probleme mari, dacă intervalul care

interesează este de ordinul deceniilor. Necazuri similare ar apărea dacă unele firme ar

recicla, în timp, mărcile angajaţilor, numerele de inventar ale mijloacelor fixe etc.

Asemenea gen de discuţii este valabil însă şi la reciclarea cheilor surogat, aşadar ideea folosirii

unor surogate-sistem se poate dovedi benefică.

Dintre materialele dedicate cheilor surogat, v-am recomanda pe cele scrise de Mike Lonigro41

,

Ian Harrington42

, Graeme C. Simsion43

şi Fabian Pascal. În ceea ce mă priveşte, nu împărtăşesc nici

lehamitea unor autori faţă de cheile surogat, mergându-se, în acest sens, până la a le "demasca" drept

identificatoare deghizate de obiecte (celebrele OID-uri din orientarea pe obiecte), dar nici frenezia

"surogării" manifestată de mulţi proiectanţi de baze de date, deşi, recunosc, frenezia cu pricina nu are

nimic toxic în ea, ci, mai degrabă, ţine de un soi de lene (subscriu cu toată inima la ideea că lenea nu e

toxică, dacă nu e însoţită de "aditivi").

O altă temere legată de cheile surogat priveşte îngreunarea accesului la informaţiile din baza

de date, întrucât acesta nu se mai realizează prin atribute explicite, ci prin numere destul de irelevante

pentru obiectul/entitatea în cauză. Ori, utilizatorii obişnuiţi ar fi obligaţi să reţină kilograme întregi de

cifre nesuferite, doar pentru a obţine datele de care au nevoie. Nici acest neajuns nu trebuie să

descurajeze, deoarece trebuie să vedem o aplicaţie, indiferent de tipologia sa, pe cel puţin două

straturi, date şi interfaţă. În majoritatea copleşitoare a cazurilor, utizatorii interacţionează cu baza doar

prin meniuri, rapoarte şi mai ales formulare, iar cheile surogat pot fi chiar ascunse.

În orice caz, dacă nu am reuşit să tranşăm discuţia chei naturale-chei surogat, măcar să arătăm

cu degetul pe cele mai toxice: cheile inteligente. Astfel, au existat firme în care angajaţii

compartimentelor personal-salarizare audiaseră sau au citiseră în cursurile de baze de date sau

analiză/proiectare despre teoria codurilor şi, la angajarea unei persoane la compartimentul

Contabilitate, îi atribuiau o marcă de tip CTB85CC101, în care primele trei litere semnalizau că este

încadrat la compartimentul Contabilitate, iar cele două C-uri semnalizau biroul Calculaţia costurilor,

în cadrul compartimentului Contabilitate. Partizanii acestui sistem argumentau că marca ar conţine, în

acest fel, informaţii preţioase (compartimentul şi biroul) despre fiecare angajat, cheia fiind chiar, după

unii autori, inteligentă.

41

[Lonigro98] 42

[Harrington02], pp.77-82

Page 73: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 73

După cinci ani, însă, persoana respectivă se transferă la compartimentul Financiar. Asta

înseamnă că marca, fiind inteligentă, trebuie schimbată. Costul schimbării depinde de întrebarea: unde

se află cele 5 (ani) * 12 (luni) înregistrări despre calcululul lunar al salariului, plus 5(ani) * 12 (luni) *

20 (zile lucrătoare) înregistrări, dacă pontajul se preia zilnic ? Răspunsul poate fi însoţit de apăsătoare

dureri de cap.

Nu întotdeauna atributele implicate în schema bazei pot fi identificate cu uşurinţă. Mai mult,

deseori nu strică o doză rezonabilă de artificial, de improvizaţie, doză care poate salva pe termen lung

o schemă sau măcar să o scutească de multe necazuri. Deşi mai puţin intuitiv decât modelul obiectual,

relaţionalul are certe atuuri în ceea ce priveşte mecanismul de declarare a restricţiilor şi mai ales în

ceea ce priveşte opţiunile de extragere a informaţiilor din bază (mecanismul de manipulare a datelor).

Chiar şi aşa, rămân în discuţie o mare parte din restricţii care ţin de integritatea bazei de date, dar sunt

legate nu atât de reguli intrinseci modelului (cheie primară, restricţie de entitate, integritate

referenţială), cât de regulile aplicaţiei, sau, altfel spus, reguli ale afacerii. Din păcate, nici un model de

date nu oferă un mecanism infailibil de declarare a tuturor restricţiilor semantice dintr-o bază de date.

3.8 DEPENDENŢE DE INCLUZIUNE

Este greu de explicat motiv pentru care toate cărţile majore care tratează problema

normalizării şi, în general, proiectarea bazelor de date, sunt atât de discrete în materie de dependenţe

de incluziune. Nu-i vorbă că noţiunea ar fi din cale-afară de dificilă, ci mai deagrabă că în aplicaţiile

economice, chiar de calibru mediu, este aproape imposibil să nu se manifeste un asemenea gen de

dependenţă.

Într-o exprimare lejeră, între două atribute X şi Y există o dependenţă de incluziune (DI) dacă

şi numai dacă orice valoare a lui X este obligatoriu şi valoare a lui Y şi se notează simplu X Y. Mai

general, o DI semnifică faptul că proiecţia pe m atribute date ale relaţiei R este un subset al proiecţiei

pe m atribute date din relaţia S. De aici şi caracterizarea DI ca dependenţe interrelaţii44

. Definiţia însă

nu obligă ca R şi S să fie neapărat distincte, deci DI se poate institui între atribute şi grupe de atribute

ale aceleaşi relaţii. După Casanova s.a., DI semnalizează relaţiile de tip este-un/o45

, iar Sciore este şi

mai limpede când scrie că folosim DI pentru a arăta că două atribute din relaţii diferite se referă la

acelaşi lucru46

. Am putea amenda afirmaţia lui Sciore prin precizarea faptului că atributele nu trebuie

neapărat să fie din relaţii diferite.

Cel mai frecvent exemplu de DI este Manager Angajat47, care înseamnă că orice manager

este un angajat al companiei. Graful din figura 3.35 ilustrează o situaţie comună în firmele de

pretutindeni: fiecare angajat al unei întreprinderi este arondat unui compartiment (Producţie,

Marketing, Personal, Financiar, Contabilitate etc.); fiecare compartiment are un singur şef; fiecare şef

este, şi el, angajat, chiar dacă mai uită uneori de lucrul acesta.

43

[Simsion01], pp.282-285 44

[Fagin 81] 45

[Casanova s.a.82] 46

[Sciore83] 47

[Fagin81], [Casanova s.a.82], [Johnson & Klug82], [Sciore83]

Page 74: BD1 2010 FotacheM Integral

74 Baze de date I

Figura 3.35. O dependenţă de incluziune

Dependenţa de joncţiune reprezentată prin săgeata punctată este, deci, MarcăŞef Marcă.

Decuparea relaţiilor din graf nu ridică mari probleme. Avem două surse de DF, prin urmare cele două

relaţii vor fi: PERSONAL {Marcă, NumePren, Adresă, Compartiment} şi COMPARTIMENTE

{Compartiment, MarcăŞef}. Dependenţa de incluziune va fi încorporată în schemă sub forma

restricţiei referenţiale dintre COMPARTIMENTE.MarcăŞef şi PERSONAL.Marcă.

Frunze şi conturi

Ne îndepărtăm de exemplele consacrate, apelând la baza de date CONTABILITATE, pe care,

după discuţia din paragrafele precedente, am adus-o la schema: NOTE_CONTABILE

{IdNotăContabilă, Data, ExplicaţiiNotă}, OPERAŢIUNI {IdOperaţiune,

IdNotăContabilă, ExplicaţiiOp, NrConturiDebitoare, NrConturiCreditoare,

UnContDebitor, UnContCreditor} şi DETALII_OPERAŢIUNI {IdOperaţiune,

ContDebitor, ContCreditor, Suma}. Ei bine, a sosit momentul pentru a introduce una din cele

mai importante restricţii ale unei aplicaţii de contabilitate generală: conturile care apar în operaţiunile

contabile nu trebuie să aibă conturi sintetice/analitice subordonate !

Să începem explicaţiile mai din "amonte". Pentru reflectarea elementelor patrimoniale, a

capitalului, datoriilor şi obligaţiilor, contabilitatea foloseşte conturi organizate în serii, clase şi grupe48

.

De exemplu, în seria 1 sunt incluse conturile organice de bilanţ, în seria 2 conturile de procese, în seria

3 conturile de rezultate, în seria 4 conturile în afara bilanţului (extrapatrimoniale), iar în seria 5

conturile privind circuitul contabilităţii manageriale. Seria 1 conţine 7 clase, pentru: capitaluri, active

imobilizate, stocuri, terţi, trezorerie, regularizare şi conturi rectificative. Fiecare clasă are una sau mai

multe serii de conturi. Astfel, clasa Stocuri are şapte grupe: materii şi materiale, obiecte de inventar;

producţie în curs; produse; stocuri la terţi; animale; mărfuri; ambalaje. În fiecare grupă sunt conturile

propriu-zise care pot fi sintetice de ordinul 1 (alcătuite din trei cifre) sau de ordinul 2 (patru cifre).

Fiecare sintetic de ordinul 2 aparţine unui sintetic de ordinul 1. Tabelul 3.5 indică o porţiune din

planul de conturi al firmei X. Orice tip cont sintetic poate fi descompus pe conturi analitice, în funcţie

de specificul şi interesele întreprinderii.

Tabel 3.5. Extras din planul de conturi al unei firme

Simbol cont TipCont Denumire cont

Clasa: Conturi de CAPITALURI

...

101 pasiv Capital social

1011 pasiv Capital social subscris ne-vărsat

1012 pasiv Capital social subscris vărsat

...

48

Vezi, spre exemplu, Horomnea, E. - Bazele contabilităţii. Concepte, aplicaţii, lexicon, Editura Sedcom Libris,

Iaşi, 2004, pp. 178-180

Page 75: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 75

211 activ Terenuri şi amenajări

2111 activ Terenuri

2112 activ Amenajări

....

301 activ Materii prime

301.01 activ Nisip

301.02 activ Ciment

301.03 activ Var

301.09 activ Alte materii prime

...

308 bifuncţional49

Diferenţe de preţ la materii prime

...

401 pasiv Furnizori

401.01 pasiv Furnizor A

401.02 pasiv Furnizor B

401.99 pasiv Alţi furnizori

...

428 bifuncţional Alte creanţe şi datorii faţă de salariaţi

4281 activ Alte creanţe faţă de salariaţi

4282 pasiv Alte datorii faţă de salariaţi

...

411 activ Clienţi

...

4426 activ TVA deductibilă

4427 pasiv TVA colectată

...

601 activ Cheltuieli cu materii prime

601.01 activ Cheltuieli cu nisipul

601.02 activ Cheltuieli cu cimentul

601.03 activ Cheltuieli cu varul

601.09 activ Cheltuieli cu alte materii prime

...

Astfel, contul 301 - Materii prime este decompus pe patru analitice. Primele trei, 301.01,

301.02 şi 301.03, reprezintă cele mai importante materii prime pentru firmă, iar ultimul, 301.09 le

grupează pe toate celelalte. Mai sunt descompuse pe analitice conturile de furnizori (401) şi cheltuieli

cu materiile prime (600). Interesant că un cont sintetic de gradul I bifuncţional (428) poate avea un

"subordonat" de activ (4281), şi un altul de pasiv (4282) !

Din moment ce am devenit mai riguroşi, este evident că, practic, putem introduce în schemă

atributele SimbolCont, TipCont, DenumireCont, iar între ContDebitor şi SimbolCont, pe de o

parte, şi ContCreditor şi SimbolCont, pe de altă parte, avem de-a face cu o dependenţă de

incluziune. Graful dependenţelor ia forma din figura 3.36.

Figura 3.36. Dependenţele funcţionale şi de incluziune pentru BD CONTABILITATE

Page 76: BD1 2010 FotacheM Integral

76 Baze de date I

Relaţia nouă ce rezultă din graf este evidentă: PLAN_CONTURI {SimbolCont, TipCont,

DenumireCont}, iar cele două dependenţe de incluziune se vor materializa la implementarea bazei în

două restricţii referenţiale (vezi listingul de pe pagina web).

Proiecte vechi şi noi

Am recurs la un exemplu privind proiectele derulate într-o firmă cu ocazia paragrafului 5.4,

pentru a ilustra forma normalizată Boyce-Codd. Acum, însă, ne apropiem de realitatea dintr-o

companie, luând în calcul următoarele elemente:

un angajat, identificat prin Marcă este inclus în schema unui compartiment funcţional

(Contabilitate, Marketing etc.);

orice compartiment are un singur şef (MarcăŞef);

firma, datorită specificului activităţii sale, lucrează pe bază de proiecte; fiecare proiect are un

identificator (IdProiect), demarează la o anumită dată (DatăProiect) şi are un termen de

realizare (Termen);

un proiect este alcătuit din una sau mai multe activităţi (IdActivitate), o activitate având o

titulatură (DenActivit) şi o scurtă descriere (DescActivit);

orice angajat este capabil să desfăşoare una sau mai multe activităţi;

un angajat poate lucra la mai multe proiecte;

la un proiect sunt angajate una sau mai multe persoane;

activitate poate fi inclusă în unul sau mai multe proiecte;

în cadrul unui proiect, fiecare activitate începe la o anumită dată (DataÎnceput), se

finalizează la o altă dată (DataFinal) şi constă într-o serie de operaţii, responsabilităţi şi

rezultate (Detalii);

aceeaşi persoană poate desfăşura, în acelaşi proiect, una, două sau mai multe activităţi;

activitate într-un proiect poate fi desfăşurată de mai multe persoane.

Dependenţele corepunzătoare acestor cerinţe pot fi reprezentate sub formă de graf ca în figura 3.37.

În graful de mai sus există o serie de informaţii care nu pot fi furnizate de schema construită

pe baza decupării relaţiilor:

- care sunt activităţile pe care le poate desfăşura o persoană ?

- în ce proiecte a fost implicat un angajat ?

- care sunt persoanele implicate într-un anumit proiect ?

- care este activitatea, sau activităţile, desfăşurate de o anumită persoană într-un proiect dat ?

- care sunt persoanele care au desfăşurat o anumită activitate într-un anumit proiect ?

49

A fost o vreme când la această facultate picai sigur la examele de Contabilitate dacă afirmai că există conturi bifuncţionale. Acum puteţi pica şi pentru alte motive !

Page 77: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 77

Figura 3.37. Dependenţele bazei de date PROIECTE

O prim artificiu ar fi introducerea în schema bazei şi alte atribute care să "fixeze" toate

legăturile neprinse în dependenţe. Astfel, un angajat poate fi "legat" de o anumită activitate pe care o

poate desfăşura printr-un atribut nou, Competenţe, care să sugereze abilităţile şi experienţa

angajatului în privinţa activităţii cu pricina: (Marcă, IdActivitate) Competenţe. Pe de altă

parte, participarea unui angajat într-o activitate din cadrul unui proiect poate fi descrisă cu ajutorul a

trei atribute - Atribuţii, Probleme şi Rezultate - care indică ce sarcini a avut angajatul legat de

acea activitate din cadrul proiectului, ce probleme a întâmpinat şi care au fost rezultatele muncii sale.

Dependenţele sunt evidente: (Marcă, IdProiect, IdActivitate) Atribuţii, (Marcă,

IdProiect, IdActivitate) Probleme şi (Marcă, IdProiect, IdActivitate)

Rezultate. Noul graf s-ar prezenta ca în figura 3.38, noile atribute fiind scrise "aplecat" (italic).

Figura 3.38. Noul graf al dependenţelor BD PROIECTE

Relaţiile decupate din graf sunt următoarele: PERSONAL {Marcă, Numepren, Adresă,

Compartiment}, COMPARTIMENTE {Compartiment, MarcăŞef}, ACTIVITĂŢI {IdActivi-

tate, DenActivit, DescrActivit}, COMPETENŢE {Marcă, IdActivitate, Competenţe},

PROIECTE {IdProiect, TitluProiect, MarcăŞefProiect, DatăLansare, Termen}, ACTIVI-

TĂŢI_PROIECTE {IdProiect, IdActivitate, DataÎnceput, DataFinal, Detalii} şi PER-

SOANE_ACTIVITĂŢI_PROIECTE {IdProiect, IdActivitate, Marcă, Atribuţii, Probleme,

Rezultate}.

Alte frunze, dar aceleaşi conturi

Soluţiei la care am ajuns în acest paragraf pentru schema bazei de date CONTABILITATE i

se poate reproşa, printre altele, faptul că, uneori, la inserarea unei linii în PLAN_CONTURI trebuie

Page 78: BD1 2010 FotacheM Integral

78 Baze de date I

modificate alte linii din aceeaşi tabelă, ceea ce nu este posibil în toate SGBD-urile. Iar dacă la inserare

lucrurile mai pot funcţiona, declanşatorul de modificarea are şanse considerabile să se împotmolească.

Pentru a încerca să ajungem la următoarea soluţie, pornim de la dependenţele conţinute în

graful din figura 5.35. Dependenţele de incluziune dintre ContDebitor, pe de o parte, şi

ContCreditor, pe de altă parte, şi SimbolCont sunt discutabile. De fapt, restricţia este ca toate

conturile debitoare şi creditoare din orice înregistrare contabilă să fie elementare (frunze), adică să nu

fie descompuse pe analitice (sau sintetice de ordinul 2). Putem defini, deci, un atribut denumit ContE-

lementar care identifică (printr-o valoarea booleană TRUE sau şir de caractere "Da") un cont

"frunză". Putem vorbi de o "specializare" a conturilor, cele două dependenţe de incluziune iniţiale

fiind acum ContDebitor ContElementar, respectiv ContCreditor ContElementar, la care

se adaugă ContElementar SimbolCont. Spre deosebire de celelate două, ultima DI indică o

specializare. Noul graf al dependenţelor este cel din figura 3.39.

Figura 3.39. Trei dependenţe de incluziune, dintre care una semnifică o specializare

Decupând relaţiile din graf, obţinem: PLAN_CONTURI {SimbolCont, TipCont,

DenumireCont}, CONTURI_ELEMENTARE {ContElementar }, NOTE_CONTABILE {IdNo-

tăContabilă, Data, ExplicaţiiNotă}, OPERAŢIUNI {IdOperaţiune, IdNotăContabilă,

ExplicaţiiOp, NrConturiDebitoare, NrConturiCreditoare, UnContDebitor,

UnContCreditor} şi DETALII_OPERAŢIUNI {IdOperaţiune, ContDebitor, ContCreditor,

Suma}. Lucrurile sunt chiar interesante, deoarece acum restricţiile referenţiale se stabilesc astfel: (a)

între DETALII_OPERAŢIUNI.ContDebitor (copil) şi CONTURI_ELEMENTARE.ContElementar

(părinte); (b) între DETALII_OPERAŢIUNI.ContCreditor (copil) şi CONTURI_ELEMENTA-

RE.ContElementar (părinte); (c) între CONTURI_ELEMENTARE.ContElementar (copil) şi

PLAN_CONTURI.SimbolCont.

3.9 CAZ PRACTIC - CENTRU DE ÎNCHIRIERE CASETE VIDEO

Deşi nu întotdeauna la zi cu legislaţia pentru protecţia drepturilor de autor, centrele de

închiriere de casete video au constituit o afacere tentantă, mai ales în anii '90 şi, am putea spune, îşi are

încă clientela sa, deşi HBO-ul, în general, televiziunea prin cablu constituie un concurent foarte

puternic. Pentru a-l putea diferenţia de concurenţă, vrem ca baza de date a centrului pentru care lucrăm

să ofere şi date care să ajute la fidelizarea clienţilor:

Page 79: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 79

- informaţii despre filme: genul de film, distribuţie, realizare (scenariu, regie) etc.;

- premiile importante luate de filme: premiul, categoria, anul decernării;

- date despre împrumuturi, restituiri şi întârzieri;

- informaţii statistice, de genul: filmele cele mai cerute, clienţii cei mai fideli, clienţii cu cele mai mari

întârzieri la returnarea casetelor;

- date despre clienţi: zi de naştere/onomastică, genuri preferate şi, implicit, structura pe vârste şi

ocupaţii ale clienţilor;

- un sistem de evaluare (rating, în termeni elevaţi) a filmelor de către clienţi.

Ceea ce am discutat despre baza de date FILMOGRAFIE în capitolele anterioare ne este deci

de mare folos pentru cele ce urmează. În privinţa atributelor surogat, de la început este evident că am

avea nevoie de cel puţin trei:

IdFilm - pentru identificarea fără ambiguitate a unui film;

IdCasetă - un număr unic pentru diferenţierea între ele a casetelor;

IdÎmprumut - număr atribuit fiecărui împrumut de una sau mai multe casete, la un moment

oarecare.

Pentru identificarea clienţilor am putea să considerăm codul numeric personal (CNP-ul) ca

fiind mulţumitor. Din paragraful 3.6.3 ne-au rămas câteva dependenţe memorabile, unele funcţionale:

(1) IdFilm TitluOriginal

(2) IdFilm TitluRO

(3) IdFilm AnLans

(4) DenPremiu LocDecernare

(5) (IdFilm, Rol) Actor

(6) (DenPremiu, Categorie, IdFilm) AnPremiu.

(7) (DenPremiu, Categorie, IdFilm, Actor) AnPremiu

iar altele multi-valorice: IdFilm Producător | Regizor; IdFilm Producător |

Gen sau IdFilm Regizor | Gen. Adăugăm şi scenaristul, aşa că: IdFilm

Producător | Scenarist sau IdFilm Regizor | Gen.

Caseta este identificată de atributul IdCasetă, astfel că putem scrie:

(8) IdCasetă DataCumpărării

(9) IdCasetă ProducătorCasetă

(10) IdCasetă AnProdCasetă

(11) IdCasetă PreţCumpărare

Anticipând o cerere mai mare, un film poate fi compărat în mai multe exemplare, deci pe mai

multe casete: IdFilm / IdCasetă. Nici reciproca nu e prea valabilă, întrucât pe o casetă pot fi

adunate filme de mai mică dimensiune, scurt metraje (doar trei exemple: filmuleţele cu Stan şi Bran,

Chaplin şi Tom şi Jerry): IdCasetă / IdFilm.

Pentru a putea gestiona coerent toate fimele (scurt metraje sau nu) de pe o casetă, apelăm la un

atribut de genul FilmNr, adică numărul de ordine al filmului de pe o casetă (vă amintiţi de atributul

Linie pentru facturi ?). Aşa că:

(12) (IdCasetă, FilmNr) IdFilm

Page 80: BD1 2010 FotacheM Integral

80 Baze de date I

Am zice că problema raportului filme-casete este tranşată. Da' de unde ! Persoanele mai

simţitoare poate-şi amintesc de febra pre-telenovelistă declanşată de un film epopee - Pasărea Spin. Ei

bine, ca şi alte filme-maraton, acesta se întinde pe mai multe casete. Astfel încât, spre exemplu, pe o

casetă poate fi partea a patra a filmului XYZ. Din fericire, dependenţa de mai sus nu este compromisă,

însă am avea nevoie de un atribut adiţional - ParteFilm, iar dependenţa s-ar putea scrie:

(13) (IdCasetă, FilmNr) ParteFilm

Ba chiar putem fi siguri că, pe o casetă, casa producătoare poate să introducă ultima parte a

unui film plus un scurt-metraj de acelaşi regizor sau un medalion actoricesc etc.

Despre clienţi, numai de bine:

(14) CNPClient NumeClient

(15) CNPClient AdresaClient

(16) CNPClient TelefonClient

(17) CNPClient DataNaştereClient

(18) CNPClient NivelStudiiClient

Un client poate împrumuta una sau mai multe casete. Fiecare împrumut este identificat printr-

o cheie surogat - IdÎmprumut, aşa că:

(19) IdÎmprumut DataOraImprumut

(20) IdÎmprumut CNPClient

Deoarece simultan se pot împrumuta mai multe casete, IdÎmprumut / IdCasetă, ceea

ce e destul de neliniştitor, atâta vreme cât obiectivul central al aplicaţiei este de a gestiona

împrumuturile de casete. Fireşte, prima tentanţie este de a recurge la un atribut suplimentar, ca în cazul

liniilor din facturi, atribut căreia îi putem spune chiar NrCrtÎmpr (adică un soi de număr curent al

împrumutului, număr care indică a câta casetă din împrumut este cea curentă), aşa că, cu un pic de

cârpeală, problema s-ar rezolva:

(IdÎmprumut, NrCrtÎmpr) IdCasetă

Dacă adăugăm că restituirea casetelor nu este întotdeauna simultană, adică un client poate

împrumuta trei casete într-o sâmbătă dimineaţă şi să returneze una sâmbătă seara şi celelalte două

duminică, se poate scrie, fie (IdÎmprumut, NrCrtÎmpr) DataOraRestituirii, fie

(IdÎmprumut, IdCasetă) DataOraRestituirii. Eu, unul, aş alege varianta din urmă.

Ei bine, dacă privim mai atent ultima dependenţă, observăm că, la o adică, aceasta ar face de

prisos atributul NrCrtÎmpr. Diferenţa dintre varianta folosirii atributului NrCrtÎmpr (varianta 1) şi

cea fără (a doua) este ilustrată în figura 3.40.

Figura 3.40. Două variante de dependenţe pentru împrumuturi şi restituiri

Page 81: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 81

Fiecare îşi are avantajele şi dezavantajele sale. Prima pare mai complicată şi, totodată, are un

uşor aer de artificialitate indus de folosirea NrCrtÎmpr. Cel mai important avantaj al său ţine de

faptul că preia foarte bine succesiunea temporală împrumut (prima DF cu sursa compusă) - restituire

(a doua). Consecinţa esenţială este că în schemă nu apar valori nule. Graful din dreapta figurii este

mult mai simplu. Din momentul împrumutului până în cel al restituirii valoarea atributului DataO-

raRestuirii ar fi NULLă. Astfel, pentru a afla în orice moment casetele aflate la clienţi, condiţia în

SQL ar fi DataOraRestuirii IS NULL. Tocmai adversarii folosirii valorilor nule ar dezaproba această a doua

soluţie. Noi, însă, nefiind duşmani (dar nici prieteni) ai nulităţii în bazele de date relaţionale, vom opta

pentru această a doua soluţie, chiar dacă lenea a cântărit mult în alegere. Aşadar:

(21) (IdÎmprumut, IdCasetă) DataOraRestituirii

Să adunăm tot ce-am discutat până acum într-un graf. Figura 3.41 este reprezentare destul de

bună a ansamblului de dependenţe funcţionale (1)-(21), plus cele multivaloare. Este drept, aglomeraţia

este cam mare, dar arta cere sacrificii.

Figura 3.41. Graful DF pentru BD Centru de închiriere - versiunea 1.0

Decupăm relaţiile din graf: PREMII_DENUMIRI {DenPremiu, LocDecernare}, FILME

{IdFilm, TitluOriginal, TitluRO, AnLans}, DISTRIBUŢIE {IdFilm, Rol, Actor}, PRE-

MII_FILME {IdFilm, DenPremiu, Categorie,AnPremiu}, PREMII_INTERPRETARE

{IdFilm, Actor , DenPremiu, Categorie, AnPremiu}, PRODUCĂTORI {IdFilm, Producă-

tor}, REGIZORI {IdFilm, Regizor}, SCENARIŞTI {IdFilm, Scenarist}, FILME_GENURI

{IdFilm, Gen}, CASETE {IdCasetă, DataCumpărării, ProducătorCasetă, AnProdCasetă,

PreţCumpărare}, CASETE_FILME {IdCasetă, FilmNr, IdFilm, ParteFilm}, CLIENŢI

{CNPClient, NumeClient, AdresaClient, TelefonClient, DataNaşteriiClient, Ni-

velStudiiClient}, ÎMPRUMUTURI {IdÎmpr, DataOraÎmpr, CNPClient}, şi CASETE_ÎM-

PRUMUTATE {IdÎmpr, IdCasetă, DataOraRestituirii}

Schema pare a răspunde necesarului informaţional pe care ni-l propusesem iniţial. Astfel,

putem afla: numărul de casete împrumutate de un client pe o anumită perioadă; ponderea comediilor

Page 82: BD1 2010 FotacheM Integral

82 Baze de date I

între filmele închiriate de un client, sau pe tot centrul; ce genuri de filme preferă tinerii între 30 şi 35

de ani cu studii medii etc. Să ne ocupăm însă de regulile scrise sau nescrise ale centrului:

- un client nu poate împrumuta mai mult de patru casete simultan !

- la fiecare 10 casete împrumutate, un client are dreptul la o casetă împrumutată gratuit;

- împrumutul este pentru 48 de ore; la remiterea casetei se calculează o penalizare de 75% din preţul

de închiriere al casetei pentru fiecare zi de întârziere; iată şi tarifele zilnice la închiriere:

50 000 lei pentru casetele produse în anul calendaristic curent şi cel precedent;

40 000 lei pentru casetele produse în urmă cu doi şi trei ani;

30 000 lei pentru restul.

- pierderea sau distrugerea unei casete antrenează o amendă care reprezintă dublul preţului casetei.

Pentru prima regulă se poate introduce un atribut redundant numit NrCaseteNerestituite,

atribut actualizabil la declanşatoarele de inserare, modificare şi ştergere în

CASETE_ÎMPRUMUTATE. Acest atribut ar depinde de CNPClient: CNPClient

NrCaseteNerestituite. Ba chiar am putea exagera şi mai mult, folosind două atribute depen-

dente de CNPClient, unul NrCaseteÎmprumutate şi altul NrCaseteRestituite. Paradoxal,

deşi ideea pare deplasată, există argumente în favoarea sa. Iar, întrucât a doua regulă spune că la

fiecare 10 casete împrumutate, un client are dreptul la o casetă împrumutată gratuit chiar ne convin

ambele atribute, aşa că, în dezaprobarea publicului, ne precipităm şi scriem:

(22) CNPClient NrCaseteÎmprumutate

(23) CNPClient NrCaseteRestituite

A doua regulă e centrului reprezintă o tentativă de a fideliza clienţii, mai ales pe cei depen-

denţi: la 10 casete închiriate, a 11-a este împrumutată gratuit. Noroc de atributul NrCaseteÎmpru-

mutate de mai sus, pentru că, în condiţiile în care acesta este actualizat corect, putem şti în orice

moment dacă se cuvine să acordăm gratuitatea sau nu. Pentru a cunoaşte, totuşi, în timp, câte casete au

fost împrumutate cu titlu gratuit, am putea recurge la un atribut nou, Gratuită, care să ia valorile

„D” (da) sau „N” şi să depindă funcţional astfel: (24) (IdÎmprumut, IdCasetă) Gratuită.

A treia regulă e ceva mai dură: împrumutul este pentru 48 de ore; la remiterea casetei se

calculează o penalizare de 75% din preţul casetei pentru fiecare zi de întârziere. Implementarea sa ar

presupune că, la "de-nulizarea" valorii unui atribut DataOraRestituirii să verificăm dacă

diferenţa CASETE_ÎMPRUMUTATE.DataOraRestituirii - ÎMPRUMUTURI.DataÎmpr este mai

mare de două zile (întrucât cele două atribute sunt de tip DATETIME, adică dată şi oră, diferenţa lor

furnizează, de obicei, numărul de zile dintre cele două momente). Dacă da, atunci calculăm penaliza-

rea după relaţia: Penalizare := 0.75 * (CASETE_ÎMPRUMUTATE.DataOraRestituirii -

ÎMPRUMUTURI.DataÎmpr -2). Este evidentă nevoia de atributul Penalizare care va depinde

funcţional astfel: (25) (IdÎmprumut, IdCasetă) Penalizare. Cu această ocazie, ne

putem propune să rezolvăm o problemă pe care trebuia s-o avem în vedere mai demult, şi anume Cât

trebuie să achite clientul la fiecare împrumut (închirierea a una, două, trei sau patra casete) ? Pentru

acest scop apelăm la atributul ValoareÎnchir (de la valoare închiriere): (26) IdÎmprumut

ValoareÎnchir.

Page 83: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 83

În fine, ultima regulă, potrivit căreia pierderea sau distrugerea unei casete antrenează o

amendă care reprezintă dublul preţului casetei, are o dublă implicaţie: pe de o parte, regretabilul

eveniment trebuie consemnat la (un soi de) restituire, iar atributul Penalizare va conţine acum

dublul contravalorii casetei; pe de altă parte, trebuie să existe cumva în baza de date o indicaţie

precum că respectiva caseta nu mai poate fi închiriată, adică este pierdută sau distrusă. Soluţia ştergerii

înregistrării corespunzătoare din tabela CASETE nu este una prea ingenioasă, întrucât ar trebui să

pierdem toţi "copiii" acestei înregistrări, deci inclusiv de câte ori şi cui a fost împrumutată, şi, astfel,

informaţiile statistice privind filmele, clienţii şi împrumuturile vor fi serios afectate. Cel mai nimerit

pare să recurgem la un atribut numit StareCasetă care să aibă o valoare implicită OK (sau nulă,

deşi nu suntem fani nullişti) şi, dacă este cazul, Pierdută, Distrusă, ba chiar, dacă tot îl avem, putem

consemna şi situaţiile în care caseta a fost scoasă din uz "de moarte bună" (Casată) sau se apropie de

această stare (Uzată): (27) IdCasetă StareCasetă

Nu este însă suficient să cunoaştem starea casetei, ci şi de la ce împrumut i se trage pierderea

sa distrugerea. Aşă că apelăm la un atribut special, StareLaRestituire, care indică halul în care un

client ne-a restituit caseta (să fim înţeleşi: e vorba de halul casetei, nu al clientului !) şi care depinde

funcţional astfel: (28) (IdÎmprumut, IdCasetă) StareLaRestituire.

Ca un alt artificiu propus, ne imaginăm că, în ton cu vremurile, clienţii vor putea să-şi

rezerve/comande casete pe web, iar centrul să aibă un serviciu de furnizare a casetelor la domiciliu (de

tipul pizza delivery) sau, în cel mai rău caz, datorită dimensiunii impresionante a centrului (dă,

Doamne !), vor fi amplasate trei-patru terminale într-un colţ prin care clientul poate să caute filmele

după actori, regizori, subiect etc. Tocmai pentru a evita execuţia unei interogări care să implice

tabelele CASETE, ÎMPRUMUTURI şi CASETE_ÎMPRUMUTATE prin care să se afle dacă o casetă

este disponibilă sau împrumutată la un moment dat, se poate recurge la un alt atribut redundant numit

Disponibilitate, actualizabil automat prin declanşatoarele tabelei CASETE_ÎMPRUMUTATE:

(29) IdCasetă Disponibilitate.

Apropo, ne propusesem şi un mic mecanism de rating al filmelor de către clienţi, deşi, din câte

am văzut pe site-urile româneşti dedicate vânzărilor (legale) de carte şi CD-uri, sistemul nu prea a

prins la noi. La modul cel mai simplu, putem alege o scală de punctare de la 0 la 5, de genul celei

practicate de Laurenţiu Brătan în revista 22, atributul Punctaj fiind "implicat" în dependenţa: (30)

(IdFilm, CNPClient) Punctaj.

Tot frământându-ne mintea cu mecanismul de căutare a informaţiilor despre filme, realizăm

că, la un moment dat, este posibil ca unii clienţi să dorească a viziona filme în care joacă actori

spanioli, sau filme regizate de cineaşti născuţi în perioada 1930-1940 sau site-ul/pagina Web dedicată

unei anumite personalităţi cinematografice. Putem vorbi de generalizare: actorii, regizorii şi scenariştii

sunt toţi cineaşti, ca să nu amintim numeroşi actori care sunt şi regizori, sau regizori ce sunt şi

producători şi scenarişti. Aşa că am putea introduce o serie de atribute pentru identificarea şi

caracterizarea oricărui actor/scenarist/producător/regizor: IdCineast, NumeCineast,

DataNaşterii, DataMorţii, Naţionalitate, PaginăWeb. Fără a schimba numele celor patru

atribute (ar fi fost mai nimerite titulaturile IdScenarist, IdProducător, IdRegizor şi

IdActor), reprezentăm cele patru dependenţe de incluziune Scenarist IdCineast,

Producător IdCineast, Regizor IdCineast, Actor IdCineast.

Page 84: BD1 2010 FotacheM Integral

84 Baze de date I

Punând cap la cap tot ceea ca am discutat despre legăturile semantice dintre atributele bazei de

date, obţinem reprezentarea sub formă de graf din figura 3.42. Pentru un plus de lizibilitate, am grupat

toate destinaţiile surselor IdFilm, IdCineast, IdCasetă şi CNPClient.

Figura 3.42. Graful DF pentru BD Centru de închiriere - versiunea 2.0

Pe baza grafului, schema finală a bazei de date este următoarea: PREMII_DENUMIRI

{DenPremiu, LocDecernare}, FILME {IdFilm, TitluOriginal, TitluRO, AnLans},

CINEAŞTI {IdCineast, NumeCineast, DataNaşterii, DataMorţii, Naţionalitate, Pagi-

năWeb}, DISTRIBUŢIE {IdFilm, Rol, Actor*}, PREMII_FILME {IdFilm, DenPremiu,

Categorie,AnPremiu}, PREMII_INTERPRETARE {IdFilm, Actor* , DenPremiu, Categorie,

AnPremiu}, PRODUCĂTORI {IdFilm, Producător*}, REGIZORI {IdFilm, Regizor*},

SCENARIŞTI {IdFilm, Scenarist*}, FILME_GENURI {IdFilm, Gen}, CASETE {IdCasetă,

DataCumpărării, ProducătorCasetă, AnProdCasetă, PreţCumpărare, StareCasetă,

Disponibilitate}, CASETE_FILME {IdCasetă, FilmNr, IdFilm, ParteFilm}, CLIENŢI

{CNPClient, NumeClient, AdresaClient, TelefonClient, DataNaşteriiClient,

NivelStudiiClient, NrCaseteÎmprumutate, NrCaseteRestituite}, ÎMPRUMUTURI

{IdÎmpr, DataOraÎmpr, CNPClient, ValoareÎnchir}, CASETE_ÎMPRUMUTATE {IdÎmpr,

IdCasetă, DataOraRestituirii, Gratuită, Penalizare, StareLaRestituire }, APRECI-

ERI_FILME {IdFilm, CNPClient, Punctaj}.

Atributele marcate cu asterisc sunt chei străine, părintele comun fiind câmpul IdCineast din

tabela CINEAŞTI.

Page 85: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 85

Capitolul 4

CREAREA ŞI POPULAREA TABELELOR UNEI UNEI BAZE DE DATE

Obiective:

I. Prezentarea principalelor module şi funcţionalităţi ale unui sistem de

gestiune a bazelor de date

II. Clarificarea tipologiei opţiunilor pentru Limbajele de Definire a

Datelor şi Limbajele de Manipulare a Datelor

III. Caracterizarea atribuţiilor administratorului bazei de date şi altor

categorii de utilizatori

IV. Conturarea principalor segmente ale pieţei SGBD-urilor

V. Enumerarea principalelor tipuri de date gestionabile prin SQL

VI. Prezentarea principalelor clauze SQL pentru declararea restricţiilor

unei baze de date relaţionale

VII. Exemplificarea comenzilor SQL de editare a conţinutului unei tabele:

INSERT, UPDATE, DELETE

Rezultate aşteptate:

A. Deprinderea principalelor clauze ale comenzii CREATE TABLE

pentru crearea tabelelor şi declararea restricţiilor

B. Cunoaşterea modului de editare a tabelelor dintr-o bază de date

relaţională

Page 86: BD1 2010 FotacheM Integral

86 Baze de date I

4.1 SISTEME DE GESTIUNE A BAZELOR DE DATE

Apărute în anii '60, Sistemele de Gestiune a Bazelor de Date (prescurtat SGBD-uri) reprezintă

un ansamblu de programe ce permit utilizatorilor (profesionişti şi neprofesionişti) să interacţioneze cu

o bază de date, în vederea creării, actualizării şi interogării acesteia. SGBD-ul este software-ul care

asigură şi supervizează: introducerea de informaţii în baza de date; actualizarea şi extragerea datelor

din bază; autorizarea şi controlul accesului la date; păstrarea independenţei dintre structura bazei şi

programe50

. Obiectivul esenţial al unui SGBD este furnizarea unui mediu eficient, adaptat utilizatorilor

care doresc să consulte sau să actualizeze informaţiile conţinute în bază. Bazele de date sunt concepute

pentru a prelucra un volum mare de informaţii. Gestiunea acestora impune nu numai o structurare

riguroasă a datelor, dar şi o raţionalizare a procedurilor de acces şi prelucrare.

Principalele funcţiuni ale unui SGBD vizează:

- descrierea ansamblului de date la nivel fizic şi conceptual;

- crearea (iniţializarea) şi exploatarea (consultarea şi actualizarea) bazei de date;

- controlul integrităţii bazei;

- confidenţialitatea informaţiilor conţinute în bază;

- accesul simultan al mai multor utilizatori la informaţii;

- securitatea în funcţionare.

- furnizarea unui set de comenzi şi instrucţiuni, necesare atât utilizatorilor pentru consultarea directă a

bazei, prin intermediul unui limbaj de manipulare, cât şi programatorilor, pentru redactarea

programelor de lucru cu baza de date;

- revizia şi restructurarea bazei;

- monitorizarea performanţelor.

Un SGBD prezintă, în general, următoarele module51

:

Gestionarul fişierelor, care se ocupă cu afectarea spaţiilor de memorie pe disc şi cu structurile fizice

de date care servesc la reprezentarea informaţiilor pe suport.

Gestionarul bazei de date face legătura datelor "fizice" din bază cu aplicaţiile-program de

consultare şi actualizare.

Procesorul de consultare "traduce" instrucţiunile limbajului de consultare în instrucţiuni elementare,

"inteligibile" pentru gestionarul bazei. Mai mult, acesta optimizează consultarea, pentru a obţine

rezultatele într-un timp cât mai scurt.

Modulele DML (Data Manipulation Language) realizează conversia instrucţiunilor limbajului de

manipulare a datelor (DML) inserate într-un program de aplicaţie, în proceduri curente ale limbajului-

gazdă, interacţionând cu procesorul de consultare în vederea producerii secvenţelor de cod adecvate.

Modulele Limbajului de Definire a Datelor - DDL (Data Definition Language) "traduc" (prin

compilare sau interpretare) şi execută instrucţiunile DDL, obţinându-se ansamblul de tabele ce

reprezintă metadatele stocate în dicţionarul de date.

Aceste cinci module interacţionează cu o serie de componente fizice ale bazei:

Fişierele de date reprezintă suportul propriu-zis al bazei.

50 [G. Dodescu s.a. 87], p. 511 51 [Korth&Silberschatz88], pp.17-18

Page 87: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 87

Dicţionarul de date înmagazinează informaţii relative la structura bazei, fiind solicitat în toate

operaţiunile de consultare şi actualizare.

Indecşi, într-un număr suficient pentru mărirea vitezei de acces la date.

Sintetic, structura unui sistem de lucru cu o bază de date este prezentată în figura 4.152.

Aplicaţii-program Apeluri-sistemConsultare

(interogare)Structura bazei

Utilizatori finali

curenţiProgramatori

Utilizatori finali

ocazionaliAdministratorul bazei

Compilatorul limbajului

de definire a datelorProcesorul de

consultare

Precompilatorul

limbajului de

manipulare a datelor

Coduri-obiect ale

aplicaţiilor-program

Gestionarul bazei de date

Sistemul de gestiune a bazei de date

Gestionarul fişierelor

Fişier de dateDicţionar

de

dateBaza de date

Figura 4.1 Structura unui sistem de lucru cu o bază de date

Pentru a-şi îndeplini funcţiunile, un SGBD pune la dispoziţia utilizatorilor săi două tipuri de

comenzi/instrucţiuni. Primul modul este destinat lucrului cu schema bazei de date şi, de aceea, în

literatura de profil se foloseşte titulatura DDL (Data Definition Language) – limbaj de definire a

datelor. Al doilea priveşte conţinutul bazei de date, adică inserarea, modificarea şi ştergerea datelor,

precum şi căutarea datelor în bază (cu sau fără prelucrarea lor), motiv pentru care comenzile/instruc-

ţiunile de acest tip sunt referite ca opţiuni DML (Data Manipulation Language) – limbaj de manipulare

a datelor. Multe lucrări desprind din categoria DDL o a treia categorie denumită DCL (Data Control

Language) – limbaj pentru controlul datelor, care priveşte gestiunea utilizatorilor/grupurilor de

utilizatori şi drepturile de acces ale acestora la anumite obiecte ale bazei.

Limbaje de definire a datelor

Arhitectura unei baze de date este specificată printr-o serie de definiţii redactate sub formă de

instrucţiuni scrise în DDL. Execuţia acestor definiţii se materializează într-un ansamblu de tabele care

sunt memorate într-un fişier special, denumit dicţionar (sau repertoar) de date. Un dicţionar de date

conţine deci metadate, adică date relative la alte date, fiind consultat înaintea oricărei citiri sau

modificări a datelor din bază (dicţionarul de date a fost explicat chiar în primul capitol).

Principale funcţiuni ale DDL sunt:

- Descrierea logică a bazei de date şi sub-schemelor proprii fiecărui grup de utilizatori.

52 Preluare din [Korth&Silberschatz88], p.19

Page 88: BD1 2010 FotacheM Integral

88 Baze de date I

- Identificarea schemei, sub-schemelor şi diverselor agregări de date.

- Specificarea fişierelor de date şi a legăturilor logice dintre acestea. Pe baza acestor specificaţii se

poate realiza accesul la date chiar şi în condiţiile co-existenţei mai multor modele de organizare într-o

aceeaşi BD.

- Definirea restricţiilor semantice la care sunt supuse datele. Acestea reprezintă ansamblul valorilor

permise ale fiecărei date, eventual formula de calcul a unei date pe baza valorilor altor date.

- Respectarea acestor restricţii asigură coerenţa bazei.

- Definirea cheilor de acces rapid şi a cheilor confidenţiale (parolelor).

- Definirea metodelor de exploatare a fişierelor ce vor fi utilizate în aplicaţii pentru selectarea

înregistrărilor.

- Definirea procedurilor speciale de criptare, în vederea generării cheilor de acces.

- Definirea modului de indexare sau de localizare a entităţilor.

- Determinarea tipului unei date, în sensul de dată de bază sau derivată (calculată printr-o expresie, pe

baza valorilor altor date).

Dată fiind complexitatea structurilor de memorare şi metodelor de acces la acestea, la nivel

elementar fişierele de date şi dicţionarul de date sunt accesibile numai specialiştilor. În schimb, pentru

descrierea schemei BD, marea majoritate a limbajelor de interogare prezintă comenzi adecvate, ce pot

fi utilizate şi de ne-informaticieni.

Limbaje de manipulare a datelor

Prin manipularea datelor se înţelege efectuarea uneia dintre următoarele operaţiuni:

extragerea unor date din bază (consultare);

scrierea de noi date în bază (adăugare);

ştergerea datelor perimate sau eronate (uneori chiar şi a celor corecte);

modificarea valorii unor date.

Comenzile DML sunt utilizate pentru a prelucra datele în funcţie de structura lor. La nivel

fizic, prin DML se vizează identificarea şi implementarea unor algoritmi performanţi de acces la date,

în timp ce la nivel extern DML trebuie să faciliteze dialogul utilizatorului cu baza în vederea obţinerii

informaţiilor dorite. În mod curent, termenul consultare sau interogare desemnează acţiunea de

căutare şi identificare a datelor necesare, dintre cele aflate în bază. Ansamblul instrucţiunilor DML

pentru căutare şi identificarea datelor constituie limbajul de consultare.

Gestionarul bazei

O bază de date consumă un volum considerabil de memorie, astfel încât poate fi stocată

numai pe disc, memoria internă (RAM) a calculatorului fiind insuficientă (şi volatilă). Programele de

exploatare fac frecvent transferuri de date între memoria internă şi disc. Deoarece viteza de transfer

este mult mai mică, prin comparaţie cu viteza de lucru a procesorului, găsirea unor soluţii, la nivel

fizic, pentru transferul rapid al datelor prezintă o importanţă deosebită.

Pe de altă parte, obiectivul esenţial al bazelor de date este de a facilita exploatarea datelor, în

vederea obţinerii de către utilizatori a informaţiilor necesare. Acest obiectiv priveşte nivelul extern,

utilizatorul nefiind realmente interesat de modul în care informaţia se găseşte fizic pe suportul de

înregistrare.

Page 89: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 89

Rezolvarea acestor probleme cade sub incidenţa gestionarului bazei de date, care este un

modul de programe ce realizează interfaţa dintre datele interne (de pe suport) conţinute în bază şi

programele (sau comenzile) de consultare şi actualizare; principalele sale sarcini pot fi grupate astfel:

Interacţiunea cu gestionarul de fişiere. Datele brute sunt stocate pe disc prin intermediul sistemului

de gestiune a fişierelor, care este o componentă a sistemului de operare al calculatorului. Gestionarul

BD traduce instrucţiunile diverselor DML în instrucţiuni-sistem, la nivel elementar, fiind "responsabil"

şi de buna desfăşurare a operaţiilor de scriere/citire a datelor în/din bază.

Validitatea (corectitudinea) datelor. Datele stocate trebuie să satisfacă anumite restricţii de

integritate, specificate de administratorul bazei. După implementarea mecanismului de integritate,

gestionarul verifică dacă toate actualizările se derulează cu respectarea restricţiilor.

Securitatea datelor. Accesul la date trebuie să fie selectiv, gestionarul bazei fiind cel care asigură

respectarea drepturilor de acces ale fiecărui utilizator.

Salvare şi restaurare. Şi sistemele informatice sunt supuse accidentelor: distrugerea suprafeţei

magnetice, întreruperi în furnizarea energiei electrice, erori ale programelor etc. sunt inerente chiar şi

în ţări super-dezvoltate. În multe cazuri, datelor ce erau în curs de prelucrare în momentul accidentului

se alterează sau se pierd în totalitate. Gestionarul are dificila misiune de a detecta la timp avariile şi de

a restaura datele pierdute în forma şi conţinutul dinaintea accidentului. Aceasta se realizează prin

intermediul unor programe speciale de salvare-restaurare.

Prelucrări simultane (concurente). Întrucât în acelaşi timp pot lucra cu baza doi sau mulţi utilizatori,

trebuie asigurată coerenţa datelor prin afectarea unor nivele de prioritate la nivel de operaţii şi

utilizatori.

Administratorul bazei de date

Din cele prezentate până acum, reiese clar că un sistem de gestiune a bazei de date reprezintă

un ansamblu de programe ce asigură controlul complet al datelor, dar şi al aplicaţiilor care le

exploatează. Administratorul unei baze de date este persoana responsabilă de sistem în ansamblul său.

Rolul acestuia este determinant în câteva activităţi foarte importante.

Definirea arhitecturii bazei de date se realizează prin scrierea definiţiilor care vor fi transformate de

compilatorul DDL într-un ansamblu de tabele stocate permanent în dicţionarul de date.

Definirea modalităţilor în care va fi structurată memoria externă şi a metodelor de acces la date.

Acestea devin operaţionale prin redactarea unor specificaţii scrise ca instrucţiuni ale limbajului de

definire (descriere) a datelor.

Modificarea arhitecturii şi organizării fizice a bazei de date poate fi realizată prin intermediul

instrucţiunilor DDL, obţinându-se astfel actualizările corespondente ale dicţionarului de date.

Autorizarea accesului la date se acordă fiecărui utilizator al bazei de date, administratorul fiind cel

care decide asupra datelor ce pot fi consultate şi actualizate de fiecare utilizator sau grup de utilizatori.

Specificarea restricţiilor de integritate. Aceste restricţii sunt stocate pe disc şi consultate de

gestionarul bazei la fiecare actualizare.

În plus, administratorul bazei de date este cel care: asigură legătura cu utilizatorii; defineşte

procedurile de verificare a drepturilor de acces şi a procedurilor de validare a integrităţii datelor;

defineşte strategia de salvare (înregistrarea copiilor de siguranţă)/restaurare a bazei; monitorizează

performanţele bazei şi o adaptează la modificările ulterioare ale sistemului informaţional.

Page 90: BD1 2010 FotacheM Integral

90 Baze de date I

Utilizatorii bazelor de date

În ciuda complexităţii unui SGBD, tipologia utilizatorilor ce pot utiliza bazele de date este

extrem de generoasă. Cert este că există categorii profesionale (cum ar fi administratorii de baze de

date) care îşi câştigă existenţa exclusiv din „mânuirea” SGBD-urilor, aşa cum există utilizatori (cum ar

fi personalul de la ghişeele băncilor) care nu au nici cel mai mic habar despre baza de date în care îşi

stochează datele şi îşi extrag informaţiile.

Cu riscul de a simplifica lucrurile excesiv, ne putem opri numai la patru categorii de utilizatori

ce pot interacţiona cu sistemul:

- Administratorii BD. După cum am văzut în paragraful precedent, un administrator este un fel de il

capo di tutti capi pentru o bază de date. Trebuie să fie în egală măsură profesionist şi de caracter,

deoarece acces la cele mai intime informaţii stocate în bază.

- Programatorii (dezvoltatorii) de aplicaţii. Sunt informaticienii care interacţionează cu baza de date

prin intermediul instrucţiunilor DML integrate în programe scrise într-un limbaj-gazdă (Cobol, Pascal,

C, Visual Basic, Java etc.) sau în limbajul SGBD-lui respectiv.

- Utilizatori ocazionali, dar cunoscători. Aceştia sunt persoane care interacţionează cu sistemul, nu

prin intermediul programelor de aplicaţii, ci printr-un limbaj de consultare specific bazei (SQL).

Cunoscând porţiuni din schema bazei (fără a avea drepturi de administrare), ei pot lansa interogări prin

care pot obţine ad-hoc informaţii punctuale necesare.

- Utilizatorii curenţi neprofesionişti (neinformaticieni) sunt persoane care efectuează operaţii de rutină

asupra bazei de date, utilizând aplicaţiile disponibile, fără a cunoaşte schema bazei şi fără a pricepe

prea multe din tehnologia bazelor de date (aşa cum sunteţi dvs. acum !).

4.2 PIAŢA ACTUALĂ A SGBD-urilor

Deşi IBM a fost prima care a iniţial un proiect destinat elaborării unui SGBD relaţional

(System/R, începând cu 1974), prima firmă care a lansat primul SGBDR comercial a fost Relational

Software Inc., astăzi Oracle. Infiinţarea firmei a avut loc în 1977, iar lansarea produsului în 1979.

Impunerea pe piaţă a SGBD-urilor grefate pe modelul relaţional a fost mult mai dificilă decât s-ar

putea înţelege astăzi. Abia în deceniul 9, datorită vitezei ameliorate, securităţii sporite şi mai ales

productivităţii dezvoltatorilor de aplicaţii, modelul relaţionat a câştigat supremaţia. În ţara noastră cel

mai utilizat SGBD al deceniului 9 a fost FoxPro, deşi piaţa sa mondială se diminuase încă de la

mijlocul anilor `90.

Există o largă tipologie a SGBDR-urilor, aşa încât orice categorisire îşi are riscul său. Cele

mai accesibile şi, implicit, cele mai utilizate sunt SGBD-urile dedicate iniţial uzului individual:

Access, Paradox, Visual FoxPro. Astăzi, multe dintre acestea au caracteristici profesionale şi pot fi

folosite la dezvoltarea aplicaţiilor pentru accesul simultan a zeci de utilizatori.

Pentru aplicaţiile complexe din bănci, corporaţii, organizaţii şi instituţii de mari dimensiuni s-

au impus SGBDR-urile de "categoria grea": Oracle, DB2 (IBM), Informix (IBM), Sysbase, SQL

Server (Microsoft). Acestea sunt mult mai robuste, mult mai fiabile, dar şi costisitoare.

În ultimul timp şi-au făcut apariţia, ca alternative la marii coloşi, aşa zisele Free-DBMS-uri,

precum PostgreSQL, MySQL, mSQL etc. Acestea rulează, de obicei, pe sisteme de operare de tip

Linux (foarte ieftine) şi se întrevăd ca adversari serioşi ai marilor producători. Se pare că cel mai mare

Page 91: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 91

segment de piaţă din categoria SGBD-urilor Open-Source îl are MySQL, deşi în materie de

funcţionalităţi SQL şi proceduri stocate, acest produs a fost, până de curând, foarte slab.

4.3 CREAREA TABELELOR IN LIMBAJUL SQL

Până în acest moment al cursului, discuţiile au fost strict teoretice, urmărind familiarizarea cu

noţiunile fundamentale ale bazelor de date. Din acest paragraf (nu chiar imediat, ci câteva pagini mai

încolo), vom trece şi la chestiuni aplicative, ce pot fi lansate în execuţia de la consola unui SGBD. Şi

tot din acest paragraf, vom începe să gravităm în jurul celui mai important limbaj din lumea bazelor de

date, şi anume SQL.

4.3.1. Prezentare generală a limbajului SQL (partea I)

SQL (de la Structured Query Language) a devenit, de o bună bucată de vreme, limbajul-cheie

în lumea bazelor de date. Nu există astăzi lucrări sau publicaţii în domeniul SGBD-urilor care să nu

prezinte mecanismul de lucru al acestui limbaj sau ultimele produse/tendinţe din lumea SQL. Impactul

său este profund. Devenit un soi de esperando al limbajelor pentru baze de date, SQL are toate şansele

de a nu cădea în desuetitudinea esperando-ului propriu-zis, aceasta deoarece a fost altoit pe toate

tipurile de SGBD-uri, de la cele dedicate microcalculatoarelor (PC), la cele care operează în medii

distribuite/eterogene/client-server. SQL este, pe de o parte, unul dintre "responsabilii" noii aproprieri a

non-informaticianului de datele sale, iar, pe de altă parte, pentru profesionişti, reprezintă nucleul

dezvoltării aplicaţiilor ce utilizează bazele de date.

Deşi este referit, în primul rând, ca un limbaj de interogare, SQL este mult mai mult decât un

instrument de consultare a bazelor de date, deoarece permite, în egală măsură:

Definirea datelor

Consultarea BD

Manipularea datelor din bază

Controlul accesului

Partajarea bazei între mai mulţi utilizatori ai acesteia

Menţinerea integrităţii BD.

Astfel că principale comenzi SQL pot fi rezumate ca în tabelul 4.1.

Tabel 4.1 Cele mai importante comenzi SQL

Comandă Scop

Pentru manipularea datelor

SELECT Extragerea datelor din bază

INSERT Adăugarea de noi linii într-o tabelă

DELETE Ştegerea de linii dintr-o tabelă

UPDATE Modificarea valorilor unor atribute

Pentru definirea bazei de date

CREATE TABLE Adăugarea unei noi tabele în baza de date

DROP TABLE Ştergerea unei tabele din bază

ALTER TABLE Modificarea structurii unei tabele

CREATE VIEW Crearea unei tabele virtuale

DROP VIEW Ştergerea unei tabele virtuale

Page 92: BD1 2010 FotacheM Integral

92 Baze de date I

Pentru controlul accesului la BD

GRANT Acordarea unor drepturi pentru utilizatori

REVOKE Revocarea unor drepturi pentru utilizatori

Pentru controlul tranzacţiilor

COMMIT Marchează sfârşitul unei tranzacţii

ROLLBACK Abandonează tranzacţia în curs

4.3.2. Tipuri de date SQL

La crearea efectivă a oricărei tabele, pentru fiecare atribut, pe lângă nume şi eventuale

restricţii trebuie obligatoriu declarat tipul acestuia – dacă este un număr, un şir de caracterre, o dată

calendaristică etc. Deşi există unele diferenţe semnificative între dialectele SQL, următoarele categorii

de tipuri de date sunt cvasi-prezente.

- SMALLINT: întregi - scurte (4 poziţii, reprezentate pe 16 biţi),

- INTEGER sau INT: întregi (9 poziţii, 32 biţi),

- NUMERIC(p,s) sau DECIMAL(p,s) sau DEC(p,s) - reale, cu un total de p poziţii, din care s la partea

fracţionară ,

- FLOAT: reale, virgulă mobilă (20 poziţii ptr. mantisă),

- REAL: real, virgulă mobilă (cu precizie mai mică decât FLOAT, dar la nivelul de intrare este

identic),

- DOUBLE PRECISION: reale, virgulă mobilă, dublă precizie (30 de poziţii ptr. mantisă),

- CHAR(n) sau CHARACTER(n): şir de caractere de lungime n (max. 240),

- VARCHAR(n) sau CHAR VARYING(n) sau CHARACTER VARYING(n): şir de caractere de

lungime variabilă (max. 254),

- DATE: dată calendaristică,

- TIME: ora etc.,

- TIMESTAMP: an, lună, zi, ora, minutul, secunda, plus o fracţiune zecimală dintr-o secundă.

Joe Celko tratează riguros fiecare categorie de dată, aşa cum este prezentă în SQL-9253

. În

ceea ce priveşte datele temporale, el operează o delimitare între:

evenimente, pentru care există tipurile DATE, TIME, TIMESTAMP,

intervale – perioade de timp dintre două evenimente (momente) - tipul INTERVAL (YEAR,

MONTH, DAY, HOUR, MINUTE, SECOND) şi

perioade – sunt intervale cu un moment fix de început, pentru care este necesară o combinaţie

de două câmpuri, fie TIMESTAMP - TIMESTAMP, fie TIMESTAMP - INTERVAL.

4.3.3. Crearea tabelelor: CREATE TABLE

Crearea unei baze de date are o componentă tehnică pronunţată, mai ales în cazul serverelor de

baze de date: Oracle, DB/2, SQL Server, Informix, PostgreSQL etc. Chiar dacă există instrumente de

admistrare care uşurează considerabil această activitate, crearea unei baze de date necesită cunoştinţe

de administrare, strict legate de produsul software de gestiune a bazei.

În cele ce urmează, vom eluda elementele tehnice/fizice (spaţiile-tabelă, segmentele de

rollback, fişierele de date, procesele sistem etc.), lăsându-le în seama administratorilor BD; vom

53

[Celko99]

Page 93: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 93

considera baza de date creată din punct de vedere fizic, astfel încât ne interesează numai crearea

tabelelor, modificarea structurii lor şi declararea restricţiilor.

Comanda SQL utilizată pentru crearea unei tabele este CREATE TABLE. Precizăm că este

vorba de crearea structurii tabelei. Popularea cu înregistrări şi actualizarea ulterioară se realizează prin

alte comenzi: INSERT, UPDATE, DELETE. Tabelele sunt cele din baza de date VÎNZĂRI. Pentru

crearea unei tabele şi declararea atributelor acesteia, fără nici o referire la restricţii, comanda CREATE

TABLE are, în cazul tabelei JUDETE, următoarea formă:

CREATE TABLE JUDETE

(Jud CHAR(2),

Judet VARCHAR(25),

Regiune VARCHAR (15))

Întrucât atributele acestei tabele sunt exclusiv de tip şir de caractere, s-au folosit opţiunile CHAR şi

VARCHAR. CHAR este preferat pentru atributul Jud, deoarece indicativul auto al judeţului este

format exclusiv din două litere (cu excepţia Bucureştiului). Întrucât denumirile judeţului şi regiunii au

lungime variabilă, pentru aceste atribute a fost preferat tipul VARCHAR. Pentru ilustrarea şi altor

două tipuri de date, prezentăm comanda de crearea a tabelei FACTURI:

CREATE TABLE FACTURI ( NrFact NUMERIC(8), DataFact DATE, CodCl DECIMAL(6), Obs VARCHAR(50) )

NrFact şi CodCl sunt de tip numeric, în timp ce DataFact de tip dată calendaristică. Se mai cuvine

de adăugat că unele atribute pot fi iniţializate cu o valoare implicită la adăugarea unei linii în tabela

respectivă. Clauza este DEFAULT:

CREATE TABLE FACTURI ( NrFact NUMERIC(8), DataFact DATE DEFAULT CURRENT_DATE,

CodCl DECIMAL(6) DEFAULT 1001, Obs VARCHAR(50) )

Ca urmare a clauzei DEFAULT, în orice linie adăugată în tabela FACTURI, valoarea

implicită (dacă nu este specificată în comanda INSERT) a DataFact va fi data sistemului (data

curentă), iar CodCl se va iniţializa cu valoarea 1001.

4.3.4. Declararea restricţiilor

În SQL se pot declara toate tipurile de restricţii ale unei BDR: valori obligatorii (nenule),

unicitate, restricţii referenţiale şi restricţii utilizator.

Valori nenule

Unele atribute, obligatoriu cele din cheia primară, nu pot prezenta valori nule. Pentru aceasta,

la crearea tabelei şi declararea atributului se foloseşte opţiunea NOT NULL:

CREATE TABLE FACTURI (

NrFact NUMERIC(8) NOT NULL, DataFact DATE DEFAULT CURRENT_DATE NOT NULL,

CodCl DECIMAL(6) DEFAULT 1001 NOT NULL, Obs VARCHAR(50) )

Cheie primară/unicitate

Cheia primară a unei relaţii este definită prin clauza PRIMARY KEY, plasată fie imediat,

după atributul cheie, fie după descrierea ultimului atribut al tabelei. A doua variantă este întrebuinţată

cu precădere atunci când cheia primară a tabelei este compusă. Pentru atributele de tip cheie candidat

se poate folosi clauza UNIQUE care va asigura respectarea unicităţii valorilor. Iată câteva variante de

folosire a clauzelor PRIMARY KEY şi UNIQUE:

CREATE TABLE FACTURI (

Page 94: BD1 2010 FotacheM Integral

94 Baze de date I

NrFact NUMERIC(8) PRIMARY KEY, DataFact DATE DEFAULT CURRENT_DATE NOT NULL,

CodCl DECIMAL(6) DEFAULT 1001 NOT NULL, Obs VARCHAR(50) ) ;

CREATE TABLE JUDETE (

Jud CHAR(2) PRIMARY KEY, Judet VARCHAR(25) NOT NULL UNIQUE, Regiune VARCHAR (15) ) ;

CREATE TABLE LINIIFACT (

NrFact NUMERIC(8) NOT NULL, Linie SMALLINT NOT NULL, CodPr NUMERIC(6) NOT NULL,

Cantitate NUMERIC(10) NOT NULL, PretUnit NUMBER (12),

PRIMARY KEY (NrFact, Linie), UNIQUE (NrFact, CodPr) ) ;

Pentru tabela LINIIFACT cheia primară este combinaţia de atribute (NrFact, Linie); restricţia de

unicitate pentru cuplul (NrFact, CodPr) înseamnă că se interzice ca, pe o factură, un produs să apară

repetat.

Restricţii referenţiale

Declarea restricţiilor referenţiale se realizează utilizând clauza FOREIGN KEY. Astfel, pentru

stabilirea legăturii LINIIFACT-FACTURI comanda de creare are forma:

CREATE TABLE LINIIFACT (

NrFact NUMERIC(8) NOT NULL, Linie SMALLINT NOT NULL, CodPr NUMERIC(6) NOT NULL,

Cantitate NUMERIC(10) NOT NULL, PretUnit NUMBER (12),

PRIMARY KEY (NrFact, Linie),

UNIQUE (NrFact, CodPr),

FOREIGN KEY NrFact REFERENCES FACTURI(NrFact),

FOREIGN KEY CodPr REFERENCES PRODUSE(CodPr) )

Este drept, şi următoarea variantă este corectă:

CREATE TABLE LINIIFACT (

NrFact NUMERIC(8) NOT NULL REFERENCES FACTURI(NrFact),

Linie SMALLINT NOT NULL,

CodPr NUMERIC(6) NOT NULL REFERENCES PRODUSE(CodPr),

Cantitate NUMERIC(10) NOT NULL, PretUnit NUMBER (12),

PRIMARY KEY (NrFact, Linie), UNIQUE (NrFact, CodPr) )

În SQL92 se poate specifica şi modul în care va fi păstrată integritatea bazei de date la

ştergerea unei linii-părinte sau modificarea unei chei primare ce prezintă înregistrări-copil. Pentru a

interzice ştergerea unor facturi (înregistrări din FACTURI) pentru care există tupluri corespondente în

LINIIFACT şi, pe de altă parte, pentru ca la modificarea unui număr de factură (NrFact) în FACTURI

să se modifice automat, în cascadă, toate liniile-copil din LINIIFACT, forma comenzii se schimbă în:

CREATE TABLE LINIIFACT ( NrFact NUMERIC(8) NOT NULL, Linie SMALLINT NOT NULL,

CodPr NUMERIC(6) NOT NULL, Cantitate NUMERIC(10) NOT NULL, PretUnit NUMBER (12),

PRIMARY KEY (NrFact, Linie), UNIQUE (NrFact, CodPr),

FOREIGN KEY NrFact REFERENCES FACTURI(NrFact)

ON DELETE RESTRICT ON UPDATE CASCADE,

FOREIGN KEY CodPr REFERENCES PRODUSE(CodPr)

ON DELETE RESTRICT ON UPDATE CASCADE )

Restricţii-utilizator

În SGBD-urile actuale, restricţiile-utilizator, denumite şi restricţii de comportament, sunt

implementate sub forma regulilor de validare la nivel de câmp (field validation rule), la nivel de

înregistrare (record validation rule) sau, eventual, pot fi incluse în declanşatoare (triggere). Cu rezerva

că aceste reguli pot avea forme complexe şi întrebuinţa elemente avansate de SQL şi/sau extensiile

Page 95: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 95

procedurale ale SQL în mediul respectiv, prezentăm în continuare o regulă de validare pentru atributul

DataFact în tabela FACTURI şi o alta pentru atributul Linie în LINIIFACT.

CREATE TABLE FACTURI (

NrFact NUMERIC(8)

CONSTRAINT nn_facturi_nrfact NOT NULL

CONSTRAINT pk_facturi PRIMARY KEY,

DataFact DATE DEFAULT CURRENT_DATE

CONSTRAINT nn_datafact NOT NULL

CONSTRAINT ck_datafact CHECK (DataFact >= '01/08/2005' AND DataFact <= '31/12/2010'),

CodCl DECIMAL(6) DEFAULT 1001

CONSTRAINT nn_facturi_codcl NOT NULL

CONSTRAINT fk_facturi_clienti REFERENCES CLIENTI(CodCl)

ON DELETE RESTRICT ON UPDATE CASCADE,

Obs VARCHAR(50) ) ;

CREATE TABLE LINIIFACT (

NrFact NUMERIC(8) CONSTRAINT nn_liniifact_nrfact NOT NULL,

Linie SMALLINT CONSTRAINT nn_linie NOT NULL CONSTRAINT ck_linie CHECK (Linie > 0),

CodPr NUMERIC(6) CONSTRAINT nn_liniifact_codpr NOT NULL,

Cantitate NUMERIC(10) CONSTRAINT nn_cantitate NOT NULL,

PretUnit NUMERIC (12) CONSTRAINT nn_pretunit NOT NULL,

CONSTRAINT pk_liniifact PRIMARY KEY (NrFact, Linie),

CONSTRAINT un_liniifact UNIQUE (NrFact, CodPr),

CONSTRAINT fk_liniifact_facturi FOREIGN KEY NrFact REFERENCES FACTURI(NrFact)

ON DELETE RESTRICT ON UPDATE CASCADE,

CONSTRAINT fk_liniifact_produse FOREIGN KEY CodPr REFERENCES PRODUSE(CodPr)

ON DELETE RESTRICT ON UPDATE CASCADE )

Ca urmare a clauzei CHECK, data unei facturi nu poate avea valori în afara intervalului 1

august 2005 – 31 decembrie 2010, iar atributul Linie trebuie să prezinte valori întregi mai mari ca 0.

Ca un alt element de noutate, fiecărei restricţii (cheie primară, unicitate, regulă la nivel de

atribut etc.) i s-a dat un nume, lucru util atunci când, la un moment dat (sălvari, restaurări, încarcarea

BD), vrem sa dezactivăm una sau mai multe dintre acestea. Pentru a uşura lucrul, se prefixează numele

fiecărei restricţii cu tipul său:

pk_ (PRIMARY KEY) pentru cheile primare

un_ (UNIQUE) pentru cheile alternative

nn_ (NOT NULL) pentru atributele obligatorii (ce nu pot avea valori nule)

ck_ (CHECK) pentru reguli de validare la nivel de atribut

fk_ (FOREIGN KEY) pentru cheile străine.

Pe portalul FEAA, la pagina disciplinei, este disponibil listingul cu toate comenzile pentru

crearea tabelelor din baza de date VÎNZĂRI.

4.3.5. Modificarea structurii unei tabele

Odată creată o tabelă, schema sa rămâne constantă. Dacă totuşi, ulterior, se doreşte introducerea,

modificarea sau ştergerea unor atribute, sau declararea/anularea unor restricţii, comanda necesată este ALTER

TABLE. Câteva exemple:

Page 96: BD1 2010 FotacheM Integral

96 Baze de date I

- Adăugarea atributului DataNast (data naşterii) în tabela PERSOANE se realizează astfel:

ALTER TABLE PERSOANE ADD DataNast DATE

- Ştergerea unui atribut: ALTER TABLE PERSOANE DROP COLUMN DataNast

- Modificarea tipului/lungimii unui atribut: ALTER TABLE PERSOANE MODIFY (Nume VARCHAR(21))

- Adăugarea/modificarea valorii implicite: ALTER TABLE PERSOANE MODIFY (Sex DEFAULT 'F')

- Anularea unei valori implicite: ALTER TABLE PERSOANE MODIFY (Sex DEFAULT NULL)

- Instituirea restricţiei de nenulitate: ALTER TABLE PERSOANE MODIFY (Sex NOT NULL)

- Invers, acceptarea valorilor NULL: ALTER TABLE PERSOANE MODIFY (Sex NULL)

Toate restricţiile: cheie primară - PRIMARY KEY, unicitate - UNIQUE, referenţială -

FOREIGN KEY, de comportament - CHECK pot fi declarate ulterior creării tabelei şi, bineînţeles,

anulate la un moment dat. Spre exemplu, formatul general al comenzii pentru dezactivarea cheii

primare este: ALTER TABLE PERSOANE DROP PRIMARY KEY. Pentru reinstituirea cheii primare a

tabelei PERSOANE comanda are forma: ALTER TABLE PERSOANE ADD PRIMARY KEY (CNP).

4.3.6. Ştergerea tabelelor

Orice tabelă creată poate fi ştearsă prin comanda DROP TABLE. Dacă, însă, tabela este ună

părinte (adică are copii), ştergerea trebuie făcută împreună cu toţi copii (DELETE CASCADE),

altminteri SGBD-ul se va împotrivi (va afişa un mesaj de eroare).

4.4 COMENZI PENTRU MODIFICAREA CONŢINUTULUI

SQL prezintă comenzi dedicate modificării conţinutului unei tabele, înţelegând prin aceasta

trei acţiuni prin care se actualizează baza:

a) adăugarea de noi linii la cele existente în tabelă,

b) ştergerea unor linii,

c) modificarea valorii unui atribut.

Comanda SQL de adăugare de noi linii este INSERT. Exemple:

INSERT INTO judete VALUES ('IS', 'Iasi', 'Moldova') ;

INSERT INTO coduri_postale VALUES ('700505', 'Iasi', 'IS') ;

INSERT INTO clienti VALUES (1001, 'Client 1 SRL', 'R1001', 'Tranzitiei, 13 bis', '700505', NULL) ;

INSERT INTO persoane VALUES ('CNP1', 'Ioan', 'Vasile', 'I.L.Caragiale, 22', 'B', '700505', '123456', '987654',

'094222222', NULL) ;

INSERT INTO persclienti VALUES ('CNP1', 1001, 'Director general');

INSERT INTO produse VALUES (1, 'Produs 1','buc', 'Tigari', .19) ;

INSERT INTO facturi (nrfact, datafact, codcl) VALUES (1111, TO_DATE('01/08/2005','DD/MM/YYYY'), 1001);

VALUES (1121, TO_DATE('07/08/2005','DD/MM/YYYY'), 1001);

INSERT INTO liniifact (nrfact, linie, codpr, cantitate, pretunit) VALUES (1111, 1, 1, 50, 1000) ;

INSERT INTO liniifact (nrfact, linie, codpr, cantitate, pretunit) VALUES (1111, 2, 2, 75, 1050) ;

INSERT INTO incasari VALUES (1234, TO_DATE('15/08/2005','DD/MM/YYYY'),

'OP', '111', TO_DATE('10/08/2005','DD/MM/YYYY')) ;

INSERT INTO incasfact VALUES (1234, 1111, 53996) ;

Page 97: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 97

Ordinea valorilor din clauza VALUES trebuie să fie identică cu cea declarată la crearea (sau

modificarea structurii) tabelelor. Modificarea ordinii este posibilă numai prin enumerarea, după

numele tabelei, a atributelor ce vor primi valorile specificate.

Pentru tabelele FACTURI şi LINIIFACT au fost incluse în clauza VALUES mai puţine valori

decât atributele tabelei. Este obligatorie, în aceste cazuri, precizarea atributelor care vor primi valorile.

Restul, adică cele nespecificate, vor avea, pe liniile respective, valori NULL. Funcţia TO_DATE

(valabilă în PostgreSQL şi Oracle) ajută la specificarea datelor calendaristice (DD/MM/YYY

înseamnă că două primele două poziţii desemnează ziua, următoarele două de după bara oblică luna,

iar ultimele patru anul).

Comanda SQL pentru ştergerea uneia sau mai multor linii dintr-o tabelă este DELETE, al

cărei formatul general este: DELETE FROM nume-tabelă WHERE predicat. Din tabelă vor fi şterse

toate liniile care îndeplinesc condiţia specificată în predicatul din clauza WHERE:.

- DELETE FROM FACTURI WHERE NrFact = 1122 elimină din tabela facturi toate liniile în care valoarea

atributului NrFact este egală cu 1122, altfel spus, din tabela FACTURI este ştearsă linia care se referă la factura

1122. Când pentru această factură există înregistrări copil în LINIIFACT sau INCASFACT, SGBD-ul ţine cont

de opţiunea declarată la crearea tabelei. Dacă s-a specificat (implicit sau explicit) ON DELETE RESTRICT,

ştergerea este interzisă. În cazul ON DELETE CASCADE sunt şterse, odată cu linia din FACTURI, toate liniile

copil din LINIIFACT şi INCASFACT.

- DELETE FROM JUDETE WHERE Regiune = „Moldova‟ elimină din baza de date toate judeţele din

Moldova (şterge toate liniile tabelei JUDETE în care valoarea atributului Regiune este Moldova).

Pentru a modifica valoarea unuia sau mai multor atribute pe una sau mai multe linii dintr-o

tabelă se foloseşte comanda UPDATE cu formatul general (simplificat): UPDATE tabelă SET atribut1

= expresie1 [, atribut2= expresie2 ….] WHERE predicat. Modificarea se va produce pe toate liniile

tabelei care îndeplinesc condiţia formulată prin predicat. Exemple:

- Noul număr de telefon al clientului ce are codul 1001 este 0232-313131: UPDATE CLIENTI SET

Telefon = „0232-313131‟ WHERE CodCl = 1001

- În cadrul unei noi relaxări fiscale, se decide creşterea procentului TVA de la 19% la 22% pentru

toate produsele. Atributul modificat este ProcTVA din tabela PRODUSE, pe toate liniile: UPDATE

PRODUSE SET ProcTVA = .22

Pe portalalul FEAA, la pagina disciplinei, este disponibil listingul cu toate comenzile pentru

popularea cu înregistrări a tabelelor din baza de date VÎNZĂRI.

Page 98: BD1 2010 FotacheM Integral

98 Baze de date I

Capitolul 5

ALGEBRA RELAŢIONALĂ

Obiective:

I. Definirea unui limbaj de interogare

II. Explicarea operatorilor ansamblişti (reuniune, intersecţie, diferenţă,

produs cartezia) şi relaţionali (selecţie, proiecţie, joncţiune, diviziune)

III. Înlănţuirea operatorilor pentru a obţine soluţii la probleme de

interogare a bazei de date

Rezultate aşteptate:

Deprinderea redactării de interogări în algebra relaţională

Page 99: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 99

În capitolul 2 am văzut că modelul relaţional formulat de Codd are la bază trei elemente:

structuri, operaţii şi reguli de integritate. Pentru exprimarea operaţiilor aplicabile structurilor de date

prezentul capitol prezentă algebra relaţională, limbaj teoretic bazat pe teoria ansamblurilor (seturilor).

Chiar dacă are cusurul de a fi teoretic, algebra relaţională poate constitui un bun punct de plecare în

înţelegerea chestiunilor esenţiale ale celui mai important limbaj dedicat bazelor de date – SQL.

5.1 GENERALITĂŢI DESPRE LIMBAJELE DE INTEROGARE

Odată cu apariţia bazelor de date, s-a încercat elaborarea fudamentelor teoretice ale unor

limbaje care să prelucreze conţinutul acestora. Pentru modelul relaţional, au fost propuse atât limbaje

ansambliste (Alpha, algebra relaţională, SQL), cât şi predicative (calcul relaţional, QBE), atât teoretice

(algebra relaţională, calcul relaţional), cât şi aplicative (SQL, QBE, Que). Noi vom zăbovi doar asupra

câtorva elemente care ar trebui să ajute la înţelegerea modalităţilor în care sunt extrase informaţiile

dintr-o bază de date relaţională.

Aşa cum algebra “obişnuită” aplică operatori, cum fi adunarea, scăderea etc., asupra unor

numere, rezultatul operatorilor fiind tot un număr, trebuie spus că:

- operatorii relaţionali se aplică relaţiilor luate în întregime, adică tuturor tuplurilor care alcătuiesc

relaţiile respective;

- rezultatul fiecărui operator (rezultatul consultării) este o nouă relaţie ce poate servi ca argument într-

o altă consultare s.a.m.d.;

- logica operatorilor se bazează pe valorile atributelor, ceea ce constituie dealtminteri suportul

singurului mod de acces în BD. Întrucât consultările mono şi multi-relaţii sunt efectuate exclusiv prin

compararea valorilor atributelor (definite pe domenii compatibile), accesul total independent de limbaj

este asigurat.

Limbajul algebric relaţional cuprinde două tipuri de operatori: ansamblişti - REUNIUNE,

INTERSECŢIE, DIFERENŢĂ, PRODUS CARTEZIAN - şi relaţionali - SELECŢIE, PROIECŢIE,

JONCŢIUNE şi DIVIZIUNE. O altă clasificare distinge operatorii fundamentali, ireductibili

(reuniunea, diferenţa, produsul cartezian, selecţia, proiecţia) de cei derivaţi, a căror funcţionalitate

poate fi realizată prin combinarea operatorilor fundamentali (intersecţia, joncţiunea, diviziunea).

Pentru cele ce urmează se notează cu: t sau r, un tuplu al unei relaţii (linie a unei tabele) şi t(A), un

sub-tuplu al relaţiei R, relativ la atributul A (valoarea atributului A în linia t).

Algebra, ca şi calculul relaţional, serveşte ca punct de referinţă în caracterizarea unui limbaj ca

fiind complet sau incomplet, din punct de vedere relaţional. Dacă un limbaj permite exprimarea tuturor

operatorilor enumeraţi mai sus şi oferă cel puţin facilităţile algebrei relaţionale, se spune că acesta este

un limbaj relaţional complet54

.

5.2 OPERATORII ANSAMBLIŞTI

Trei dintre operatorii ansamblişti - reuniunea (" "), intersecţia (" ") şi diferenţa (“-“) - pot

opera numai cu două relaţii uni-compatibile. Fie R1 ( A1, A2, ..., An,) şi R2 (B1, B2, ..., Bm) două

54 [Saleh94], p.35

Page 100: BD1 2010 FotacheM Integral

100 Baze de date I

relaţii. Se spune despre R1 şi R2 că sunt unicompatibile dacă: (a) n = m; (b) i {1,2, ..., n}, Ai şi Bi

sunt de acelaşi tip sintactic (aceasta nu înseamnă că trebuie să prezinte, neapărat, domenii identice de

definire). Relaţiile R1 şi R2 din figura 5.1 sunt unicompatibile deoarece: (a) ambele au acelaşi număr

de atribute; (b) atributele A, B, C din R1 (le putem nota şi R1.A, R1.B, R1.C) corespund sintactic

(sunt de acelaşi tip) atributelor C, D şi E din R2 (R2.C, R2.D, R2.E).

Figura 5.1. Două relaţii unicompatibile

Reuniunea

Reuniunea a două relaţii unicompatibile, R1 şi R2 , este definită astfel: R1 R2 = { tuplu t |

t R1 sau t R2 }. Se notează: R3 R1 R2. Conţinutul tabelei reuniune R3 este prezentat

în figura 5.2. Primele trei tupluri din rezultat sunt preluate din R1, iar ultimele două din R2. R3 are

numai cinci tupluri deoarece un tuplu este comun tabelelor R1 şi R2. Algebra relaţională elimină

automat dublurile (tuplurile identice), astfel încât restricţia de unicitate este asigurată după orice

operaţie.

Figura 5.2. Reuniunea a două relaţii

Există suficiente situaţii informaţionale care fac uz de reuniunea a două tabele. Să luăm două

exemple:

A. Tabelele ce reflectă tranzacţii economice pot fi descompuse (sparte) în funcţie de anul (luna sau

cincinalul, deceniul) la care se referă; dacă ne raportăm la baza noastră de date, tabela LINIIFACT

poate fi ruptă în alte două, LF_2000_2001 care ar conţine facturile emise în anii 2000 şi 2001 şi

LINIIFACT ce conţine numai înregistrări aferente anului calendaristic 2002. Începând cu 2002, orice

situaţie statistică privind vânzările în perioada 2000-2002, 2000-2003 etc. necesită reuniunea celor

două tabele, LF_2000_2001 şi LINIIFACT.

B. Pentru a afla care sunt clienţii care au cumpărat cel puţin unul din produsele Produs 1 şi Produs 2,

se poate proceda la reuniunea tabelei ce conţine clienţii care au cumpărat Produs 1 cu tabela clienţilor

care au cumpărat Produs2.

Page 101: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 101

Reuniunea este comutativă. Singura problemă neclară ar fi legată de numele atributelor în

relaţia rezultat. În acest sens, se poate institui regula potrivit căreia numele atributelor relaţiei-reuniune

sunt numele primei relaţii participantă în operaţie. Aceasta nu are importanţă asupra comutativităţii,

deoarece conţinutul tabelei-rezultat este identic, indiferent care este prima relaţie enumerată.

Intersecţia

Intersecţia a două relaţii uni-compatibile, R1 şi R2, poate fi definită astfel: R1 R2 = {tuplu

t t R1 şi t R2 } Se notează: R4 R1 R2. Conţinutul tabelei intersecţie R4 este

prezentat în figura 5.3. Cum numai un tuplu este absolut identic şi în R1 şi R2, tabela rezultat este

alcătuită dintr-o singură linie.

Figura 5.3. Intersecţia a două relaţii

Exemple de informaţii care fac necesară recurgerea la intersecţie:

- Pentru a afla care sunt clienţii care au cumpărat şi Produs 1 şi Produs 2, se poate proceda la

intersecţia tabelei clienţilor care au cumpărat Produs 1 cu tabela alcătuită din clienţii care au cumpărat

Produs2;

- Zilele în care s-a făcut vânzări şi clientului Client 1 SRL şi clientului Client 2 SA;

- Persoanele de la firmele-client care cumulează posturile de Director vânzări şi Şef aprovizionare.

Ca şi reuniunea, intersecţia este comutativă, iar numele atributelor relaţiei-intersecţie sunt

extrase din prima relaţie participantă în operaţie.

Diferenţa

Diferenţa a două relaţii uni-compatibile, notate R1 şi R2, este definită astfel: R1 - R2 = {tuplu

t t R1 şi t R2 }. Se notează: R5 R1 R2. Conţinutul tabelei-diferenţă R5 (figura

5.4) conţine numai tuplurile din prima relaţie, R1, care nu se regăsesc în a doua relaţie, R2.

Figura 5.4. Diferenţa a două relaţii

Page 102: BD1 2010 FotacheM Integral

102 Baze de date I

Aşadar, din rezultat este eliminat al treilea tuplu din R1, deoarece valorile acestuia există şi în

R2 (al doilea tuplu din R2). Exemple ce informaţii care fac necesară recurgerea la diferenţă:

- Care sunt clienţii care au cumpărat Produs 1 dar nu au cumpărat Produs 2 ?

- Care sunt zilele în care s-a făcut vânzări clientului Client 1 SRL dar nu există nici o factură către

Client 2 SA ?

Spre deosebire de reuniune şi intersecţie, diferenţa nu este este comutativă. Atributelor

relaţiei-diferenţă sunt cele ale primei relaţii (descăzutul), iar tuplurile sunt extrase din relaţia descăzut

nu se regăsesc în relaţia scăzător. În plus, nu există restricţii privind cardinalitatea (numărul de tupluri)

celor două relaţii, adică nu este musai ca relaţia descăzut să conţină mai multe tupluri decât cea

scăzător.

Produsul cartezian

Produsul cartezian al două relaţii R1 şi R2, denumit de Codd joncţiune încrucişată (CROSS

JOIN), este ansamblul tuturor tuplurilor obţinute prin concatenarea fiecărei linii din tabela R1 cu toate

liniile tabelei R2. Formal, dacă notăm cele două relaţii: R1 (A1, A2, ..., An) şi R2 (B1, B2, ..., Bm),

produsul cartezian este definit astfel: R1 R2 = { ( t1 , t2 ) t1 R1 şi t2 R2 }. Spre

deosebire de celelalte trei operaţiuni precedente, produsul cartezian nu face apel la noţiunea de relaţii

uni-compatibile, iar relaţia rezultat cumulează atributele celor două relaţii-argument. În figura 5.5 este

ilustrat rezutatul produsului cartezian a tabelelor R1 şi R2.

Figura 5.5. Produsul cartezian

Se notează: R6 ― R1 R2. Tabela rezultat R6 are o nouă structură - şase atribute (trei

preluate din R1 şi trei din R2). Întrucât există un atribut cu nume comun, C, pentru a diferenţia cele

două apariţii, acestea sunt prefixate, în antetul tabelei, cu numele relaţiei din care provine. Prima linie

din R6 este obiţinută prin “lipirea” primului tuplu din R1 cu primul tuplu din R2, a doua din primul

tuplu din R1 cu al doilea din R2 etc. Cum R1 are 3 tupluri, iar R2 tot 3, relaţia-rezultat al produsului

cartezian are 3 * 3 = 9 tupluri.

Nu prea există situaţii care să reclame folosirea directă şi exclusivă a produsului cartezian. Cel

mai important “merit” al acestuia în algebra relaţională este că permite “alipirea” a două relaţii,

fundamentând astfel operatorul-cheie care este joncţiunea.

Page 103: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 103

5.3 OPERATORII RELAŢIONALI

Cei patru operatori prezentaţi în paragraful precedent sunt generali, spre deosebire de

următorii care sunt specifici algebrei relaţionale. De obicei gruparea operatorilor relaţionali se face

astfel: (a) operatori unari de restricţie, care permit decupajul unei relaţii, pe orizontală - SELECŢIA şi

pe verticală - PROIECŢIA; (b) operatori binari de extensie: JONCŢIUNEA şi DIVIZIUNEA. O altă

deosebire majoră faţă de paragraful precedent este că vom putea recurge şi la exemple concrete din

baza de date “cobai” prezentată în capitolul anterior.

Selecţia

Selecţia triază dintr-o relaţie (tabelă) numai tuplurile ce satisfac o condiţie specificată printr-

un predicat. Ca preambul, definim noţiunea de formulă F asupra unei relaţii R, ca o expresie logică

compusă din: operanzi care sunt nume de atribute sau constante; operatori de comparaţie aritmetică:

, , , , =, ; operatori logici: ŞI, SAU, NON. Selecţia unei relaţii R, printr-o condiţie F, notată

SF(R), poate fi definită: SF(R) = { tuplu t (t R şi F(t) = adevărată)}. O notaţie ceva mai

pământească este: R1 SELECŢIE (R; <expresie-logică>), dar, de dragul ştiinţei, vom detalia:

R este relaţia R (A1,A2,...An) asupra căreia se aplică selecţia (Ai sunt atributele sale).

R1 este noua relaţie obţinută în urma selecţiei, care va avea aceeaşi schemă relaţională cu R -

R1 (A1,A2, ... An).

<expresie-logică> poate fi scrisă mai analitic astfel:

<expresie-logică> = (termen1) şi/sau (termen2) ... şi/sau (termenk), unde

o termen j = expresie1 expresie2 .

o expresie1 sau expresie2 sunt expresii calculate plecând de la atributele Ai.

o poate fi unul dintre operatorii pentru comparaţie.

Exemplu 1: Pentru a pune în operă savanta notaţie de mai sus, luăm în discuţie o primă

problemă: Care sunt liniile din R1 pentru care valorile atributelor A şi C sunt mai mari decât 20 ?

Ajungem, astfel, la notaţia:

R SELECŢIE (R1; A > 20 AND C > 20). Tabela R este prezentată în figura 5.6.

Figura 5.6. Rezultat selecţie – exemplul 1

Exemplu 2: Care sunt judeţele din Moldova ?

Mai întâi, se identifică în baza de date tabela (sau tabelele) din care se extrage rezultatul. În

acest exemplu, aceasta este JUDETE. Apoi se stabilesc atributele (atributul) asupra cărora se va aplica

predicatul de selecţie. Se obţine: R SELECŢIE (JUDETE; Regiune = “Moldova”) – vezi fig. 5.7.

Figura 5.7. Rezultat selecţie – exemplul 2

Page 104: BD1 2010 FotacheM Integral

104 Baze de date I

Exemplu 3: Care sunt facturile emise în perioada 2-5 august 2000 ?

Tabela în care va opera operatorul de selecţie este FACTURI. Predicatul de selecţie utilizează atributul

DataFact: R SELECŢIE (FACTURI; DataFact >= 02/08/2000 AND DataFact <= 05/08/2000)

Figura 5.8. Rezultat selecţie – exemplul 3

Proiecţia

Prin proiecţie, o relaţie poate fi "decupată" pe verticală. Dacă selecţia extrage dintr-o tabelă

anumite linii, pe baza condiţiei îndeplinite de valorile unora dintre atribute, proiecţia permite

selectarea într-o tabelă-rezultat numai a coloanelor (atributelor) dorite dintr-o relaţie. Formal, fie o

relaţie R (A1,A2,...An). Proiecţia relaţiei R asupra unui subansamblu alcătuit din atribute proprii este o

relaţie care se obţine după parcurgerea a doi paşi: a. eliminarea dintre Ai a acelor atribute care nu sunt

specificate şi b. suprimarea dublurilor (tuplurile identice). Se notează: R1 PROIECŢIE (R; Aj,

Ak, ..., Ax). Spre deosebire de R, schema relaţiei R1 este alcătuită numai din atributele indicate: R1

(Aj, Ak, ..., Ax). Dacă după extragerea coloanelor nu există tupluri (linii) identice, R1 va avea acelaşi

număr de linii ca şi relaţia R. În caz contrar, numărul lor va fi mai mic, în funcţie de numărul

dublurilor.

Exemplu 4: Începem, ca de obicei, cu un exemplu ceva mai arid - Care sunt valorile

combinaţiei atributelor A şi C în relaţia R1 ?

R PROIECŢIE (R1; A, C). Tabela R are două coloane, A şi C, şi trei linii, ca în figura 5.9.

Figura 5.9. Rezultat proiecţie – exemplul 4

Exemplu 5: Ce regiuni ale ţării sunt preluate în bază ?

Tabela în care se află răspunsul este JUDETE. Singura coloană care interesează este Regiune.

R PROIECŢIE (JUDETE; Regiune). În primul pas, se face decupajul pe verticală, obţinându-se o

relaţia notată R‟, apoi se elimină dublurile, rezultatul final fiind relaţia R.

Figura 5.10. Rezultat proiecţie – exemplul 5

Page 105: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 105

Exemplu 6 - Care sunt: codul, denumirea şi numărul de telefon ale fiecărui client ?

Tabela care interesează este CLIENTI, din care se decupează trei coloane: CodCl, DenCl şi Telefon.

R PROIECŢIE (CLIENTI; CodCl, DenCl, Telefon).

Figura 5.11. Rezultat proiecţie – exemplul 6

Înlănţuirea consultărilor

Rezultatul unei consultări este o relaţie (tabelă) nouă. Pe baza acestui fapt, se pot înlănţui două

sau mai multe operaţiuni, redactându-se astfel interogări complexe.

Exemplu 7 - Care este numărul de telefon al clientul Client 2 SA ?

Soluţia este una foarte simpă. Cu ajutorul selecţiei se decupează din relaţia CLIENTI numai linia

corespunzăoare clientului “incriminat”. Se obţine o relaţie nou-nouţă denumită (de noi) R1. Asupra lui

R1 se aplică o proiecţie, deoarece interesează numai numărul de telefon; astfel, R2 conţine răspunsul

la problema luată în discuţie (vezi figura 5.12).

R1 SELECŢIE (CLIENTI; DenCl = ”Client 2 SA”)

R2 PROIECŢIE (R1; Telefon)

Figura 5.12. Înlănţuirea unei selecţii cu o proiecţie – exemplul 7

Exemplu 8 - Care sunt denumirile şi codurile poştale introduse pentru localităţile (prezente în

bază) din judeţele Iaşi (IS) şi Vrancea (VN) ?

Tabela “interogată” este CODURI_POSTALE. Pentru a răspunde la întrebarea pe care tot noi am

formulat-o, putem alege între următoarele două soluţii:

Soluţia 1 – figura 5.13:

R1 SELECŢIE (CODURI_POSTALE; Jud = ”IS” OR Jud = ”VN”)

R2 PROIECŢIE (R1; Loc, CodPost)

Figura 5.13. Exemplu 8 – soluţia 1

Soluţia 2 – figura 5.14:

Page 106: BD1 2010 FotacheM Integral

106 Baze de date I

R1 SELECŢIE (CODURI_POSTALE; Jud = ”IS”)

R2 PROIECŢIE (R1; Loc, CodPost)

R3 SELECŢIE (CODURI_POSTALE; Jud = ”VN”)

R4 PROIECŢIE (R3; Loc, CodPost)

R5 R2 R4

Figura 5.14. Exemplu 8 – soluţia 2

Prima soluţie este, prin simplitate, cea mai tentantă. Este, însă, un prim caz în care pentru

rezolvarea unei probleme pot fi formulate două (sau mai multe) soluţii.

Exemplu 9 - Care sunt codurile produselor care apar deopotrivă în factura 1111 şi în factura

1117 ?

Tabela din care vor fi extrase datele este LINIIFACT. Soluţia se bazează pe intersecţia relaţiei care

conţine produsele prezente în factura 1111 (R2) cu relaţia produselor prezente în factura 1117 (R4),

după cum reiese din figura 5.15.

R1 SELECŢIE (LINIIFACT; NrFact = 1111)

R2 PROIECŢIE (R1; CodPr)

R3 SELECŢIE (LINIIFACT; NrFact = 1117)

R4 PROIECŢIE (R3; CodPr)

R5 R2 R4

Figura 5.15. Soluţie - exemplu 9

Joncţiunea

Într-un paragraf anterior am văzut că produsul cartezian permite fuzionarea a două tabele într-

o tabelă mamut ce conţine toate atributele şi liniile obţinute prin combinarea fiecărui tuplu dintr-o

Page 107: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 107

relaţie cu fiecare tuplu din cealaltă. Tot atunci ne exprimam regretul sincer că operatorul produs

cartezian nu poate fi folosit, de unul singur, în interogări, dar că acest “neajuns” va fi compensat din

plin de operatorul derivat joncţiune.

Dacă produsul cartezian este o fuziune necondiţionată a două tabele, joncţiunea reprezintă

fuziunea a două relaţii care au o proprietate comună. Fie două relaţii, notate: R1 (A1, A2, ..., An) şi

R2 (B1, B2, ..., Bp). Fie Ai şi Bj două atribute definite pe acelaşi domeniu şi ansamblul operatorilor

de comparaţie: {=, >, , <, , }, ce pot fi aplicaţi celor două atribute Ai şi Bj. Joncţiunea relaţiei R1,

prin Ai, cu relaţia R2, prin Bj, notată R1 (Ai Bj) R2 sau R1 R2, este relaţia ale cărei

tupluri sunt obţinute prin concatenarea fiecărui tuplu al relaţiei R1 cu tuplurile relaţiei R2, pentru care

este verificată condiţia instituită între Ai şi Bj. R1 (Ai Bj) R2 = { t | t R1 R2 şi t(Ai) t(Bj)}.

Joncţiunea este echivalenta unui produs cartezian urmat de o selecţie. Joncţiunea definită mai

sus este referită în lucrările de specialitate ca theta-joncţiune. În lucrul cu BDR se utilizează cu

precădere echi-joncţiunea, ce reprezentă un caz particular al theta-joncţiunii, atunci când este

operatorul de egalitate ("="). Formal, echijoncţiunea se defineşte astfel: R1 (Ai = Bj) R2 = { t | t R1

R2 şi t(Ai) = t(Bj)}. Apelăm şi la o altă notaţie, mai uşor de reprezentat şi suficient de inteligibilă.

R ECHI-JONCŢIUNE (R1, R2; Ai=Bj)

Exemplu 10 – Theta-joncţiune

Începem exemplificările cu aceleaşi două tabele folosite din precedentul paragraf, R1 şi R2. Rezultatul

joncţiunii (theta-joncţiunii) exprimată prin expresia: R JONCŢIUNE (R1, R2; R1.A >= R2.E) va

fi obţinut în doi paşi, după este descris în figura 5.16.

Figura 5.16. Mecanismul de (theta)joncţionare - exemplul 10

Exemplu 11 – Echi-joncţiune

Operatorul de comparaţie dintre cele două atribute este, obligatoriu, semnul de egalitate.

R JONCŢIUNE (R1, R2; R1.A = R2.E)

Page 108: BD1 2010 FotacheM Integral

108 Baze de date I

Figura 5.17. Echi-joncţiune - exemplul 11

Exemplu 12 – Joncţiune naturală

Joncţiunea naturală presupune nu numai ca operatorul de comparaţie să fie semnul de egalitate, ci şi

denumirea identică a atributelor de legătură dintre cele două tabele. R JONCŢIUNE (R1, R2; R1.C

= R2.C). Datorită faptului că ambele atribute au acelaşi nume, se poate considera că tabela rezultat

păstrează numai unul din cele două atribute, ca în figura 5.18 şi se poate recurge la o notaţie

simplificată: R JONCŢIUNE (R1, R2; C).

Figura 5.18. Joncţiune naturală - exemplul 12

De ce se insistă atât de mult pe importanţa joncţiunii ? În primul rând pentru că permite re-

compunerea relaţiei universale iniţiale. Modelul relaţional se bazează pe spargerea bazei în relaţii,

astfel încât nivelul redundanţei datelor şi problemele la actualizarea tabelelor să fie reduse la minim.

Cele mai multe interogări, însă, operează cu date şi predicate aplicate simultan atributelor din două sau

mai multe tabele. Cum selecţia este un operator unar (poate fi aplicată unei singure relaţii), este

necesară fuzionarea prealabilă a celor două, trei… relaţii şi obţinerea unei relaţii-agregat, relaţie

agregat la care se aplică predicatul suplimentar de selecţie.

Fuzionarea este posibilă prin joncţiune. Prin joncţionarea tuturor relaţiilor dintr-o bază de date

se obţine relaţia universală (cea iniţială, atot-cuprinzătoare). Nu ne permitem să reconstituim structura

Page 109: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 109

şi conţinutul relaţiei universale ale bazei de date VINZARI, însă, prin exemple, vom încercă să

demonstrăm utilitatea joncţiunii.

Exemplu 13 - Să se obţină, pentru fiecare localitate: codurile poştale, denumirea, indicativul

judeţului, denumirea judeţului şi regiunea din care face parte

Practic, tabelei CODURI_POSTALE îi trebuie “alipite” la dreapta informaţiile din tabela JUDETE.

Problema este dificil de formulat, însă rezolvarea sa este cât se poate de simplă: joncţionarea celor

două relaţii. R JONCŢIUNE (CODURI_POSTALE, JUDETE; Jud)

Una din întrebările “clasice” pentru verificarea modului în care a fost sau nu înţeleasă

joncţiunea este: Câte linii are tabela-rezultat al joncţiunii ? În cazul nostru (de obicei în BDR)

răspunsul este: câte linii are tabela-copil. Răspunsul este corect numai atunci când se respectă

integritatea referenţială, altfel spus, numai atunci când toate valorile cheii străine se regăsesc în tabela-

părinte. Ca de obicei, lucrurile sunt mai complicate în realitate, deoarece joncţiunea nu se instituie

musai între o tabelă părinte şi una copil.

Exemplu 14 - Care sunt localităţile din Banat ?

Soluţia 1 - după calapodul exemplului anterior:

R1 JONCŢIUNE (CODURI_POSTALE, JUDETE; Jud)

R SELECŢIE (R1; Regiune = “Banat”)

Soluţia 2: Mai întâi, se aplică selecţia asupra tabelei JUDETE, iar tabela intermediară se

joncţionează cu CODURI_POSTALE:

R1 SELECŢIE (JUDETE; Regiune = “Banat”)

R JONCŢIUNE (CODURI_POSTALE, R1; Jud)

A doua variantă pare mai bună decât prima, deoarece joncţiunea operează asupra a două relaţii mai

reduse ca dimensiuni. Diferenţa este cu atât mai vizibilă atunci când relaţia JUDETE conţine toate

judeţele ţării, iar CODURI_POSTALE are câteva sute de înregistrări. Iar dacă ne gândim că înaintea

oricărei joncţiuni se calculează produsul cartezian, apare drept firească ideea amânării joncţiunii, astfel

încât aceasta să opereze asupra unor tabele cu număr cât mai mic de linii şi coloane. Coborând cu

picioarele pe pământ, trebuie spus că discuţia noastră are un caracter de principiu, deoarece algebra

relaţională este, totuşi, un limbaj… curat teoretic.

Exemplu 15 - În ce zile s-a vândut produsul cu denumirea “Produs 1” ?

Elementul de noutate îl reprezintă interesul pentru o informaţie ce provine dintr-o relaţie (atributul

DataFact din FACTURI) pe baza unei condiţii aplicate altei relaţii (atributul DenPr din PRODUSE),

iar cele două relaţii, FACTURI şi PRODUSE nu sunt în raportul părinte-copil. În aceste cazuri, este

necesară atragerea altor relaţii, până se completează “lanţul”. Interogarea ce rezolvă problema ridicată

în acest exemplu necesită şi tabela LINIIFACT.

Soluţie 1 - neoptimizată

R1 JONCŢIUNE (PRODUSE, LINIIFACT; CodPr)

R2 JONCŢIUNE (R1, FACTURI; NrFact)

R3 SELECŢIE (R2; DenPr = “Produs 1”)

R PROIECŢIE (R3; DataFact)

Page 110: BD1 2010 FotacheM Integral

110 Baze de date I

Soluţie 2 – optimizată la sânge

R1 SELECŢIE (PRODUSE; DenPr = “Produs 1”)

R2 PROIECŢIE (R1; CodPr)

R3 JONCŢIUNE (R2, LINIIFACT; CodPr)

R4 PROIECŢIE (R3; NrFact)

R5 JONCŢIUNE (R4, FACTURI; NrFact)

R PROIECŢIE (R4; DataFact)

În cea de-a doua soluţie, fideli principiului “joncţiunii celei mai economicoase”, am eliminat nu numai

tuplurile, dar şi atributele de prisos înaintea calculării relaţiei intermediare.

Exemplu 16 - În ce judeţe s-a vândut produsul cu denumirea “Produs 1” în perioada 3-5

august 2005 ?

Relaţia rezultat trebuie să conţină valori ale atributului Judet din tabela JUDETE. Predicatul de

selecţie se aplică însă, în alte două tabele: PRODUSE, în care DenPr = “Produs 1” şi FACTURI ale

cărei tupluri trebuie să verifice condiţia: DataFact >= 03/08/2005 AND DataFact <= 03/08/2005.

R1 SELECŢIE (PRODUSE; DenPr = “Produs 1”)

R2 JONCŢIUNE (R1, LINIIFACT; CodPr)

R3 PROIECŢIE (R2; NrFact)

R4 SELECŢIE(FACTURI; DataFact >= 03/08/2005 AND DataFact <= 03/08/2005)

R5 JONCŢIUNE (R3, R4; NrFact)

R6 JONCŢIUNE (R5, CLIENTI; CodCl)

R7 PROIECŢIE (R6; CodPost)

R8 JONCŢIUNE (R7, CODURI_POSTALE; CodPost)

R9 PROIECŢIE (R8; Jud)

R10 JONCŢIUNE (R9, JUDETE; Jud)

R PROIECŢIE (R10; Judet)

Exemplu 17 - În ce zile s-au vândut şi produsul cu denumirea “Produs 1” şi cel cu

denumirea “Produs 2” ?

Rezultatul conţine atributul DataFact. Predicatul de selecţie se aplică tabelei PRODUSE.

Soluţie 1

R1 SELECŢIE (PRODUSE; DenPr = “Produs 1”)

R2 JONCŢIUNE (R1, LINIIFACT; CodPr)

R3 JONCŢIUNE (R2, FACTURI; NrFact)

R4 PROIECŢIE (R3; DataFact)

R5 SELECŢIE (PRODUSE; DenPr = “Produs 2”)

R6 JONCŢIUNE (R5, LINIIFACT; CodPr)

R7 JONCŢIUNE (R6, FACTURI; NrFact)

R8 PROIECŢIE (R7; DataFact)

R R4 R8

Soluţie 2

R1 SELECŢIE (PRODUSE; DenPr = “Produs 1”)

R2 JONCŢIUNE (R1, LINIIFACT; CodPr)

Page 111: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 111

R3 JONCŢIUNE (R2, FACTURI; NrFact)

R4 SELECŢIE (PRODUSE; DenPr = “Produs 2”)

R5 JONCŢIUNE (R5, LINIIFACT; CodPr)

R6 JONCŢIUNE (R6, FACTURI; NrFact)

R7 JONCŢIUNE (R3, R6; DataFact)

R PROIECŢIE (R7; DataFact)

Prima variantă este una “cuminte”. Se intersectează relaţia zilelor în care s-a vândut primul produs

(R4) cu relaţia zilelor în care s-a facturat produsul 2 (R8). A doua e ceva mai insolită. Pur şi simplu, în

loc de intersecţie folosim joncţiunea. Cum, spre deosebire de intersecţie, relaţiile joncţionate nu

trebuie să fie unicompatibile, putem să operăm direct joncţiunea între R3 şi R6.

Reţinem ideea: putem să simulăm intersecţia a două relaţii prin joncţiune. Pentru a fi mai

convingător, rogu-vă să examinaţi figura 5.19 în care, pornind de la relaţiile R1 şi R2, se obţine

relaţia-intersecţie R direct prin operatorul intersecţie (stânga figurii) şi mai pe ocolite, folosind

joncţiunea (dreapta).

Figura 5.19. Intersecţia prin joncţiune

Exemplu 18 - Ce clienţi au cumpărat “Produs 2” şi “Produs 3”, dar nu au cumpărat

“Produs 5” ?

Bună întrebare ! Lucrurile nu însă atât de complicate precum par, deoarece folosim elemente din

exemplul anterior (intersecţia) plus operatorul… diferenţă.

R1 SELECŢIE (PRODUSE; DenPr = “Produs 2”)

R2 JONCŢIUNE (R1, LINIIFACT; CodPr)

R3 JONCŢIUNE (R2, FACTURI; NrFact)

R4 JONCŢIUNE (R3, CLIENTI; CodCl)

R5 PROIECŢIE (R4; DenCl)

R6 SELECŢIE (PRODUSE; DenPr = “Produs 3”)

R7 JONCŢIUNE (R6, LINIIFACT; CodPr)

R8 JONCŢIUNE (R7, FACTURI; NrFact)

R9 JONCŢIUNE (R8, CLIENTI; CodCl)

R10 PROIECŢIE (R9; DenCl)

Page 112: BD1 2010 FotacheM Integral

112 Baze de date I

R11 SELECŢIE (PRODUSE; DenPr = “Produs 5”)

R12 JONCŢIUNE (R11, LINIIFACT; CodPr)

R13 JONCŢIUNE (R12, FACTURI; NrFact)

R14 JONCŢIUNE (R13, CLIENTI; CodCl)

R15 PROIECŢIE (R14; DenCl)

R R5 R10 – R15

Exemplu 19 - Ce facturi au fost emise în aceeaşi zi cu factura 1120 ?

Spre deosebire de interogările de până acum, condiţia de selecţie este una indirectă. În prealabil,

trebuie determinată ziua în care a fost întocmită factura cu numărul 1120. Apoi trebuie extrase, din

relaţia FACTURI, liniile pentru care DataFact are valoarea zilei facturii-reper.

R1 SELECŢIE (FACTURI; NrFact = 1120)

R2 PROIECŢIE (R1; DataFact)

R3 JONCŢIUNE (FACTURI, R2; DataFact)

R PROIECŢIE (R3; NrFact)

Joncţiunea externă

Ideea de bază a joncţiunii externe este de a include în rezultat şi tupluri din una din relaţii, sau

din ambele relaţii, care prezintă valori ale atributului de legătură ce nu se regăsesc în cealaltă relaţie.

Dacă precedentele tipuri de joncţiune sunt comutative, în cazul joncţiunii externe trebuie specificat din

care relaţie se extrag liniile fără corepondent în cealaltă relaţie. De aceea, există joncţiune externă la

stânga şi joncţiune externă la dreapta. La acestea se adaugă joncţiunea externă totală (denumită şi

plină sau deplină) care reprezintă reuniunea celor două. Apelând la aceleaşi tabele “abstracte”, R1 şi

R2, diferenţa dintre joncţiunea internă (echi-joncţiunea) şi cele trei tipuri de joncţiune externă apare cu

mai multă claritate în figura 5.20.

Figura 5.20. Diferenţa dintre echi-joncţiune şi joncţiunile externe

Exemplu 20 - Care sunt localităţile în care nu avem nici un client ?

Page 113: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 113

Soluţia 1 se bazează diferenţa dintre tabela tuturor localităţilor (din tabela CODURI_POSTALE) şi

tabela localităţilor în care există clienţi (tabela CLIENŢI).

R1 PROIECŢIE (CODURI_POSTALE; CodPost)

R2 PROIECŢIE (CLIENTI; CodPost)

R3 R1 – R2

R JONCŢIUNE (R3, CODURI_POSTALE; CodPost)

Soluţia 2 utilizează proaspăta joncţiune externă.

R1 JONCŢIUNE EXTERNĂ LA STÎNGA (CODURI_POSTALE, CLIENTI;

CODURI_POSTALE.CodPost = CLIENTI.CodPost)

R SELECTIE (R1; CodCl IS NULL)

Pentru a identifica localităţile fără clienţi, s-au extras, din joncţiunea externă la stânga a relaţiilor

CODURI_POSTALE şi CLIENTI, numai liniile în care unul din atributele preluate din CLIENTI (noi

ne-am oprit asupra primului, CodCl) are valoarea NULL.

Diviziunea

Este cel mai complex şi mai greu de explicat dintre operatorii prezentaţi în acest capitol. Codd

l-a imaginat ca operator invers al produsului cartezian. Pentru a-l defini, se porneşte de la două relaţii

R1(X,Y) şi R2(Y); prima are, care va să zică, două atribute sau grupe de atribute, notate X şi Y, în

timp ce a doua numai atributul sau grupul de atribute notat cu Y (definit pe acelaşi domeniu ca şi în

relaţia R1). O primă restricţie: relaţia R2(Y), fiind numitorul diviziunii, nu trebuie să fie vidă.

Diviziunea relaţională R1 R2 are ca rezultat o relaţie definită ca ansamblul sub-tuplurilor

R1(X) pentru care produsul (lor) cartezian cu R2(Y) este un subansamblu al R1(X,Y). Rezultatul

expresiei R1 R2 reprezintă câtul diviziunii, fiind o relaţie ce poate fi notată R3(X). Într-o altă

formulare, xi R3 dacă şi numai dacă yi Y R2 (xi, yi) R1. Pentru

simplificarea prezentării, în continuare am considerat X şi Y două atribute, deşi, după cum reiese din

preambul, acestea pot fi grupe (ansambluri) de atribute. Să examinăm elementele din figura 5.21.

Figura 5.21. Diviziunea relaţională

Determinarea relaţiei R3 R1 R2 este sinonimă cu rezolvarea problemei: care dintre x1,

x2, x3, x4 şi x5 apar în R1, în tupluri împreună cu toate valorile lui Y din R2, respectiv y1, y2, y3,

y4 şi y5 ?

Page 114: BD1 2010 FotacheM Integral

114 Baze de date I

Se parcurg pe rând valorile xi ale atributului X din relaţia R1:

x1 apare cu y1 (în tuplul 1), cu y2 (în tuplul 4), cu y3 (în tuplul 7), cu y4 (în tuplul 10) şi cu y5

(în tuplul 13). Deci x1 îndeplineşte condiţia şi va fi inclus în relaţia R3;

x2 apare cu y1 (în tuplul 2) dar nu apare cu y2 - nu va face parte din R3;

x3 apare cu y1 (în tuplul 3), cu y2 (în tuplul 5), cu y3 (în tuplul 8), cu y4 (în tuplul 11) şi cu y5

(în tuplul 15) - îndeplineşte condiţia şi va fi tuplu în R3;

x4 nu apare cu y1 - nu va face parte din R3.

x5 nu apare cu y1 - nu va face parte din R3.

În urma raţionamentului de mai sus, tabela R3 va fi alcătuită din două tupluri. Deşi pare un operator

ceva mai metafizic, diviziunea relaţională este deosebit de utilă pentru formularea consultărilor în care

apare clauza " " ("oricare ar fi" sau "pentru toate").

Exemplu 22 -Care sunt clienţii pentrucare există cel puţin câte o factură emisă în fiecare zi ?

Într-o altă formulare, ne interesează clienţii care au cumpărat “câte ceva” în toate zilele în care s-au

efectuat vânzări ? Prin urmare, câtul va fi o tabelă cu singur atribut, DenCl (denumirea clientului), iar

divizorul va fi o relaţie alcătuită numai din atributul DataFact (conţine toate zilele în care s-au efectuat

vânzări). Dacă am merge pe calapodul prezentat, am putea nota R3(DenCl), R2(DataFact). Cunoscând

structura câtului şi a divizorului, putem determina structura tabelei-deîmpărţit: R1(DenCl, DataFact).

Relaţia R1 va conţine denumirile clienţilor şi zilele în care există măcar o factură pentru clientul

respectiv. Soluţia poate fi redactată în următorii paşi:

a. construire relaţiei-deîmpărţit:

R11 JONCŢIUNE (FACTURI, CLIENTI; CodCl)

R1 PROIECŢIE (R1; DenCl, DataFact)

b. construire relaţie-numitor: R2 PROIECŢIE (FACTURI; DataFact)

c. în fine, apoteoza: R3 R1 R2

Schema şi conţinutul relaţiilor implicate în această soluţie sunt prezentate în figura 5.22.

Figura 5.22. Diviziunea relaţională – exemplu 22

Page 115: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 115

Poate o să spuneţi că n-a meritat efortul, dar acest gen de soluţii se aplică la o largă gamă de

probleme. Să discutăm câteva speţe.

Exemplu 23 - În ce zile s-au vândut şi produsul cu denumirea “Produs 1” şi cel cu denumirea

“Produs 2” ?

Vă gândiţi, probabil, că aţi mai văzut undeva acest enunţ. Este chiar textul problemei de la exemplul

17. La acel moment, am formulat două soluţii: una “clasică”, bazată pe intersecţie şi una mai

neconvenţională ce utilizează joncţiunea. Adăugăm la acestea o alta, încadrabilă probabil tot în

categoria “neconvenţionale”.

Soluţie 3

R11 SELECŢIE (PRODUSE; DenPr = “Produs 1” OR DenPr = “Produs 2”)

R12 JONCŢIUNE (R11, LINIIFACT; CodPr)

R13 JONCŢIUNE (R12, FACTURI; NrFact)

R1 PROIECŢIE (R13; DataFact, CodPr)

R2 PROIECŢIE (R11; CodPr)

R3 R1 R2

Relaţia divizor este alcătuită din două tupluri ce conţin codurile celor două produse. Deîmpărţitul este

alcătuit din atributele DataFact şi CodProd şi conţine toate produsele vândute în fiecare din zilele de

facturare.

Exemplu 24 - Ce facturi au fost emise în aceeaşi zi cu factura 1120 ?

Deşi reprezintă un regres vizibil faţă de nivelul interogărilor precdente, revenim discret la enunţul

exemplului 19. Pentru această problemă se poate încropi şi o variantă de rezolvare bazată pe

diviziunea relaţională.

R1 PROIECŢIE (FACTURI; NrFact, DataFact)

R2‟ SELECŢIE (FACTURI; NrFact = 1120)

R2 PROIECŢIE (R2‟; DataFact)

R DIVIZIUNE (R1, R2)

Exemplu 25 - Căror clienţi le-au fost vândute toate produsele firmei ?

R11 JONCŢIUNE (LINIIFACT, FACTURI; NrFact)

R1 PROIECŢIE (R11; CodCl, CodPr)

R2 PROIECŢIE (PRODUSE; CodPr)

R3 DIVIZIUNE (R1, R2)

R JONCŢIUNE (R3, CLIENŢI; CodCl)

Page 116: BD1 2010 FotacheM Integral

116 Baze de date I

Capitolul 6

INTEROGĂRI SQL (1)

Obiective:

I. Scurtă incursiune în standardele SQL

II. Reflectarea în SQL a operatorilor ansamblişti

III. Prelucrarea datelor calendaristice

IV. Folosirea operatorilor BETWEEN, LIKE, IN

V. Joncţiunile în SQL

VI. Funcţii agregat: COUNT, SUM, AVG, MIN, MAX

VII. Gruparea tuplurilor: GROUP BY şi HAVING

VIII. Manipularea valorilor NULL şi joncţiuni externe

IX. Structuri alternative

Rezultate aşteptate:

Deprinderea tuturor opţiunilor pentru redactarea interogărilor de

complexitate medie

Page 117: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 117

6.1 ISTORICUL SQL. STANDARDE

După mulţi autori, momentul decisiv în naşterea SQL ca limbaj îl constituie lansarea

proiectului System /R de către firma IBM, eveniment ce a avut loc în 1974. Tot în 1974 Chamberlin şi

Boyce au publicat o lucrare în care este prezentată forma unui limbaj structurat de interogare,

"botezat" SEQUEL (Structured English as QUEry Language). În 1975 Chamberlin, Boyce, King şi

Hammer publică un articol în care prezintă sub-limbajul SQUARE, asemănător SEQUEL-ului, dar

care utiliza expresii matematice şi nu cuvinte din limba engleză. Autorii celor două studii au

demonstrat că limbajele SEQUEL şi SQUARE sunt complete din punct de vedere relaţional.

Un colectiv de autori, condus de Chamberlin, elaborează în 1976 o nouă lucrare în care se face

referire la SEQUEL 2, acesta fiind "preluat" ca limbaj de interogare al SGBD-ului System /R al firmei

IBM. În 1980 Chamberlin schimbă denumirea SEQUEL în SQL - Structured Query Language (Limbaj

Structurat de Interogare), dar şi astăzi mulţi specialişti pronunţă SQL ca pe predecesorul său. Anii

următori au înregistrat apariţia a o serie întreagă de lucrări care l-au perfecţionat, ultimul deceniu

consacrându-l ca pe cel mai răspândit limbaj de interogare a BDR, fiind prezent în numeroase

"dialecte" specifice tuturor SGBDR-urilor actuale, de la DB2 la Microsoft SQL Server, de la Oracle la

FoxPro şi ACCESS.

American National Standard Institute publică în 1986 standardul SQL ANSI X3.136-1986.

Este un standard care se bazează, într-o mare măsură, pe "dialectul" SQL al produsului DB2 de la

IBM. În 1989 are loc revizuirea extinderea acestui standard, "născându-se" SQL-89, care mai este

denumit şi SQL1. Deşi recunoscut ca bază a multor SGBDR-uri comerciale, SQL1 şi-a atras

numeroase critici. În plus, variantele comercializate de diferiţii producători, deşi asemănătoare în

esenţă, erau (şi sunt) incompatibile la nivel de detaliu. Pentru a umple golurile SQL1, ANSI a elaborat

în 1992 "versiunea" SQL2, specificaţiile fiind prezentate la un nivel mult mai detaliat (dacă SQL1 se

întindea pe numai 100 de pagini, SQL2 a fost publicat în aproape 600). IBM a avut un aport

incontestabil la apariţia şi maturizarea SQL, fiind un producător cu mare influenţă în "lumea" SGBD-

urilor, iar produsul său, DB2, este unul din standardele de facto ale SQL.

Standardul SQL:1999 a fost amânat de câteva ori până la publicarea sa, iar cea mai recentă

versiune este SQL:2008 (între timp a fost publicată şi versiunea SQL:2003). Actualmente, principalele

orientări ale SQL vizează transformarea acestuia într-un limbaj complet, în vederea definirii şi

gestionării obiectelor complexe şi persistente. Aceasta include: generalizare şi specializare, moşteniri

multiple, polimorfism, încapsulare, tipuri de date definite de utilizator, triggere (declanşatoare) şi

proceduri stocate, expresii privind interogări recursive şi instrumente adecvate de administrare a

datelor.

La momentul actual, SQL reprezintă cel mai important limbaj actual în domeniul bazelor de

date, atât prin gama comenzilor şi opţiunilor de care dispune, dar mai ales datorită faptului că s-a

reuşit standardizarea sa şi portarea pe toate Sistemele de Gestiune a Bazelor de date semnificative. Cu

atât mai mult, cu cât, spre deosebire de majoritatea covârşitoare a altor limbaje, poate fi deprins relativ

uşor de neinformaticieni şi utilizat pentru chestiuni de mare fineţe de către profesionişti. Acest capitol

se doreşte a fi o prezentare a elementelor esenţiale prin care, dată fiind structura unei baze de date

relaţionale, pot fi formulate interogări (fraze SELECT) prin care se obţin răspunsuri la gamă eterogenă

Page 118: BD1 2010 FotacheM Integral

118 Baze de date I

de întrebări. În plus, sunt evocate pe scurt comenzile pentru actualizarea unei tabele (INSERT,

UPDATE, DELETE), precum şi cele de declarare a structurii bazei de date (CREATE TABLE).

Din perspectiva prezentei lucrări, obiectivul principal al SQL constă în a oferi utilizatorului

mijloacele necesare formulării unei consultări numai prin descrierea rezultatului dorit, cu ajutorul unei

aserţiuni (expresie logică), fără a fi necesară şi explicitarea modului efectiv în care se face căutarea în

BD. Altfel spus, utilizatorul califică (specifică) rezultatul, iar sistemul se ocupă de procedura de

căutare. Deşi toate clasificările îl încadrează la limbaje de generaţia a IV-a, SQL nu este, totuşi, un

limbaj de programare propriu-zis, prin comparaţie cu Basic, Pascal, C, COBOL etc. SQL nu conţine

(până la SQL3) instrucţiuni/comenzi pentru codificarea secvenţelor alternative şi repetititive, cu atât

mai puţin facilităţi de lucru cu obiecte vizuale, specifice formularelor de preluare a datelor (căsuţe-

text, butoane radio, liste, butoane de comandă etc.). Din acest punct de vedere, poate fi referit ca sub-

limbaj orientat pe lucrul cu bazele de date. Comenzile sale pot fi, însă, inserate în programe redactate

în limbaje de programare "clasice".

Principalele atuuri ale SQL sunt:

1. Independenţa de producător, nefiind o tehnologie "proprietară".

2. Portabilitate între diferite sisteme de operare.

3. Este standardizat.

4. "Filosofia" sa se bazează pe modelul relaţional de organizare a datelor.

5. Este un limbaj de nivel înalt, cu structură relativ apropiată limbii engleze.

6. Furnizează răspunsuri la numeroase interogări simple, ad-hoc, neprevăzute iniţial.

7. Constituie suportul programatic pentru accesul la BD.

8. Permite multiple imagini asupra datelor bazei.

9. Este un limbaj relaţional complet.

10. Permite definirea dinamică a datelor, în sensul modificării structurii bazei chiar în timp ce o parte

din utilizatori sunt conectaţi la BD.

11. Constituie un excelent suport pentru implementarea arhitecturilor client-server.

6.2 SINTAXA DE BAZĂ A FRAZEI SELECT

Fără îndoială, cea mai gustată parte din SQL este cea legată de interogarea bazei, adică de

obţinerea de informaţii din cele mai diverse, prin prelucrări, grupări etc. În SQL o interogare se

formulează printr-o frază SELECT care are un format pe cât de simplu, pe atât de flexibil. Cele trei

clauze principale sunt SELECT, FROM şi WHERE55

, din care numai primele două sunt obligatorii.

Pentru început, vom face o paralelă cu operatorii algebrei relaţionale prezentaţi în capitolul 5.

6.2.1 Reflectarea operatorilor din algebra relaţională (1)

Selecţia şi proiecţia

Clauza SELECT corespunde operatorului proiecţie din algebra relaţională, fiind utilizată

pentru desemnarea listei de atribute (coloanele) din rezultat. Clauza FROM este cea în care sunt

enumerate relaţiile din care vor fi extrase informaţiile aferente consultării. Prin WHERE se

desemnează predicatul selectiv al algebrei relaţionale, relativ la atribute ale relaţiilor care apar în

clauza FROM.

55 La adresa http://w3.one.net/~jhoffman/sqltut.htm este un bun curs introductiv despre SQL

Page 119: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 119

Prin execuţia unei fraze SELECT se obţine un rezultat de formă tabelară. Acesta poate fi o

listă (text), o tabelă propriu-zisă sau o tabelă temporară (care, de obicei, nu poate fi actualizată), dar şi

o tabelă derivată (imagine). Uneori rezultatul poate fi obţinut şi ca o variabilă masiv (tablou). Atunci

când clauza WHERE este omisă, se consideră implicit că predicatul P are valoarea logică "adevărat",

astfel încât vor fi incluse în rezultat toate liniile din tabela, sau produsul cartezian al tabelelor,

enumerată/enumerate în clauza FROM. Dacă, în locul coloanelor C1, C2, ... Cn, apare simbolul *,

rezultatul va fi alcătuit din toate coloanele (atributele) relaţiilor specificate în clauza FROM. De

asemenea, atributele rezultatului preiau numele din tabela/tabelele specificate în FROM. Schimbarea

numelui se realizează prin clauza AS.

În capitolul 1 era subliniată echivalenţa noţiunilor relaţie-tabelă. Conform restricţiei de

unicitate, într-o relaţie nu pot exista două linii identice. În SQL, tabela obţinută dintr-o consultare

poate conţine două sau mai multe tupluri identice. Spre deosebire de algebra relaţională, în SQL nu

se elimină automat tuplurile identice (dublurile) din rezultat. Pentru aceasta este necesară utilizarea

opţiunii DISTINCT:

SELECT DISTINCT C1, C2, ..., Cn

FROM R1, R2, ..., Rm

WHERE P

În concluzie, o frază SELECT, în forma în care a fost prezentată, corespunde: unei selecţii algebrice

(clauza WHERE - P); unei proiecţii (SELECT - Ci); unui produs cartezian (FROM - R1 R2 ...

Rm), şi conduce la obţinerea unui rezultat cu n coloane, fiecare coloană fiind un atribut din R1, R2, ...,

Rm sau o expresie calculată pe baza unor atribute din R1, R2, ..., Rm. Acum vom trece în SQL câteva

interogări din algebra relaţională, pe baza exemplelor din capitolul 5.

Exemplul 1 - selecţie

SELECT * FROM R1 WHERE A > 20 AND C > 20

Exemplul 2 – selecţie (Care sunt judeţele din Moldova ?)

SELECT * FROM JUDETE WHERE Regiune = “Moldova”

Exemplul 3 – selecţie (Care sunt facturile emise în perioada 2-5 august 2005 ?)

Formatul standard al unei constante de tip dată calendaristică este YYYY-MM-DD, aşa încât o

interogarea în SQL-92 (şi în dialectul PostgreSQL) poate avea forma:

SELECT * FROM FACTURI WHERE DataFact >= '2005-08-02' AND DataFact <= '2005-08-05'

Exemplul 4 – proiecţie

SELECT A,C FROM R

Exemplul 5 (Care sunt regiunile ţării preluate în bază ?)

După cum aminteam mai sus, spre deosebire de algebra relaţională, SQL nu elimină automat dublurile.

Tabela obţinută prin consultarea:

SELECT Regiune FROM JUDETE

are structura şi conţinutul identice cu R‟ (figura 5.10). Pentru a obţine răspunsul de forma tabelei R (o

regiune să apară o singură dată în răspuns) se foloseşte clauza DISTINCT:

SELECT DISTINCT Regiune FROM JUDETE

Exemplul 6 (Care sunt: codul, denumirea şi numărul de telefon ale fiecărui client ?)

SELECT CodCl, DenCl, Telefon FROM CLIENTI

Nu este necesară clauza DISTINCT, deoarece CodCl este cheia primară a tabelei CLIENTI.

Page 120: BD1 2010 FotacheM Integral

120 Baze de date I

Exemplul 7 (Care este numărul de telefon al clientul Client 2 SA ?)

SELECT Telefon FROM CLIENTI WHERE DenCl = 'Client 2 SA'

Exemplul 8 (Care sunt denumirile şi codurile poştale ale localităţilor (prezente în bază) din

judeţele Iaşi (IS) şi Vrancea (VN) ?)

SELECT Loc, CodPost FROM coduri_postale WHERE Jud = 'IS' OR Jud = 'VN'

Reuniunea

Un rezultat identic cu tabela R3 din figura 5.2. se obţine prin fraza SELECT următoare:

SELECT * FROM R1 UNION SELECT * FROM R2

La reuniunea a două tabele, SQL elimină automat liniile identice din rezultat. Dacă se doreşte

preluarea tuturor liniilor celor două relaţii, şi, implicit, apariţia de linii duplicate se foloseşte clauza

ALL astfel:

SELECT * FROM R1 UNION ALL SELECT * FROM R2

Revenim la exemplul 8 din capitolul 5 (Care sunt denumirile şi codurile poştale ale

localităţilor (prezente în bază) din judeţele Iaşi (IS) şi Vrancea (VN) ?). Fraza SQL echivalentă

soluţiei 2 (bazată pe reuniune este):

SELECT Loc, CodPost FROM CODURI_POSTALE WHERE Jud = „IS‟

UNION SELECT Loc, CodPost FROM CODURI_POSTALE WHERE Jud = „VN‟

Intersecţia

Raportându-ne la exemplul din figura 2.3, echivalentul tabelei R4 se obţine în SQL prin:

SELECT * FROM R1 INTERSECT SELECT * FROM R2

SQL92 permite, ca şi în cazul reuniunii, prezenţa repetată în rezultat a unor linii (tupluri duplicate),

bineînţeles atunci când cele două relaţii asupra cărora se aplică intersecţia nu respectă restricţia de

unicitate:

SELECT * FROM R1 INTERSECT ALL SELECT * FROM R2

Dacă tuplul t1 apare repetat şi în R1 şi în R2, mai precis, de n ori în R1 şi de m ori în R2, în rezultatul

operatorului INTERSECT t1 apare o singură dată, în timp ce utilizând INTERSECT ALL t1 va apărea

de un număr de ori care este minimul dintre n şi m.

Pentru o primă ilustrare a utilizării a intersecţiei, transcriem soluţia din algebra relaţională

formulată la exemplul 9 (Care sunt codurile produselor care apar simultan şi în factura 1111 şi în

factura 1117 ?):

SELECT CodPr FROM LINIIFACT WHERE NrFact = 1111

INTERSECT SELECT CodPr FROM LINIIFACT WHERE NrFact = 1117

Exemplele de mai sus funcţionează în DB2, Oracle, şi PostgreSQL nu însă şi în alte SGBD-

uri (ex. Visual FoxPro) care nu are implementat operatorul INTERSECT, astfel încât intersecţia

trebuie realizată prin alte clauze şi operatori.

Diferenţa

Operatorul aşteptat ar fi MINUS. În standardul SQL92, şi în câteva SGBDR-uri precum DB2,

operatorul MINUS nu există, fiind substituit de EXCEPT, iar în alte SGBD-uri nu există nici unul, nici

altul. Tabela R5 din figura 2.5, calculată prin expresia R1-R2, se obţine în SQL (şi PostgreSQL) prin

interogarea:

Page 121: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 121

SELECT * FROM R1 EXCEPT SELECT * FROM R2

Ca şi în cazul reuniunii şi intersecţii, eliminarea tuplurilor duplicate se face automat, iar repetarea lor

se asigură prin EXCEPT ALL.

Modificăm enunţul exemplului 9 în următorul: Care sunt codurile produselor care apar în

factura 1111, dar nu apar în factura 1117 ?

SELECT CodPr FROM LINIIFACT WHERE NrFact = 1111

EXCEPT SELECT CodPr FROM LINIIFACT WHERE NrFact = 1117

Produsul cartezian

SQL nu pune la dispoziţie vreun operator special dedicat produsului cartezian. Nici nu este nevoie.

Tabela R6 din figura 5.5 se obţine, pur şi simplu, prin enumerarea celor două relaţii în clauza FROM:

SELECT * FROM R1, R2

6.2.2 Coloane-expresii

O facilitate importantă în multe interogări SQL ţine de definirea, pe lângă atributele

tabelelelor, a unor coloane noi, pe baza unor expresii. Clauza AS permite denumirea coloanelor

calculate, sau redenumirea unor coloane ale tabelelor. Să luăm un exemplu banal: Care este, pentru

fiecare produs din factura 1111, codul, cantitatea, preţul unitar şi valoarea fără TVA ?

SELECT CodPr, Cantitate, PretUnit, Cantitate * PretUnit AS ValFaraTVA

FROM LINIIFACT WHERE NrFact = 1111

Rezultatul interogării este prezentat în figura 6.1.

Figura 6.1. Exemplu de coloană calculată

A patra coloana este denumită ValFaraTVA, după cum a fost specificat în clauza AS. Valorile sale sunt

determinate pe baza expresiei Cantitate * PretUnit.

Un alt tip de expresie este cel bazat pe concatenare, adică pe alipirea mai multor constante şi

variabile într-o coloană nouă. Operatorul SQL pentru concatenare este alcătuit din două bare verticale

(||). Spre exemplu, interogarea următoare produce rezultatul din figura 6.2.

SELECT 'Judetul ' || Judet || ' se afla in ' || Regiune AS Exemplu_Concatenare

FROM JUDETE

Figura 6.2. Concatenarea unor literali şi atribute şir de caractere

În PostgreSQL şi Oracle se pot concatena direct, adică fără conversie prealabilă, literali,

atribute-şir de caractere, numerice etc. Alte SGBD-uri, precum DB2, necesită transformarea valorilor

Page 122: BD1 2010 FotacheM Integral

122 Baze de date I

non-şir de caractere (atribute/constante numerice, dată calendaristică etc.), operaţiune realizată prin

funcţia CAST. Spre exemplu, în Oracle şi PostgreSQL interogarea următoare este funcţională:

SELECT 'Factura ' || NrFact || ' a fost emisa pe data ' || DataFact AS Concatenare_Oracle_PgSQL

FROM FACTURI

în timp ce DB2 nu o suportă. Folosind funcţia de conversie CAST, putem redacta următoarea variantă

a interogării care funcţionează şi în PostgreSQL:

SELECT 'Factura ' || CAST (NrFact AS CHAR(8)) || ' a fost emisa pe data ' ||

CAST (DataFact AS VARCHAR(10)) AS Concatenare_DB2_PgSQL

FROM FACTURI

Valorile atributului NrFact sunt transformate şi şiruri de caractere de lungime fixă (8 poziţii), în timp

ce ale DataFact vor fi convertite în şiruri de caractere de lungime variabilă (vorba vine, deoarece

formatul datei este unitar pentru toate liniile rezultatului) de maximum 10 poziţii.

În finalul discuţiei despre coloanele-expresii, mai zăbovim preţ de câteva rânduri la expresiile

de tip dată calendaristică. Modurile în care au fost implementate aceste funcţiuni sunt foarte eterogene

de la SGBD la SGBD. Să presupunem că orice factură trebuie încasată în maximum două săptămâni.

DataFact fiind un atribut de tip DATE, implicit se consideră 14 ca fiind numărul zilelor:

SELECT NrFact AS Factura, DataFact AS Data_Facturare, DataFact + 14 AS Scadenta_Incasare

FROM FACTURI

Specificarea intervalelor calendaristice este o sarcină uşoară în mai toate serverele de baze de

date. Astfel, interogarea următoare este redactată în PostgreSQL şi furnizează acelaşi rezultat în

privinţa datei scadente:

SELECT NrFact AS Factura, DataFact AS Data_Facturare,

DataFact + 14 AS Scadenta_Incasare1,

DataFact + INTERVAL '14 DAYS' AS Scadenta_Incasare2,

DataFact + INTERVAL '2 WEEKS' AS Scadenta_Incasare3

FROM FACTURI

Dacă am presupune că scadenţa ar fi peste două luni de la facturare, interogările ar trebui

modificate astfel în PostgreSQL:

SELECT NrFact AS Factura, DataFact AS Data_Facturare,

DataFact + INTERVAL '2 MONTHS' AS Scadenta_Incasare

FROM FACTURI

sau

SELECT NrFact AS Factura, DataFact AS Data_Facturare, DataFact + INTERVAL '2' MONTH

AS Scadenta_Incasare FROM FACTURI

Ultima variantă funcţionează şi în Oracle care are însă şi o funcţie proprie – ADD_MONTHS:

SELECT NrFact AS Factura, DataFact AS Data_Facturare,

ADD_MONTHS(DataFact,2) AS Scadenta_Incasare

FROM FACTURI

Complicându-ne şi mai zdravăn, dorim să afişăm pentru fiecare factură ce dată va fi peste 1

an, două luni şi 25 de zile de la momentul emiterii.

Soluţia 1 PostgreSQL:

SELECT NrFact AS Factura, DataFact AS Data_Facturare,

DataFact + INTERVAL '1 YEAR' + INTERVAL '2 MONTH'

+ INTERVAL '25 DAY' AS O_Data_Viitoare

Page 123: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 123

FROM FACTURI

Soluţia 2 PostgreSQL:

SELECT NrFact AS Factura, DataFact AS Data_Facturare,

DataFact + INTERVAL '1 YEAR 2 MONTH 25 DAY' AS O_Data_Viitoare

FROM FACTURI

Soluţia 1 Oracle (transformarea anilor în luni):

SELECT NrFact AS Factura, DataFact AS Data_Facturare,

ADD_MONTHS(DataFact,14)+15 AS O_Data_Viitoare

FROM FACTURI

Soluţia 2 Oracle:

SELECT NrFact AS Factura, DataFact AS Data_Facturare,

DataFact + INTERVAL '1-2' YEAR TO MONTH + 25 AS O_Data_Viitoare

FROM FACTURI

În ceea ce priveşte operaţiunile de adunare şi scădere între două date calendaristice, aici

lucrurile se prezintă şi mai diferenţiat. Spre exemplu, interesează intervalul scurs între momentul

curent şi cel al emiterii fiecărei facturi. Interogarea PostgreSQL/Oracle are forma:

SELECT NrFact, DataFact AS Data_Facturare, CURRENT_DATE - DataFact AS Timp_Scurs

FROM FACTURI

Rezultatul scăderii a două date calendaristice conţine numărul zile. În PostgreSQL avem

nevoie de funcţia specială AGE care calculează intervalul dintre două date calendaristice. Interogarea

următoare afişează numărul anilor şi lunilor scurse de la data emiterii fiecărei facturi până în prezent

(vezi figura 6.3):

SELECT NrFact, DataFact, CURRENT_Date AS Astazi,

AGE(CURRENT_DATE, DataFact) AS Interval_Total, EXTRACT (YEAR FROM

AGE(CURRENT_DATE, DataFact)) AS Interval_Ani,

EXTRACT (YEAR FROM AGE( CURRENT_DATE,DataFact)) * 12 +

EXTRACT (MONTH FROM AGE( CURRENT_DATE,DataFact)) AS Interval_Luni

FROM facturi

Figura 6.3 Folosirea funcţiei AGE

Page 124: BD1 2010 FotacheM Integral

124 Baze de date I

6.2.3 Opţiunea ORDER BY

Una din caracteristicile modelului relaţional este că nici ordinea atributelor, nici ordinea

liniilor în relaţii nu prezintă importanţă din punctul de vedere al conţinutului informaţional. În

practică, însă, forma de prezentare a rezultatelor interogării este importantă. Spre exemplu, o listă a

localităţilor este cu mult mai de folos decă se prezintă în ordine alfabetică. Ordonarea liniilor în

rezultatul unei interogări este posibilă prin clauza ORDER BY.

Să se obţină lista localităţilor în ordine alfabetică.

SELECT DISTINCT loc FROM coduri_postale ORDER BY Loc

Rezultatul se prezintă ca în figura 6.4.

Figura 6.4 Localităţile ordonate alfabetic

Aranjarea se face implicit crescător (ASC). Prin opţiunea DESC, ordinea prezentării se inversează. În

plus, se pot specifica mai multe coloane care să servească drept criterii suplimentare de ordonare. La

valori egale ale primului atribut, intră în acţiune criteriul de “balotaj” care este al doilea atribut etc.

Să se obţină, în ordinea descrescătoare a indicativului judeţelor, lista localităţilor în ordinea

crescătoare a denumirii.

SELECT Jud, Loc, CodPost FROM coduri_postale ORDER BY Jud DESC, Loc ASC

Rezultatul este cel din figura 6.5. Ultimul indicativ de judeţ (e vorba de ordine alfabetică) din tabela

CODURI_POSTALE este VS (pentru Vaslui), deci primele localităţi afişate vor fi din acest judeţ; prin

urmare, prima linie a rezultatului se referă la municipiul Bîrlad, iar a doua la municipiul Vaslui.

Figura 6.5. Două criterii de ordonare

Page 125: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 125

6.2.4 Operatorii BETWEEN, LIKE, IN

Pentru formularea predicatului de selecţie, SQL permite utilizarea, pe lângă "clasicii" >, , <,

, =, , şi a altor operatori, dintre care ne vom opri la: BETWEEN (între, cuprins între), LIKE (ca şi, la

fel ca), IN (în), la care se adaugă IS NULL ce va fi prezentat ceva mai târziu.

Operatorul BETWEEN este util pentru definire intervalelor de valori. Ex: Care sunt

facturile emise în perioada 2-5 august 2007 ?

Soluţie PostgreSQL/Oracle – vizualizare rezultat (pgAdmin) - figura 6.6.

SELECT * FROM FACTURI

WHERE DataFact BETWEEN DATE'2007-08-02' AND DATE'2007-08-05'

Figura 6.6. Operator BETWEEN – rezultat PostgreSQL

Soluţia Oracle/PostgreSQL. Specificarea constantelor de tip dată calendaristică fac necesară în

PostgreSQL şi Oracle funcţia TO_DATE.

SELECT * FROM FACTURI WHERE DataFact BETWEEN TO_DATE('02/08/2007','DD/MM/YYYY')

AND TO_DATE('05/08/2007','DD/MM/YYYY') ;

Să se obţină, în ordinea judeţelor, lista localităţilor cu indicativul judeţului cuprins între IS

(Iaşi) şi TM (Timiş).

SELECT Jud, Loc, CodPost FROM coduri_postale WHERE Jud BETWEEN „IS‟ AND „TM‟

ORDER BY Jud, Loc

Operatorul LIKE este util când se doreşte obţinerea unor informaţii din bază, suntem în

postura, oarecum ingrată, de a nu şti cu exactitate cum se numeşte un client sau un produs, sau, pur şi

simplu, unei persoane îi cunoaştem numai unul dintre prenume etc. Sunt numai câteva situaţii pentru

rezovarea cărora a fost gândit operatorul LIKE. Operatorul LIKE permite compararea unui atribut

(expresii) cu un literal utilizând o “mască” construită cu ajutorul specificatorilor multipli % şi _.

Simbolurile procent şi liniuţă_de_jos (underscore, diferită de cratimă sau liniuţa-de-unire) sunt

denumite şi jokeri. Procentul substituie un şir de lungime variabilă, 0 - n caractere, în timp ce liniuţa

un singur caracter.

Care din firmele-client sunt societăţi cu răspundere limitată (SRL-uri) ?

Întrucât, în tabela CLIENTI, denumirea fiecărui client se termină cu forma sa de societate (SA, SRL

etc.), se poate redacta consultarea:

SELECT * FROM CLIENTI WHERE DenCl LIKE '%SRL‟

Rezultatul se prezintă ca în figura 6.7.

Page 126: BD1 2010 FotacheM Integral

126 Baze de date I

Figura 6.7. Clienţii - SRLuri

Care dintre clienţi au numele (fără forma de societate şi spaţiul dinainte) din 8 caractere şi

sunt socieţi pe acţiuni ?

SELECT * FROM CLIENTI WHERE DenCl LIKE '________ SA'

Cele şapte liniuţe (nu se observă, dar sunt şapte), plus spaţiul care le urmează, nu au efect vizibil/im-

presionant pentru baza noastră de date, deoarece în capitolul 1, leneş fiind, am denumit toţi clienţii o

de manieră simplistă - Client 1 SRL, Client 2 SA etc. Dacă am avea mai mulţi clienţi (sau măcar un

“Client 10 SA”), atunci interogarea ar avea cu totul alt farmec.

Ce persoane au numele ce conţine litera S pe a treia poziţie ?

SELECT * FROM PERSOANE WHERE Nume LIKE '__s%'

Figura 6.8. Persoane cu litera “s” pe a treia poziţie a numelui

Rezultatul este vizualizat în figura 6.8. Atenţie, dacă există persoane al căror nume are litera S

majusculă pe a treia poziţie, acestea nu sunt extrase în rezultat ! În asemenea situaţii, soluţia de mai jos

este ceva mai sigură:

SELECT * FROM PERSOANE WHERE Nume LIKE '__s%' OR Nume LIKE '__S%'

În numele căror persoane apare, măcar o dată, litera S (indiferent de poziţie/poziţii) ?

SELECT * FROM PERSOANE WHERE Nume LIKE '%s%' OR Nume LIKE ‟%S%'

Figura 6.9. Persoane al căror nume conţine litera S

Care sunt persoanele ce trebuie felicitate de Sfântul Ion ?

Fireşte am fi tentaţi să redactăm varianta:

SELECT * FROM PERSOANE WHERE UPPER(Prenume) LIKE '%ION%'

Funcţia UPPER face conversia valorilor atributului Prenume în majuscule. Rezultatul se vede cu

ochiul liber în figura 6.10.

Figura 6.10. Tentativă ratată de a extrage “Sfinţii Ioni”

Page 127: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 127

Interogarea nu şi-a atins ţinta, deoarece, pe de o parte, nu au fost extrase persoanele cu prenume ca

Ioan, Ioana, Ioanid, iar, pe de altă parte, au fost eronat extrase şi persoanele cu prenumele Caraion şi

Simion. Prin urmare, cele trei litere - ION trebuie să fie plasate la începutul prenumelui. La acest

şablon se adaugă şi IOAN. Bun, dar dacă pe omul nostru îl cheamă Marius Ion (are două prenume) ?

Cele două şabloane trebuie să se găsească la începutul fiecărui cuvânt din atributul Prenume. Iar dacă

avem în vedere că uneori cele două prenume se despart prin cratimă (Marius-Ion), rezultă o mândreţe

de interogare (răspunsul este prezentat în figura 6.11):

SELECT * FROM PERSOANE

WHERE UPPER(Prenume) LIKE 'ION%' OR UPPER(Prenume) LIKE 'IOAN%'

OR UPPER(Prenume) LIKE '% ION%' OR UPPER(Prenume) LIKE '% IOAN%'

OR UPPER(Prenume) LIKE '%-ION%' OR UPPER(Prenume) LIKE '%-IOAN%'

Figura 6.11. Persoanele ce trebuie felicitate de Sf. Ion

Varianta funcţionează în această formă deopotrivă în PostgreSQL/Oracle. Deşi rare, există

cazuri când printre caracterele căutate în valorile unui atribut şir de caractere se găseşte chiar unul

dintre cele două şabloane, _ sau %. Dacă, spre exemplu, interesează toţi clienţii care conţin simbolul

% în numele lor (s-ar putea ca, la un moment dat, să avem în bază clienţi de genul “Procentul vesel %

SRL”). Soluţia este:

SELECT * FROM CLIENTI WHERE DenCl LIKE '%\%%' ESCAPE '\'

Datorită primului şi ultimului simbol procent, poziţia caracterului căutat (în cazul nostru, chiar %) nu

prezintă importanţă: poate fi prima, ultima sau oricare între prima şi ultima. Rezultatul corect este

obţinut graţie unui caracter declarat prin clauza ESCAPE. Aceste este backslash-ul, dar poate fi

oricare altul. Prin clauza ESCAPE s-a indicat SQL-ului că procentul ce urmează simbolului \ nu este

joker, ci are regim de caracter oarecare, ce trebuie căutat în tabelă ca atare.

Operatorul IN se recomandă atunci când se verifică dacă valoarea unui atribut este

încadrabilă într-o listă de valori dată. În locul folosirii abundente a operatorului OR, este mai elegant

să se apeleze la serviciile operatorului IN. Format general: expresie1 IN (expresie2, expresie3, ...)

Rezultatul evaluării unui predicat ce conţine acest operator va fi adevărat dacă valoarea expresiei1

este egală cu (cel puţin) una din valorile: expresie2, expresie3, ....

Care sunt localităţile din judeţele Iaşi (IS), Vaslui (VS) şi Timiş (TM) ?

Fără operatorul IN:

SELECT DISTINCT loc, jud FROM coduri_postale

WHERE Jud = 'IS' OR Jud = 'VS' OR Jud = 'TM' ORDER BY Jud, Loc

Cu operatorul IN:

SELECT DISTINCT loc, jud FROM coduri_postale

WHERE Jud IN ('IS', 'VS', 'TM') ORDER BY Jud, Loc

Page 128: BD1 2010 FotacheM Integral

128 Baze de date I

Care sunt facturile întocmite pe 1, 3 şi 7 august 2005 ?

SELECT * FROM facturi WHERE DataFact IN ('01/08/2005', '03/08/2005', '07/08/2005')

Fireşte, sintaxa trebuie adaptată în funcţie de felul în care fiecare SGBD lucrează cu atribute şi

constante de tip dată calendaristică.

6.2.5 Theta şi echi-joncţiunea

Dintre tipurile de joncţiune prezentate în capitolul 5, vom insista asupra theta-joncţiunii şi

echi-joncţiunii. SQL nu prezintă clauze sau operatori speciali pentru joncţiune, însă aşa cum am văzut,

o joncţiune este o combinaţie de produs cartezian şi selecţie. În consecinţă, pentru theta-joncţionarea

relaţiilor R1 şi R2 din exemplu 10 al algebrei relaţionale (figura 5.16) se scrie:

SELECT * FROM R1, R2 WHERE R1.A >= R2.E

iar pentru echi-joncţiune:

SELECT * FROM R1, R2 WHERE R1.A = R2.E

Joncţiunea naturală poate fi realizată numai prin specificarea numelor atributelor în clauza

SELECT a frazei de interogare. În standardul SQL92 şi în implementările SQL ale multor SGBD-uri

se poate folosi o variantă mai elegantă, ţinând seama că tot ce înseamnă theta şi echi joncţiune

reprezintă, pentru SQL, INNER JOIN (joncţiune internă). Prin urmare, cele două soluţii de mai sus pot

fi rescrise după cum urmează:

SELECT * FROM R1 INNER JOIN R2 ON R1.A >= R2.E

respectiv

SELECT * FROM R1 INNER JOIN R2 ON R1.A >= R2.E

Reluăm, pentru comparaţie, exemple din algebra relaţională. Exemplul 13 (Să se obţină,

pentru fiecare localitate: codul poştal, denumirea, indicativul şi denumirea judeţului şi regiunea din

care face parte)

Varianta 1 (SQL-1):

SELECT CodPost, Loc, coduri_postale.Jud, Judet, Regiune

FROM coduri_postale, judete

WHERE coduri_postale.Jud = judete.Jud

Varianta 2 (SQL-2):

SELECT CodPost, Loc, coduri_postale.Jud, Judet, Regiune

FROM coduri_postale INNER JOIN judete ON coduri_postale.Jud = judete.Jud

Numai atributul Jud a fost prefixat de numele tabelei din care provine. Prefixarea este

obligatorie atunci când câmpul există în două sau mai multe din tabelele enumerate în clauza FROM.

Exemplul 14 (Care sunt localităţile din Banat ?)

Varianta SQL1:

SELECT CodPost, Loc, coduri_postale.Jud, Judet, Regiune FROM coduri_postale, judete

WHERE coduri_postale.Jud = judete.Jud AND Regiune=‟Banat‟

În clauza WHERE, predicatului de selecţie pentru realizarea joncţiunii i s-a adăugat secvenţa de test a

regiunii.

Varianta SQL2:

Page 129: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 129

SELECT CodPost, Loc, coduri_postale.Jud, Judet, Regiune

FROM coduri_postale INNER JOIN judete ON coduri_postale.Jud = judete.Jud

WHERE Regiune='Banat'

Avantajul celei de-a doua variante ţine de separarea condiţiei ce ţine de regiune de condiţia legată de

joncţiunea propriu-zisă.

Exemplul 15 (În ce zile s-a vândut produsul cu denumirea “Produs 1” ?)

SELECT DISTINCT DataFact

FROM PRODUSE

INNER JOIN LINIIFACT ON PRODUSE.CodPr = LINIIFACT.CodPr

INNER JOIN FACTURI ON LINIIFACT.Nrfact = FACTURI.NrFact

WHERE DenPr = 'Produs 1'

De notat folosirea clauzei DISTINCT pentru eliminarea eventualelor dubluri.

Exemplul 16 (În ce judeţe s-a vândut produsul cu denumirea “Produs 1” în perioada 3-5

august 2005 ?)

SELECT DISTINCT Judet

FROM PRODUSE

INNER JOIN LINIIFACT ON PRODUSE.CodPr = LINIIFACT.CodPr

INNER JOIN FACTURI ON LINIIFACT.Nrfact = FACTURI.NrFact

INNER JOIN CLIENTI ON FACTURI.CodCl = CLIENTI.CodCl

INNER JOIN CODURI_POSTALE ON CLIENTI.CodPost = CODURI_POSTALE.CodPost

INNER JOIN JUDETE ON CODURI_POSTALE.Jud = JUDETE.Jud

WHERE DenPr = 'Produs 1' AND DataFact BETWEEN ‟03-08-2005‟ AND ‟05-08-2005‟

Şi în acest exemplu este recomandată folosirea clauzei DISTINCT.

Exemplul 17 (În ce zile s-au vândut şi produsul cu denumirea “Produs 1” şi cel cu denumirea

“Produs 2” ?)

SELECT DISTINCT DataFact

FROM PRODUSE INNER JOIN LINIIFACT ON PRODUSE.CodPr = LINIIFACT.CodPr

INNER JOIN FACTURI ON LINIIFACT.Nrfact = FACTURI.NrFact

WHERE DenPr = 'Produs 1'

INTERSECT

SELECT DISTINCT DataFact

FROM PRODUSE INNER JOIN LINIIFACT ON PRODUSE.CodPr = LINIIFACT.CodPr

INNER JOIN FACTURI ON LINIIFACT.Nrfact = FACTURI.NrFact

WHERE DenPr = 'Produs 2'

Ar mai fi o soluţie bazată pe joncţiunea tabelei cu ea-însăşi, lucru pe care îl vom discuta în

paragraful următor.

6.2.6 Sinonime locale şi joncţiunea unei tabele cu ea însăşi

Lucrul cu nume lungi de tabele şi atribute are marele avantaj al lejerităţii la citire şi înţelegii

rapide a logicii de derulare a interogării. În schimb, suficienţi informaticieni nu agrează risipa de

caractere (şi, implicit, de timp şi nervi) presupuse de redactările “logoreice”. Ambele părţi au dreptate

Page 130: BD1 2010 FotacheM Integral

130 Baze de date I

în oarecare măsură (sesizaţi sindromul rabinului !). Astfel încât în frazele SELECT tabelelor li se pot

asocia sinonime sau aliasuri mai scurte. Pentru ilustrare, ultima interogare se poate rescrie astfel:

SELECT DISTINCT DataFact

FROM produse P INNER JOIN liniifact LF ON P.CodPr = LF.CodPr

INNER JOIN facturi F ON LF.Nrfact = F.NrFact

WHERE DenPr = 'Produs 1'

INTERSECT

SELECT DISTINCT DataFact

FROM produse P INNER JOIN liniifact LF ON P.CodPr = LF.CodPr

INNER JOIN facturi F ON LF.Nrfact = F.NrFact

WHERE DenPr = 'Produs 2'

Tabelei PRODUSE i s-a asociat sinonimul P, LINIIFACT LF, iar pentru FACTURI F. Sinonimele

prefixează (când este necesar) numele atributelor în clauzele SELECT şi WHERE (eventual ORDER

BY, GROUP BY).

Există situaţii în care utilizarea sinonimelor n-are cu nimic de-a face cu lenea/comoditatea sau

depresiile nervoase. În afara interogărilor corelate pe care le vom discuta într-un capitol viitor, o

operaţiune în care musai trebuie folosite sinonimele este joncţionarea tabelei cu ea-însăşi sau, cum

spunem noi, moldovenii, cu dânsa-însăşi.

Revenim la exemplul 19 din algebra relaţională: Ce facturi au fost emise în aceeaşi zi cu

factura 1120 ?

Este, probabil, cel mai bun exemplu pentru prezentarea subconsultărilor; noi însă ne vom servi acum

de acest caz spre a introduce noul tip de joncţiune.

SELECT F2.NrFact

FROM FACTURI F1 INNER JOIN FACTURI F2 ON F1.DataFact = F2.DataFact

WHERE F1.NrFact=1120

Ca piatră de încercare, revenim la a doua soluţie formulată în algebra relaţională la

exemplul 17 (În ce zile s-au vândut şi produsul cu denumirea “Produs 1” şi cel cu denumirea “Produs

2” ?) .Joncţionăm o instanţă obţinută prin joncţiunea PRODUSE-LINIIFACT-FACTURI (în care

DenPr = ‟Produs 1‟) cu o altă instanţă a aceleaşi combinaţii (în care DenPr = ‟Produs 2‟).

SELECT DISTINCT F1.DataFact

FROM PRODUSE P1 INNER JOIN LINIIFACT LF1 ON P1.CodPr = LF1.CodPr

INNER JOIN FACTURI F1 ON LF1.NrFact = F1.NrFact

INNER JOIN FACTURI F2 ON F1.DataFact=F2.DataFact

INNER JOIN LINIIFACT LF2 ON LF2.NrFact = F2.NrFact

INNER JOIN PRODUSE P2 ON LF2.CodPr = P2.CodPr

WHERE P1.DenPr = 'Produs 1' AND P2.DenPr = 'Produs 2'

6.3 FUNCŢII-AGREGAT: COUNT, SUM, AVG, MIN, MAX

Formatul general al unei fraze SELECT ce conţine funcţii agregat este:

SELECT funcţia-predefinită1, ... , funcţia-predefinităN

FROM listă-tabele WHERE condiţii.

Page 131: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 131

În lipsa opţiunii GROUP BY (vezi paragraful următor), dacă în clauza SELECT este prezentă

o funcţie agregat, rezultatul va conţine o singură linie.

Funcţia COUNTcontorizează valorile nenule ale unei coloane sau numărul de linii dintr-un

rezultat al unei interogări, altfel spus, în rezultatul unei consultări, COUNT numără câte valori diferite

de NULL are o coloană specificată sau câte linii sunt.

Câţi clienţi are firma ?

SELECT COUNT (*) AS NrClienti FROM CLIENTI

Prezenţa asteriscului ca argument al funcţiei COUNT are ca efect numărarea liniilor tabelei CLIENŢI.

Rezultatul este prezentat în figura 6.12.

Figura 6.12. Câţi clienţi are firma ?

Tabela CLIENTI are cheie primară atributul CodCl care nu poate avea valori nule; de aceea,

la fel corectă este şi soluţia:

SELECT COUNT (CodCl) AS NrClienti FROM CLIENTI

Câte linii are produsul cartezian al tabelelor FACTURI şi LINIIFACT ?

SELECT COUNT(*) FROM FACTURI, LINIIFACT

Pentru câţi clienţi se cunoaşte adresa ?

SELECT COUNT (Adresa) AS NrClienti FROM CLIENTI

Rezultatul, 5, putea fi obţinut şi ceva mai complicat, folosind în clauza WHERE operatorul IS NULL

pe care-l tot amânăm pentru capitolul următor.

Câte facturi au fost emise pe 7 august 2005 ?

SELECT COUNT(NrFact) AS NrFacturi FROM FACTURI WHERE DataFact = „07/08/2005‟

Câte facturi au fost emise clienţilor din judeţul Vaslui ?

SELECT COUNT(NrFact) AS NrFacturi

FROM FACTURI F

INNER JOIN CLIENTI C ON C.CodCl=F.CodCl

INNER JOIN CODURI_POSTALE L ON C.CodPost=L.CodPost

INNER JOIN JUDETE J ON L.Jud = J.Jud

WHERE Judet=‟Vaslui‟

Câte localităţi sunt preluate în baza de date ?

Tabela CODURI_POSTALE poate conţine mai multe linii pentru acelaşi oraşe, deoarece oraşele mai

mare prezintă mai multe coduri poştale:

SELECT COUNT(Loc || Jud) AS NrLocalit FROM CODURI_POSTALE

Page 132: BD1 2010 FotacheM Integral

132 Baze de date I

Necazul e că rezultatul obţinut, poate fi incorect, deoarece funcţia COUNT numără toate valorile

nenule. Există însă o clauză prin care o valoare să se ia în calcul o singură dată: DISTINCT. Rezultatul

corect presupune varianta:

SELECT COUNT(DISTINCT Loc || Jud) AS NrLocalit FROM CODURI_POSTALE

Funcţia SUM este una dintre cele mai utilizate funcţii în aplicaţiile economice, deoarece

datele financiar-contabile şi cele ale evidenţei tehnico-operative sunt preponderent cantitative.

Probabil că prea multe explicaţii teoretice despre modul în care operează această funcţie sunt inutile,

aşa încât vom trece în revistă câteva exemple.

Care este valoarea fără TVA a facturii 1111 ?

SELECT SUM(Cantitate * PretUnit) AS ValFaraTVA FROM LINIIFACT WHERE NrFact = 1111

Care este valoarea fără TVA a facturiilor emisei pe 7 august 2007 ?

SELECT SUM(Cantitate * PretUnit) AS ValFaraTVA_17aug2007

FROM LINIIFACT LF INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

WHERE DataFact = DATE'2007-08-07'

Care sunt cele trei valori: fără TVA, TVA şi totală ale facturii 1111 ?

SELECT SUM(Cantitate * PretUnit) AS ValFaraTVA,

SUM(Cantitate * PretUnit * ProcTVA) AS TVA,

SUM(Cantitate * PretUnit + Cantitate * PretUnit * ProcTVA) AS ValTotala

FROM LINIIFACT LF, PRODUSE P WHERE LF.CodPr = p.CodPr AND NrFact = 1111

În condiţiile actuale în care procentul TVA este unic - 19% este corectă şi varianta:

SELECT SUM(Cantitate * PretUnit) AS ValFaraTVA,

SUM(Cantitate * PretUnit * .19) AS TVA,

SUM(Cantitate * PretUnit + Cantitate * PretUnit * .19) AS ValTotala

FROM LINIIFACT WHERE NrFact = 1111

Soluţia propusă este una atemporală, altfel spus, a-guvernamentală şi a-relaxare fiscală. O vizualizare

mai elegantă a rezultatului poate fi realizată în Oracle/PostgreSQL utilizând interogarea următoare:

SELECT 'Pentru factura 1111, valoarea fara TVA este '|| SUM(Cantitate * PretUnit)||

', TVA este '||SUM(Cantitate * PretUnit * ProcTVA)|| ', iar valoarea totala este '||

SUM(Cantitate * PretUnit + Cantitate * PretUnit * ProcTVA) AS Rezultat

FROM LINIIFACT, PRODUSE

WHERE LINIIFACT.CodPr=PRODUSE.CodPr AND NrFact = 1111

La cât se situează cifra vânzărilor pe data de 7 august 2007 ?

SELECT '7 aug. 2007' AS Data, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

WHERE DataFact = DATE„2005-08-07‟

Care este valoarea vânzărilor pentru “Client 1 SRL” ?

SELECT 'Client 1 SRL' AS Client, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

Page 133: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 133

FROM LINIIFACT LF

INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

INNER JOIN CLIENTI C ON F.CodCl = C.CodCl

WHERE DenCl = 'Client 1 SRL'

Care este valoarea medie a preţului (inclusiv TVA) la care a fost vândut “Produs 1” ?

Nu, n-am trecut la funcţia AVG fără să vă fi anunţat din timp. Soluţia se bazează pe raportul dintre

suma valorilor şi cantitatea însumată pentru acest produs.

SELECT SUM(Cantitate*PretUnit*(1+ProcTVA)) / SUM(Cantitate) AS PretUnitMediu

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

WHERE DenPr = 'Produs 1'

Funcţia AVG calculează media aritmetică a unei coloane într-o tabelă oarecare, prin divizarea

sumei valorilor coloanei respective la numărul de valori nenule ale acesteia.

Care este valoarea (fără TVA) medie a produselor vândute în factura 1111 ?

SELECT 'Val. medie (fara TVA) a prod. din fact. 1111' AS Explicatii,

AVG(Cantitate * PretUnit) AS ValMedie

FROM LINIIFACT WHERE NrFact = 1111

Care este media valorilor (cu TVA) la care a fost vândut “Produs 1” ?

SELECT 'Val. medie a vinzarilor prod. Produs 1' AS Explicatii,

ROUND(AVG(Cantitate*PretUnit*(1+ProcTVA)),2) ValTotMedie

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

WHERE DenPr = 'Produs 1'

Adesea, prin aplicarea funcţiei AVG, sau prin formularea unor expresii ce conţin rapoarte între

atribute/constante, se obţin rezultate cu numeroase zecimale. În cazul de faţă, pentru preîntâmpinarea

acestui disconfort vizual şi intelectual, a fost preferată funcţia ROUND ce rotunjeşte media la două

zecimale.

Funcţiile MAX şi MIN sunt deosebit de utile în diverse tipuri de analiză, determinând

valoarea maximă, respectiv minimă, pentru o coloană (atribut). Se pot folosi şi pentru atribute de tip

şir de caractere, caz în care elementul de comparaţie este codul ASCII al caracterelor.

Care este localitatea cu ultima denumire, în ordine alfabetică ?

SELECT MAX(Loc) AS UltimaLoc FROM CODURI_POSTALE

Care este primul client şi ultimul client (în ordinea numelui) din judeţul Iasi ?

SELECT MIN(DenCl) AS Primul_Client, MAX(DenCl) AS Ultimul_Client

FROM CLIENTI C INNER JOIN CODURI_POSTALE L ON C.CodPost = L.CodPost

INNER JOIN JUDETE J ON L.Jud = J.Jud

WHERE Judet = 'Iasi'

Page 134: BD1 2010 FotacheM Integral

134 Baze de date I

Care este cel mai mare preţ unitar (fără TVA) la care a fost vândut Produs 1 ?

SELECT MAX(PretUnit) FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

WHERE DenPr = 'Produs 1'

Din păcate, dacă dorim să aflăm şi în ce factură produsul are preţul unitar maxim, soluţia:

SELECT MAX(PretUnit), NrFact

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

WHERE DenPr = 'Produs 1'

nu funcţionează ! Până la subconsultările din capitolul viitor încercăm o soluţie gen “cârpeală”,

concatenând preţul cu numărul facturii:

SELECT 'Pret maxim='||MAX(TO_CHAR(LF.PretUnit,'99999999') || ', apare in fact.'||NrFact)

AS Produs1

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

WHERE DenPr = 'Produs 1'

Interogarea funcţionează, cel puţin dacă ne luăm după rezultatul din figura 6.13, aşa că n-are de ce să

ne fie jenă de improvizaţie. Rezultatul este incomplet, însă, atunci când preţul maxim apare în două

sau mai multe facturi, deoarece interogarea de mai sus extrage numai una dintre facturi (cea cu

numărul cel mai mare).

Figura 6.13. Preţul maxim pentru Produs 1 şi factura în care apare acest preţ

Care este cel mai mare şi cel mai mic preţ unitar (fără TVA) la care a fost vândut Produs 2 ?

SELECT MAX( 'Pret maxim=' || CAST (PretUnit AS CHAR(15)) || ', factura' ||

CAST (NrFact AS CHAR(10))) AS Max_Pret_Produs_2,

MIN( 'Pret minim=' || CAST (PretUnit AS CHAR(15))

|| ', factura' || CAST (NrFact AS CHAR(10))) AS Min_Pret_Produs_2

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

WHERE DenPr = 'Produs 2'

Care sunt cele mai mari două preţuri unitare (fără TVA) la care a fost vândut Produs 2 ?

SELECT 'Produs 2: '|| MAX('Primul PU: '||TO_CHAR(LF1.PretUnit,'99999999999')||', al doilea PU: '||

TO_CHAR(LF2.PretUnit,'99999999999')|| ' - factura primului:'||LF1.NrFact||

', factura-al doilea:'|| LF2.Nrfact) AS "Cele mai mari doua PretUnit"

FROM LINIIFACT LF1, LINIIFACT LF2, PRODUSE P

WHERE LF1.CodPr = P.CodPr AND DenPr = 'Produs 2' AND LF1.CodPr = LF2.CodPr

AND LF1.PretUnit > LF2.PretUnit

6.4 GRUPAREA TUPLURILOR. GROUP BY ŞI HAVING

Clauza GROUP BY formează grupe (grupuri) de tupluri ale unei relaţii, pe baza valorilor

comune ale unui atribut. În frazele SELECT formulate până în acest paragraf, prin intermediul

Page 135: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 135

WHERE au fost selectate tupluri ale tabelei. Prin asocierea unei clauze HAVING la GROUP BY este

posibilă selectarea anumitor grupuri de tupluri ce îndeplinesc un criteriu, criteriu valabil numai la

nivel de grup (nu şi la nivel de linie).

Clauza GROUP BY

Rezultatul unei fraze SELECT ce conţine această clauză se obţine prin regruparea tuturor

liniilor din tabelele enumerate în FROM, extrăgându-se câte o apariţie pentru fiecare valoare distinctă

a coloanei/grupului de coloane. Formatul general este: SELECT coloană 1, coloană 2, ....,

coloană m FROM tabelă GROUP BY coloană-de-regrupare

Care este valoarea fără TVA a fiecărei facturi emise ?

SELECT NrFact, SUM(Cantitate*PretUnit) as ValFaraTVA

FROM LINIIFACT

GROUP BY NrFact

Rezultatul se obţine prin următoarea succesiune de operaţii:

1. Se constituie un grup pentru fiecare valoare distinctă a atributului NrFact.

2. Se execută funcţia SUM(Cantitate*PretUnit) în cadrul fiecărui grup.

3. Se obţine rezultatul al cărui număr de linii coincide cu valorile distincte ale NrFact.

Care este valoarea totală a vânzărilor pentru fiecare zi în care s-au emis facturi ?

SELECT DataFact, TRUNC(SUM(Cantitate * PretUnit * (1+ProcTVA)),0) AS ValTotala

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

GROUP BY DataFact

Figura 6.14. Vânzările zilnice

După cum puteţi observa, în lipsa clauzei ORDER BY zilele nu sunt dispuse în ordine

crescătoare (sau descrescătoare)

Care sunt, pentru fiecare client, numărul de facturi şi valoarea vânzărilor ?

SELECT DenCl, COUNT(DISTINCT F.NrFact) AS NrFacturilor,

Page 136: BD1 2010 FotacheM Integral

136 Baze de date I

TRUNC(SUM(Cantitate * PretUnit * (1+ProcTVA)),0) AS ValTotala

FROM LINIIFACT LF

INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

INNER JOIN CLIENTI C ON F.CodCl = C.CodCl

GROUP BY DenCl ORDER BY DenCl

Analizând rezultatul din figura 6.15 şi comparându-l cu tabela FACTURI se observă că al cincilea

client ar trebui să aibă cinci facturi, nu trei. Această anomalie aparentă se datorează faptului că

facturile 1122 şi 2122 nu au nici o linie corespondentă în LINIIFACT, astfel încât aceste facturi “cad”

la joncţiune.

Figura 6.15. Numărul facturilor şi valoarea vânzărilor pe clienţi

Care sunt vânzările, cantitativ şi valoric, pentru fiecare produs ?

SELECT DenPr, SUM(Cantitate) AS Cantitativ, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Valoric

FROM LINIIFACT LF

INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

GROUP BY DenPr

ORDER BY DenPr

Rezultatul – vezi figura 6.16.

Figura 6.16. Vânzările cantitative şi valorice pe produse

O informaţie esenţială care lipseşte din figura 6.16 este unitatea de măsură, fără de care nu

putem fi siguri dacă totatul cantitativ se referă la cutii, sticle, pachete, baxuri etc. Din păcate, varianta:

SELECT DenPr, UM, SUM(Cantitate) AS Cantitativ, SUM(Cantitate*PretUnit*(1+ProcTVA)) AS Valoric

FROM LINIIFACT LF

INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

GROUP BY DenPr ORDER BY DenPr

nu funcţionează, deoarece UM apare separat de atributul de grupare, nefiind inclus în funcţia/funcţiile

care se execută la nivelul grupului. Există însă un remediu simplu: includerea în clauza de grupare şi a

atributului UM. Altminteri, gruparea este identică, deoarece DenPr este cheie candidată a tabelei

PRODUSE, lucru observabil în figura 6.17.

Page 137: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 137

SELECT DenPr, UM, SUM(Cantitate) AS Cantitativ, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Valoric

FROM LINIIFACT LF

INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

GROUP BY DenPr, UM ORDER BY 1,2

Figura 6.17. Includerea în rezultat a unităţii de măsură

Care este situaţia vânzărilor pe clienţi şi zile pentru luna septembrie 2007?

Interesează valoarea facturilor emise pe clienţi, şi, pentru fiecare client, cuantumul zilnic al acestora.

Este momentul să folosim o veritabilă grupare după două atribute (spre deosebire de exemplul

precendent, când gruparea celor două atribute a fost mai mult de nevoie, decât de voie).

SELECT DenCl, DataFact AS Data, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact INNER JOIN CLIENTI C

ON F.CodCl = C.CodCl

WHERE EXTRACT(YEAR FROM DataFact)=2007 AND EXTRACT(MONTH FROM DataFact)=9

GROUP BY DenCl, DataFact ORDER BY 1, 2

Figura 6.18. Vânzările zilnice pentru fiecare client

Care este situaţia vânzărilor fiecărui produs pe fiecare regiune ?

SELECT DenPr AS Produs, Regiune, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM LINIIFACT LF

INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

INNER JOIN CLIENTI C ON F.CodCl = C.CodCl

INNER JOIN CODURI_POSTALE L ON C.CodPost=L.CodPost

INNER JOIN JUDETE J ON L.Jud=J.Jud

GROUP BY DenPr, Regiune ORDER BY 1,2

Să se obţină situaţia vânzărilor din septembrie 2007 obţinută pe clienţi şi zile, afişându-se

câte un subtotal la nivel de client şi un total general.

Ideea improvizaţiei este să “alipim”, pentru fiecare client, după vânzările zilnice, câte o linie care să

conţină subtotalul:

Page 138: BD1 2010 FotacheM Integral

138 Baze de date I

SELECT DenCl AS DenumireClient, TO_CHAR(DataFact,'DD-MM-YYYY') AS Data,

TO_CHAR(SUM(Cantitate * PretUnit * (1+ProcTVA)), '999999999999') AS Vinzari

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

INNER JOIN CLIENTI C ON F.CodCl = C.CodCl

WHERE EXTRACT(YEAR FROM DataFact)=2007 AND EXTRACT(MONTH FROM DataFact)=9

GROUP BY DenCl, DataFact

UNION

SELECT DenCl || ' - Subtotal' , NULL,

TO_CHAR(SUM(Cantitate * PretUnit * (1+ProcTVA)), '999999999999')

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

INNER JOIN CLIENTI C ON F.CodCl = C.CodCl

WHERE EXTRACT(YEAR FROM DataFact)=2007 AND EXTRACT(MONTH FROM DataFact)=9

GROUP BY DenCl

UNION

SELECT CHR(255)||'TOTAL GENERAL' , NULL,

TO_CHAR(SUM(Cantitate * PretUnit * (1+ProcTVA)),

'999999999999')

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

WHERE EXTRACT(YEAR FROM DataFact)=2007 AND EXTRACT(MONTH FROM DataFact)=9

Figura 6.19. Rânduri curente, subtotaluri şi total general

Clauza HAVING

Cea mai simplă definiţie: clauza HAVING este WHERE-ul ce operează la nivel de grupuri.

Dacă WHERE acţionează la nivel de tuplu, selectând acele linii care îndeplinesc o condiţie specificată,

HAVING permite specificarea unor condiţii de selecţie care se aplică grupurilor de linii create prin

clauza GROUP BY. Din rezultat sunt eliminate toate grupurile care nu satisfac condiţia specificată.

Formatul general este:

SELECT coloană 1, coloană 2, .... , coloană m

FROM tabelă

GROUP BY coloană-de-regrupare

HAVING caracteristică-de-grup

Page 139: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 139

Care sunt zilele în care s-au întocmit cel puţin trei facturi ?

SELECT DataFact AS "Zi", COUNT(*) AS "Numar facturi"

FROM facturi

GROUP BY DataFact

HAVING COUNT(*) >= 3

Figura 6.20. Zilele în care s-au întocmit trei sau mai multe facturi

Care sunt clienţii pentru care vânzările depăşesc 5 milioane (lei) ?

SELECT DenCl AS Client,

SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

INNER JOIN CLIENTI C ON F.CodCl = C.CodCl

GROUP BY DenCl

HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) > 5000000

ORDER BY 2 DESC

În ce zilele s-au emis mai multe facturi decât pe 2 august 2007 ?

SELECT F1.DataFact AS Zi1, COUNT(DISTINCT F1.NrFact) AS Nr_Facturilor1,

F2.DataFact AS Zi2, COUNT(DISTINCT F2.NrFact) AS Nr_Facturilor2

FROM FACTURI F1, FACTURI F2

WHERE F2.DataFact = TO_DATE('02/08/2007', 'DD/MM/YYYY')

GROUP BY F1.DataFact, F2.DataFact

HAVING COUNT(DISTINCT F1.NrFact) > COUNT(DISTINCT F2.NrFact)

Să zăbovim asupra logicii acestei interogări. Mai întâi se efectuează produsul cartezian pentru două

instanţe (F1 şi F2) ale tabelei FACTURI, eliminându-se pentru F2 linile în care data este alta decât 2

august 2005, adică:

SELECT *

FROM FACTURI F1, FACTURI F2

Page 140: BD1 2010 FotacheM Integral

140 Baze de date I

WHERE F2.DataFact = TO_DATE('02/08/2007', 'DD/MM/YYYY')

ORDER BY F1.DataFact, F2.DataFact

În pasul următor, se constituie grupuri pentru fiecare combinaţie de valori (F1.DataFact, F2.DataFact).

F2.DataFact are aceeaşi valoare, 02/08/2007, prin urmare grupurile se constituie în funcţie de

F1.DataFact. Pentru ca funcţia COUNT să calculeze corect numărul facturilor dintr-o zi, este necesară

clauza DISTINCT.

Diviziunea relaţională

Multe situaţii ce reclamă diviziunea relaţională pot fi soluţionate elegant cu ajutorul clauzelor

GROUP BY şi HAVING. În exemplul următor este vorba de o intersecţie “simulată” printr-un

mecanism apropiat de diviziune.

În ce zile s-au vândut şi produsul cu denumirea “Produs 1” şi cel cu denumirea “Produs 2” ?

SELECT DISTINCT DataFact

FROM LINIIFACT LF INNER JOIN PRODUSE P ON LF.CodPr = P.CodPr

INNER JOIN FACTURI F ON LF.NrFact = F.NrFact

WHERE DenPr IN ('Produs 1', 'Produs 2')

GROUP BY DataFact

HAVING COUNT(DISTINCT LF.CodPr) = 2

Pentru fiecare grup asociat unei zile de vânzări se numără câte produse, dintre Produs 1 şi Produs 2,

au fost facturate. Funcţia COUNT() din clauza HAVING poate “întoarce” maxim valoarea 2, caz în

care data respectivă se încadrează în zilele căutate.

Una din facilităţile SQL ţine de includerea în predicatul clauzei HAVING nu numai a

constantelor şi/sau variabilelor, ci şi a altor consultări (subconsultări). Dar despre această facilitate

vom vorbi într-un paragraf viitor.

6.5 VALORILOR NULE ŞI JONCŢIUNI EXTERNE

6.5.1 Despre NULL-ităţi în SQL

Valoarea NULL a fost introdusă în capitolul 2, la explicarea noţiunilor modelului relaţional,

ca posibilitate de reprezentate a informaţiilor… inexistente sau inaplicabile56

. Raportul Interim 75-02-

09 înaintat ANSI X3 (SPARC Study Group 1975) a delimitat 14 tipuri de date incomplete ce ar putea

apărea ca rezultate ale unor operaţii sau valori ale atributelor, printre care: depăşiri ale capacităţii de

stocare, diviziune la zero, trunchierea şirurilor de caractere, ridicarea lui zero la puterea zero şi alte

erori computaţionale, precum şi valori necunoscute. La popularea bazei de date, unui client nu i se

cunoştea codul fiscal, unor clienţi nu li se ştia adresa sau telefonul. Aceste trei atribute aveau, pe una

sau mai multe linii, valoarea NULL.

Pentru care dintre clienţi nu se cunoaşte adresa ?

Soluţia cvasi-generală se bazează pe utilizarea operatorului IS NULL care extrage toate valorile NULL

pentru un atribut:

56

Vezi şi [Fotache00-2]

Page 141: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 141

SELECT *

FROM CLIENTI

WHERE Adresa IS NULL

Figura 6.21. Clienţii „fără adresă”

Valoarea NULL nu se confundă cu valoarea 0 - pentru atributele numerice - sau cu valoarea „ „

– spaţiu - pentru atributele de tip şir de caractere. Este important de notat că, în vederea identificării

valorilor nule, operatorul are forma IS NULL şi nu =NULL. Prin execuţia frazei SQL:

SELECT * FROM CLIENTI WHERE Adresa = NULL

se va obţine o tabelă cu 0 (zero) linii. Rezultatul evaluării Adresa = NULL va fi întotdeauna FALSE.

Logica operatorilor NOT, AND şi OR este ilustrată în tabelul 6.1.

Tabel 6.1. Rezultatele utilizării operatorilor NOT, AND şi AND

Aplicarea operatorului NOT unei condiţii

NOT TRUE FALSE UNKNOWN

FALSE TRUE UNKNOWN

Combinarea a două expresii utilizând operatorul AND

AND TRUE FALSE UNKNOWN

TRUE TRUE FALSE UNKNOWN

FALSE FALSE FALSE FALSE

UNKNOWN UNKNOWN FALSE UNKNOWN

Combinarea a două expresii utilizând operatorul OR

OR TRUE FALSE UNKNOWN

TRUE TRUE TRUE TRUE

FALSE TRUE FALSE UNKNOWN

UNKNOWN TRUE UNKNOWN UNKNOWN

Deşi baza de date prezentată este alcătuită deja dintr-un număr considerabil de tabele şi

atribute, introducem încă două tabele cu scop colateral temei “vânzări/încasări” – acela de a gestiona o

parte din datele privind drepturile băneşti (salariu negociat şi sporuri) ale angajaţilor firmei. Prima se

numeşte PERSONAL2 şi conţine date generale despre angajaţi: marcă; nume şi prenume; data

naşterii; compartiment; marca şefului (direct); salariu tarifar. A doua, SPORURI, evidenţiază sporurile

lunare primite de fiecare angajat: sporul de vechime (SporVechime), sporul pentru orele lucrate

noaptea (SporNoapte), sporuri pentru condiţii deosebite (SporCD) şi sporuri diverse (AlteSpor).

Cu ajutorul valorii NULL se poate face diferenţa între angajaţii pentru care nu s-a calculat

valoarea sporului pe luna curentă (şi care vor avea în câmpul corespunzător valoarea NULL) şi cei

care nu au dreptul la un asemenea spor, adică valoarea este 0. În continuare este prezentat scriptul de

creare a celor două tabele, iar în figurile 6.22 şi 6.23 conţinuturile acestora.

Listing 6.1. Script de creare a tabelelor SPORURI şi PERSONAL2

DROP TABLE sporuri ;

DROP TABLE personal2 ;

Page 142: BD1 2010 FotacheM Integral

142 Baze de date I

CREATE TABLE personal2 (

marca NUMERIC(5) CONSTRAINT pk_personal2 PRIMARY KEY,

numepren VARCHAR(40),

datanast DATE,

compart VARCHAR(20),

marcasef NUMERIC(5) CONSTRAINT fk_personal2 REFERENCES personal2(marca),

saltarifar NUMERIC(12,2) ) ;

CREATE TABLE sporuri (

an NUMERIC(4),

luna NUMERIC(2),

marca NUMERIC(5) REFERENCES personal2 (marca),

sporvechime NUMERIC(12,2),

spornoapte NUMERIC(12,2),

sporcd NUMERIC(12,2),

altespor NUMERIC(12,2),

PRIMARY KEY (an,luna,marca) ) ;

Datele din cele două tabele pot fi interpretate în maniera următoare: firma s-a înfiinţat în aprilie 2008,

când avea numai trei angajaţi; La momentul curent (luna iulie 2008) sunt 10 angajaţi. Popularea celor

trei tabele este conţinută în listingul 6.2.

Listing 6.2. Script de populare a tabelelor SPORURI şi PERSONAL2

DELETE FROM personal2 ;

INSERT INTO personal2 VALUES (1, 'ANGAJAT 1', '1962-07-01', 'DIRECTIUNE', NULL, 1600) ;

INSERT INTO personal2 VALUES (2, 'ANGAJAT 2', '1977-10-11', 'FINANCIAR', 1, 1450) ;

INSERT INTO personal2 VALUES (3, 'ANGAJAT 3', '1962-08-02', 'MARKETING', 1, 1450) ;

INSERT INTO personal2 VALUES (4, 'ANGAJAT 4', NULL, 'FINANCIAR', 2, 1380) ;

INSERT INTO personal2 VALUES (5, 'ANGAJAT 5', '1965-04-30', 'FINANCIAR', 2, 1420) ;

INSERT INTO personal2 VALUES (6, 'ANGAJAT 6', '1965-11-09', 'FINANCIAR', 5, 1350) ;

INSERT INTO personal2 VALUES (7, 'ANGAJAT 7', NULL, 'FINANCIAR', 5, 1280) ;

INSERT INTO personal2 VALUES (8, 'ANGAJAT 8', '1960-12-31', 'MARKETING', 3, 1290) ;

INSERT INTO personal2 VALUES (9, 'ANGAJAT 9', '1976-02-28', 'MARKETING', 3, 1410) ;

INSERT INTO personal2 VALUES (10, 'ANGAJAT 10', '1972-01-29', 'RESURSE UMANE', 1, 1370) ;

DELETE FROM sporuri ;

INSERT INTO sporuri VALUES (2008, 4, 1, 160, 0, 0, 132) ;

INSERT INTO sporuri VALUES (2008, 4, 2, 130, 45, 0, 70) ;

INSERT INTO sporuri VALUES (2008, 4, 3, 145, 156, 420, 157) ;

INSERT INTO sporuri VALUES (2008, 5, 1, 160, 0, 0, 0) ;

INSERT INTO sporuri VALUES (2008, 5, 2, 80, 45, 0, 70) ;

INSERT INTO sporuri VALUES (2008, 5, 3, 145, 0, 0, 0) ;

INSERT INTO sporuri VALUES (2008, 5, 10, 137, 0, 0, 430) ;

INSERT INTO sporuri VALUES (2008, 6, 1, 160, 0, 0, 0) ;

INSERT INTO sporuri VALUES (2008, 6, 2, 80, 0, 0, 150) ;

INSERT INTO sporuri VALUES (2008, 6, 4, 50, 15, 88, 120) ;

INSERT INTO sporuri VALUES (2008, 6, 5, 130, 15, 0, 20) ;

INSERT INTO sporuri VALUES (2008, 6, 10, 200, 12, 0, 6) ;

INSERT INTO sporuri VALUES (2008, 7, 1, 160, 0, NULL, NULL) ;

Page 143: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 143

INSERT INTO sporuri VALUES (2008, 7, 2, 80, 0, 0, 158) ;

INSERT INTO sporuri VALUES (2008, 7, 3, 145, 0, 0, 0) ;

INSERT INTO sporuri VALUES (2008, 7, 4, 50, 15, NULL, 15) ;

INSERT INTO sporuri VALUES (2008, 7, 5, 130, 0, 0, 120) ;

INSERT INTO sporuri VALUES (2008, 7, 6, 110, 147, 0, 0) ;

INSERT INTO sporuri VALUES (2008, 7, 7, 60, 210, 0, 0) ;

INSERT INTO sporuri VALUES (2008, 7, 8, 130, 0, 15, 0) ;

INSERT INTO sporuri VALUES (2008, 7, 9, 140, 100, 77, 0) ;

INSERT INTO sporuri VALUES (2008, 7, 10, 200, 0, 0, 120) ;

Figura 6.22. Conţinutul tabelei PERSONAL2

Figura 6.23. Conţinutul tabelei SPORURI

Care sunt persoanele şi lunile pentru care nu s-a calculat (nu se cunoaşte) sporul pentru

condiţii deosebite ?

Prin interogarea:

SELECT SPORURI.Marca, NumePren, Compart, An, Luna

FROM PERSONAL2 INNER JOIN SPORURI ON PERSONAL2.Marca=SPORURI.Marca

Page 144: BD1 2010 FotacheM Integral

144 Baze de date I

WHERE SporCD IS NULL

se obţine situaţia din figura 6.24.

Figura 6.24. Angajaţii pentru care nu s-au operat sporurile pentru condiţii deosebite

Care sunt angajaţii şi lunile în care aceştia nu au primit spor pentru condiţii deosebite ?

Atât soluţia cât şi rezultatul sunt sensibil diferite – vezi figura 6.25.

SELECT SPORURI.Marca, NumePren, Compart, An, Luna

FROM PERSONAL2 INNER JOIN SPORURI ON PERSONAL2.Marca=SPORURI.Marca

WHERE SporCD = 0

ORDER BY An, Luna, NumePren

Figura 6.25. Angajaţii şi lunile pentru care SporCD este zero (neNULL)

Care dintre angajaţi sunt născuţi înainte de 1 ianuarie 1970 şi care după această dată ?

Persoanele născute înainte (figura 6.26.):

SELECT * FROM PERSONAL2 WHERE DataNast < '01-01-1970'

Figura 6.26. Persoane născute înainte de 1 ian. 1970

şi după (figura 6.27):

SELECT * FROM PERSONAL2 WHERE DataNast >= '01-01-1970'

Page 145: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 145

Figura 6.27. Persoane născute după 1 ian. 1970

Se observă un lucru curios: dacă reunim mulţimea angajaţilor născuţi înainte de data fixată cu

mulţimea celor născuţi după această dată nu obţinem relaţia iniţială PERSONAL2 (figura 6.28).

SELECT * FROM PERSONAL2 WHERE DataNast < '01-01-1970' UNION

SELECT * FROM PERSONAL2 WHERE DataNast >= '01-01-1970'

Figura 6.28. Reuniunea persoanelor născute înainte de 1 ian. 1970 cu persoanele născute după

această dată

Din tabela obţinută în figura 6.28 lipsesc angajaţii care nu au precizată data naşterii, altfel

spus, persoanele “fără vârstă”. Pentru recompunerea tabelei PERSONAL2, în reuniune mai trebuie

adăugate şi liniile pentru care DataNast IS NULL:

SELECT * FROM PERSONAL2 WHERE DataNast < '01-01-1970' UNION

SELECT * FROM PERSONAL2 WHERE DataNast >= '01-01-1970' UNION

SELECT * FROM PERSONAL2 WHERE DataNast IS NULL

ORDER BY Marca

Acest exemplu este grăitor în privinţa logicii trivalente a modelului relaţional în ceea ce

priveşte valorile nule. În continuare, interesează un alt aspect al NULLităţilor: modul de evaluare a

expresiilor în care unul sau mai mulţi operanzi au valori nule.

Care este totalul sporurilor fiecărui angajat pe luna iulie 2008 ?

SELECT SPORURI.Marca, NumePren, Compart,

SporVechime + SporNoapte + SporCD + AlteSpor AS TotalSporuri

FROM PERSONAL2 INNER JOIN SPORURI ON

PERSONAL2.Marca=SPORURI.Marca AND An = 2008 AND Luna=7

Din păcate, rezultatul este incorect – vezi figura 6.29 – deoarece, din prezentarea conţinutului tabelei

SPORURI (figura 6.23), reiese că, pe luna iulie 2005, ANGAJAT 1 are calculat spor de vechime, iar

ANGAJAT 4 are, pe acceaşi lună şi spor de vechime, şi de noapte şi alte sporuri, iar aceste sporuri nu

au fost luate în calcul la însumare.

Page 146: BD1 2010 FotacheM Integral

146 Baze de date I

Figura 6.29. Rezultatul unei expresii în care cel puţin un operand este NULL

Explicaţia este simplă: dacă, într-o expresie, unul dintre operanzi este NULL, atunci rezultatul

evaluării întregii expresii este NULL. Fac excepţie funcţiile statistice. Dacă, spre exemplu, vrem să

calculăm: Totalul sporurilor de noapte acordate pentru luna iulie 2008, fraza:

SELECT SUM(SporNoapte) AS Total_SporuriNoapte_Luna_7

FROM SPORURI

WHERE An = 2005 AND Luna=7

calculează corect rezultatul.

Revenim la cazul cu probleme. Pentru a asigura corectitudinea totalului, ar trebui ca în

expresie orice valoare nulă să fie considerată zero. Lucru realizabil, deoarece SQL92 este “prevăzut”

cu o funcţie în acest sens – COALESCE:

SELECT s.Marca, NumePren, Compart, COALESCE(SporVechime,0) +

COALESCE (SporNoapte,0) + COALESCE (SporCD,0) +

COALESCE (AlteSpor,0) AS TotalSporuri

FROM PERSONAL2 p

INNER JOIN SPORURI s ON p.Marca=S.Marca AND s.An = 2008 AND s.Luna=7

Sumele obţinute sunt în acest caz cele din figura 6.30.

Figura 6.30. Conversia valorilor nule în zero şi evaluarea corectă a expresiei

Este important de reţinut că funcţiile VALUE, COALESCE, NVL nu se aplică la nivel de

expresie, ci fiecărui operand susceptibil de nulitate. De asemenea, un alt element interesant legat de

valorile nule ţine de conversia în sens invers, dintr-o valoare oarecare, în NULL.

Să se determine totalul sporurilor de noapte pentru luna iulie 2008, dar, în rezultat, să nu fie

luate în calcul valoarea (valorile) 300000 lei.

Page 147: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 147

Soluţia “clasică” este:

SELECT SUM(SporVechime) FROM SPORURI

WHERE An = 2008 AND Luna=7 AND SporVechime <> 300000

O variantă ceva mai elegantă se redactează prin utilizarea funcţiei NULLIF prezentă în SQL92 care, în

interogarea de mai jos, converteşte orice apariţie a valorii în NULL:

SELECT SUM(NULLIF(SporVechime,300000)) FROM SPORURI WHERE An = 2008 AND Luna=7

6.5.2 Joncţiunea externă

Standardul SQL2 introduce operatorii necesari joncţiunii externe:

LEFT OUTER JOIN pentru joncţiune externă la stânga,

RIGHT OUTER JOIN pentru joncţiune externă la dreapta,

FULL OUTER JOIN pentru joncţiune externă totală (în ambele direcţii).

Dacă ne raportăm la exemplul teoretic din algebra realaţională (paragraful 5.3, figura 5.20),

atunci joncţiunile externe la stânga, la dreapta şi totală dintre relaţiile R1 şi R2 se transcriu în

standardul SQL92 astfel:

Joncţiune externă la stânga

SELECT * FROM R1 LEFT OUTER JOIN R2 ON R1.C=R2.C

Joncţiune externă la dreapta

SELECT * FROM R1 RIGHT OUTER JOIN R2 ON R1.C=R2.C

Joncţiune externă totală

SELECT * FROM R1 FULL OUTER JOIN R2 ON R1.C=R2.C

Următorul exemplu este desprins tot din algebra relaţională (exemplul 20): Care sunt

localităţile în care nu avem nici un client ?

SELECT *

FROM CODURI_POSTALE LEFT OUTER JOIN CLIENTI

ON CODURI_POSTALE.CodPost = CLIENTI.CodPost

WHERE CLIENTI.CodPost IS NULL

Figura 6.31. Localităţile în care nu sunt clienţi

Care au fost sporurile de noapte acordate angajaţilor pe lunile mai şi iunie 2008 ?

Situaţia obţinută se referă la două luni. Există însă angajaţi care nu au acest spor pe una sau chiar pe

ambele luni. Cu interogarea:

SELECT AN, Luna, PERSONAL2.Marca, NumePren, SporNoapte

FROM PERSONAL2 INNER JOIN SPORURI

ON PERSONAL2.Marca=SPORURI.Marca AND an=2008 AND Luna IN (5,6)

ORDER BY NumePren, An, Luna

rezultatul arată ca în figura 6.32.

Page 148: BD1 2010 FotacheM Integral

148 Baze de date I

Figura 6.32. Sporurile de noapte pe mai şi iunie – varianta 1 de afişare

Pentru acest exemplu, interesează însă formatul de prezentare din figura 6.33.

Figura 6.33. Sporurile de noapte pe mai şi iunie – rezultatul dorit

Schimbăm alura SELECT-ului:

SELECT AN, Luna, PERSONAL2.Marca, NumePren, SporNoapte

FROM PERSONAL2 LEFT OUTER JOIN SPORURI ON

PERSONAL2.Marca=SPORURI.Marca AND An=2008 AND Luna=5

ORDER BY NumePren, An, Luna

Se obţine astfel tabela din figura 6.34.

Figura 6.34. Sporurile de noapte pe luna mai

Elementul îmbucurător este că în rezultat au fost incluşi toţi angajaţii. Cei care nu erau

angajaţi în această perioadă prezintă valori NULL pentru atributele An şi Luna. Pentru afişarea pe

Page 149: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 149

coloane separate a sporurilor de noapte pe lunile mai şi iunie (ca în figura 6.33) sunt necesare două

joncţiuni externe ale tabelei PERSONAL2 cu două instanţe ale tabelei SPORURI.

SELECT PERSONAL2.Marca, NumePren, S1.SporNoapte AS SporNoapte_Mai,

S2.SporNoapte AS SporNoapte_Iunie

FROM PERSONAL2

LEFT OUTER JOIN SPORURI S1 ON PERSONAL2.Marca=S1.Marca

AND 2008=S1.An AND 5 = S1.Luna

LEFT OUTER JOIN SPORURI S2 ON PERSONAL2.Marca=S2.Marca

AND 2008=S2.An AND 6 = S2.Luna

ORDER BY NumePren

Elementul-cheie îl constituie prezenţa operatorului joncţiunii externe în dreptul atributelor An şi Luna.

Prin această se includ în rezultat şi liniile din tabela PERSONAL2 care nu prezintă corespondenţă

după atributul Marca cu tabela SPORURI pentru cele două luni.

Să se obţină sporurile de noapte pentru al doilea trimestru al anului 2008, atât lunar, cât şi

cumulat.

Sunt necesare trei instanţe ale tabelei SPORURI, fraza SELECT devenind supraponderală:

SELECT PERSONAL2.Marca, NumePren, COALESCE(S1.SporNoapte,0) AS Spor_Noapte_Aprilie,

COALESCE (S2.SporNoapte,0) AS Spor_Noapte_Mai,

COALESCE (S3.SporNoapte,0) AS Spor_Noapte_Iunie,

COALESCE (S1.SporNoapte,0) + COALESCE (S2.SporNoapte,0)+

COALESCE (S3.SporNoapte,0) AS Spor_Noapte_Trim_II

FROM PERSONAL2

LEFT OUTER JOIN SPORURI S1 ON PERSONAL2.Marca=S1.Marca

AND 2008=S1.An AND 4 = S1.Luna

LEFT OUTER JOIN SPORURI S2 ON PERSONAL2.Marca=S2.Marca

AND 2008=S2.An AND 5 = S2.Luna

LEFT OUTER JOIN SPORURI S3 ON PERSONAL2.Marca=S3.Marca

AND 2008=S3.An AND 6 = S3.Luna

ORDER BY NumePren

Rezultat est asemănător celui din figura 6.35.

Figura 6.35. Sporurile de noapte pe trimestrul al II-lea, pe luni şi cumulat

Page 150: BD1 2010 FotacheM Integral

150 Baze de date I

6.6 STRUCTURI ALTERNATIVE: CASE, DECODE

Până la standardul SQL:1999, SQL a fost un limbaj pur neprocedural. Cu toate acestea,

începând cu standardul 92, SQL a introdus facilitatea codării structurilor alternative prin clauza CASE.

Câţi dintre clienţi sunt din Iaşi (cod poştal 6600) şi câţi din afara Iaşului ?

Începem cu o versiune “ajutătoare”. Pentru a scrie în dreptul fiecărui client dacă e din Iaşi sau din

afara Iaşului, se foloseşte interogarea:

SELECT DenCl, CodCl, CLIENTI.CodPost, Loc,

CASE Loc WHEN 'Iasi' THEN ' Din Iasi' ELSE 'Din afara Iasului' END AS Pozitionare

FROM CLIENTI INNER JOIN CODURI_POSTALE ON CLIENTI.CodPost = CODURI_POSTALE.CodPost

Rezultatul arată ca în figura 6.36.

Figura 6.36. Atribut calculat pe baza unei secvenţe alternative

Pentru a răspunde exact la întrebare, se poate redacta o variantă după cum urmează (rezultatul

final este prezentat în figura 6.37):

SELECT CASE Loc WHEN 'Iasi' THEN 'Din Iasi' ELSE 'Din afara Iasului' END AS Pozitionare,

COUNT(*) AS NrClienti

FROM CLIENTI INNER JOIN CODURI_POSTALE ON CLIENTI.CodPost = CODURI_POSTALE.CodPost

GROUP BY CASE Loc WHEN 'Iasi' THEN 'Din Iasi' ELSE 'Din afara Iasului' END

Figura 6.37. Numărul clienţilor ieşeni şi al celor din afara Ieşilor

Câţi angajaţi au primit, pe luna iulie 2008, spor pentru condiţii deosebite şi câţi nu ?

SELECT CASE WHEN SporCD > 0 THEN 'Au spor CD' ELSE 'Nu au spor CD' END,

COUNT(*) AS Nr

FROM SPORURI WHERE An=2008 AND Luna=7

GROUP BY CASE WHEN SporCD > 0 THEN 'Au spor CD' ELSE 'Nu au spor CD' END

Scadenţa fiecărei facturi emise este 20 de zile. Dacă însă data limită cade într-o sâmbătă sau

duminică, atunci scadenţa se mută în lunea următoare. Care sunt zilele scadente în aceste condiţii ?

Soluţia Oracle/PostgreSQL este una simplă, un rol decisiv avându-l, pe lângă structura CASE,

puternica funcţie TO_CHAR:

SELECT NrFact, TO_CHAR(DataFact,'YYYY-MM-DD') AS DataFact,

TO_CHAR(DataFact + 20,'YYYY-MM-DD') AS Scadenta1,

TO_CHAR(DataFact + 20,'DAY') AS NumeZi1,

CASE WHEN TO_CHAR(DataFact + 20,'DAY') = 'SATURDAY'

Page 151: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 151

THEN TO_CHAR(DataFact + 22,'YYYY-MM-DD')

ELSE CASE WHEN TO_CHAR(DataFact + 20,'DAY') = 'SUNDAY'

THEN TO_CHAR(DataFact + 21,'YYYY-MM-DD')

ELSE TO_CHAR(DataFact + 20,'YYYY-MM-DD')

END

END AS Scadenta,

TO_CHAR( CASE WHEN TO_CHAR(DataFact + 20,'DAY') = 'SATURDAY'

THEN DataFact + 22

ELSE CASE WHEN TO_CHAR(DataFact + 20,'DAY') = 'SUNDAY'

THEN DataFact + 21

ELSE DataFact + 20

END

END,

'DAY') AS Zi_Scadenta

FROM FACTURI

Figura 6.38. Scadenţa rectificată a încasării facturilor

Capitolul 7

INTEROGĂRI SQL (2). SUBCONSULTĂRI

Page 152: BD1 2010 FotacheM Integral

152 Baze de date I

Obiective:

Rezolvarea problemelor legate de interogarea BD folosind:

I. Subconsultări în clauza WHERE

II. Subconsultări în clauza HAVING

III. Subconsultări în clauza FROM

IV. Subconsultări sclarare în clauza SELECT

V. Actualizări avansate – UPDATE & SELECT

Rezultate aşteptate:

Formarea deprinderilor pentru redactarea de interogări SQL complexe

Page 153: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 153

7.1 SUBCONSULTĂRI ÎN CLAUZA WHERE. OPERATORUL IN

Una din cele mai importante facilităţi ale SQL constă în includerea unei consultări în alta, pe

două sau mai multe nivele, altfel spus, utilizarea subconsultărilor. Prin subconsultări se obţin tabele

temporare intermediare folosite ca “argumente” ale frazelor SELECT superioare.

7.1.1 Operatorul IN în subconsultări

Operatorul cel mai utilizat în materie de subconsultări este IN pe care l-am întâlnit deja într-un

paragraf precedent într-o cu totul altă ipostază - testarea încadrării valorii unui atribut într-o listă de

constante. Pentru cele ce urmează, domeniul de test al este alcătuit din liniile unei tabele obţinute

printr-o (sub)consultare.

Revenim (pentru a doua oară) la exemplul 19 din algebra relaţională: Ce facturi au fost emise

în aceeaşi zi cu factura 1120 ? Într-un paragraf anterior a fost formulată o soluţie bazată pe joncţiunea

a două instanţe ale tabelei FACTURI. Iată şi o soluţie mai simplă bazată pe subconsultări:

SELECT NrFact FROM facturi WHERE DataFact IN

(SELECT DataFact FROM facturi WHERE NrFact=1120)

Execuţia acestei interogări se derulează în doi timp. Mai întâi, se execută sub-consultarea SELECT

DataFact FROM facturi WHERE NrFact=1120 obţinându-se o tabelă intermediară cu o singură linie şi

o singură coloană (DataFact). În al doilea pas sunt selectate liniile tabelei FACTURI care îndeplinesc

condiţia DataFact = „2007-08-07‟. În rezultat a fost inclusă şi factura de referinţă – 1120. Dacă se

doreşte excluderea acesteia, fraza SELECT se modifică astfel:

SELECT NrFact FROM facturi WHERE DataFact IN

(SELECT DataFact FROM facturi WHERE NrFact=1120)

AND NrFact <> 1120

Ce facturi au fost emise în alte zile decât factura 1120 ?

Acest exemplu necesită folosirea operatorului de negaţie:

SELECT NrFact FROM facturi WHERE DataFact NOT IN

(SELECT DataFact FROM facturi WHERE NrFact=1120)

Care sunt clienţii cărora li s-au trimis facturi în aceeaşi zi în care a fost întocmită factura

1120 ?

SELECT DenCl FROM clienti WHERE CodCl IN

(SELECT CodCl FROM facturi WHERE DataFact IN

(SELECT DataFact FROM facturi WHERE NrFact=1120) )

Rezultatul prezentat în figura 7.1 se obţine folosind trei nivele de interogare (fraza principală, o sub-

consultare şi o sub-sub-consultare).

Figura 7.1. Clienţii pentru care există facturi emise în aceeaşi zi ca 1120

Page 154: BD1 2010 FotacheM Integral

154 Baze de date I

În ce judeţe s-a vândut produsul “Produs 2” ?

Am ales acest exemplu pentru a “vântura”, prin subconsultări, cât mai multe tabele ale bazei:

SELECT Judet FROM judete WHERE Jud IN

(SELECT Jud FROM coduri_postale WHERE CodPost IN

(SELECT CodPost FROM clienti WHERE CodCl IN

(SELECT CodCl FROM facturi WHERE NrFact IN

(SELECT NrFact FROM liniifact WHERE CodPr IN

(SELECT CodPr FROM produse WHERE DenPr = 'Produs 2') ) ) ) )

Revenim la tabela PERSONAL2 din figura 6.22: Câţi subordonaţi direcţi are ANGAJAT 2 ?

La această problemă (la care răspunsul este 2) formulăm, pentru comparaţie, două soluţii. Soluţia

bazată pe joncţiune este:

SELECT COUNT(*) AS NrSubordonati

FROM personal2 SUBORDONATI, personal2 SEFI

WHERE SUBORDONATI.MarcaSef=SEFI.Marca AND SEFI.NumePren='ANGAJAT 2'

A doua soluţia utilizează o subconsultare:

SELECT COUNT(Marca) AS NrSubordonati

FROM personal2

WHERE MarcaSef IN

(SELECT Marca FROM personal2 WHERE NumePren='ANGAJAT 2')

Tot prin subconsultări putem realiza intersecţia şi diferenţa relaţională. Raportându-ne la

intersecţia a două relaţii, R1 şi R2, operaţiunea se poate realoza în SQL şi astfel:

SELECT * FROM R1 WHERE (A,B,C) IN (SELECT C,D,E FROM R2)

Un alt exemplu de intersecţie, În ce zile s-au vândut şi produsul cu denumirea “Produs 1” şi

cel cu denumirea “Produs 2” ?, se poate rezolva şi astfel:

SELECT DISTINCT DataFact

FROM produse p

INNER JOIN liniifact lf ON p.CodPr = lf.CodPr

INNER JOIN facturi f ON lf.Nrfact = f.NrFact

WHERE DenPr = 'Produs 1' AND DataFact IN

(SELECT DISTINCT DataFact FROM produse p INNER JOIN liniifact lf

ON p.CodPr = lf.CodPr INNER JOIN facturi f ON lf.Nrfact = f.NrFact

WHERE DenPr = 'Produs 2')

Cât priveşte diferenţa relaţională, cheia rezolvării este NOT IN. Ce clienţi au cumpărat şi

“Produs 2” şi “Produs 3”, dar nu au cumpărat “Produs 5” ?

SELECT DISTINCT DenCl

FROM produse p

INNER JOIN liniifact lf ON p.CodPr = lf.CodPr

INNER JOIN facturi f ON lf.NrFact = f.NrFact

INNER JOIN clienti c ON f.CodCl = c.CodCl

WHERE DenPr = „Produs 2‟ AND f.CodCl IN

(SELECT CodCl

FROM produse p INNER JOIN liniifact lf ON p.CodPr = lf.CodPr

INNER JOIN facturi f ON lf.Nrfact = f.NrFact

Page 155: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 155

WHERE DenPr = „Produs 3‟)

AND f.CodCl NOT IN

(SELECT CodCl

FROM produse p

INNER JOIN liniifact lf ON p.CodPr = lf.CodPr

INNER JOIN facturi f ON lf.Nrfact = f.NrFact

WHERE DenPr = „Produs 5‟)

Până la apariţia operatorilor LEFT OUTER JOIN, RIGHT OUTER JOIN şi FULL OUTER

JOIN în multe SGBD-uri joncţiunea externă era realizată prin reuniunea liniilor obţinute din echi-

joncţiune cu liniile unei tabele (completate cu zerouri/spaţii pentru atributele celeilalte tabele) ce nu au

corespondent în cealaltă. Joncţiunea externă la stânga a relaţiilor R1 şi R2 prin atributul C ar putea fi

realizată şi astfel:

SELECT * FROM R1 INNER JOIN R2 ON R1.C = R2.C

UNION

SELECT A,B,C, 0, ' ', 0 FROM R1 WHERE C NOT IN

(SELECT C FROM R2)

Tot cu ajutorul operatorului IN (şi NOT IN) se poate aborda şi "problema" diviziunii

relaţionale în SQL. Diviziunea tabelelor din figura 5.21 se realizează astfel:

SELECT X FROM R1 EXCEPT

SELECT DISTINCT R1.X FROM R1, R2 WHERE (R1.X, R2.Y) NOT IN

(SELECT X, Y FROM R1)

Aflaţi clienţii pentru care există cel puţin câte o factură emisă în fiecare zi cu vânzări din

perioada 10-20 septembrie 2007.

Urmăm logica pe care tocmai am prezentat-o.

SELECT DISTINCT DenCl FROM clienti INNER JOIN facturi ON clienti.CodCl=facturi.CodCl

WHERE DataFact BETWEEN DATE'2007-09-10' AND DATE'2007-09-20' AND clienti.CodCl IN

(SELECT CodCl FROM clienti C EXCEPT

SELECT DISTINCT C.CodCl FROM clienti C, facturi F1, facturi F2

WHERE C.CodCl=F1.CodCl AND F1.DataFact BETWEEN DATE'2007-09-10' AND DATE'2007-09-20'

AND F2.DataFact BETWEEN DATE'2007-09-10' AND DATE'2007-09-20'

AND (C.CodCl, F2.DataFact) NOT IN

(SELECT C.CodCl, DataFact FROM clienti C INNER JOIN facturi F ON C.CodCl=F.CodCl

WHERE DataFact BETWEEN DATE'2007-09-10' AND DATE'2007-09-20'

))

7.1.2 Operatori de comparaţie în subconsultări

Până acum subconsultările au fost conectate la fraza SELECT superioară exclusiv prin

operatorul IN. În paragraful următor vom vedea că pentru (sub)interogările comparative, pot fi

întrebuinţaţi ALL, SOME, ANY. Atunci când rezultatul unei subconsultări se concretizează într-o

tabelă cu o singură coloană şi o singură linie, corelarea poate fi făcută cu operatorii de comparaţie

obişnuiţi: = , >, >=, <, <=. Vom ilustra această facilitate prin câteva exemple.

Page 156: BD1 2010 FotacheM Integral

156 Baze de date I

Care este cel mai mare preţ unitar la care s-a vândut un produs ?

SELECT MAX(PretUnit) FROM liniifact

Care este cel mai mare preţ unitar, şi care este produsul, precum şi factura unde se

înregistrează respectivul preţ maxim ?

SELECT NrFact, DenPr, PretUnit FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

WHERE PretUnit = (SELECT MAX(PretUnit) FROM liniifact)

Care sunt cele mai mari două preţuri unitare de vânzare, care sunt produsele şi facturile

pentru care se înregistrează respectivele preţuri maxime ?

SELECT NrFact, DenPr, PretUnit FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

WHERE PretUnit >=

(SELECT MAX(PretUnit) FROM liniifact WHERE PretUnit <

(SELECT MAX(PretUnit) FROM liniifact)

)

Pentru a înţelege mecanismul interogării de mai sus, pornim de la SELECT-ul “cel mai de

jos”. SELECT MAX(PretUnit) FROM liniifact extrage preţul unitar maxim din tabela LINIIFACT.

Subconsultarea superioară, (SELECT MAX(PretUnit) FROM liniifact WHERE PretUnit < ( …ultima

subconsultare… ), determină al doilea preţ unitar din LINIIFACT. SELECT-ul principal afişează toate

preţurile unitare mai mari sau egale cu penultimul.

Care sunt cele mai mari cinci preţuri unitare de vânzare, produsele şi facturile în care apar

cele cinci preţuri maxime ? Aici voiam, de fapt, să ajungem:

SELECT NrFact, DenPr, PretUnit

FROM liniifact INNER JOIN produse ON liniifact.CodPr=PRODUSE.CodPr

WHERE PretUnit >

(SELECT MAX(PretUnit) FROM liniifact WHERE PretUnit <

(SELECT MAX(PretUnit) FROM liniifact WHERE PretUnit <

(SELECT MAX(PretUnit) FROM liniifact WHERE PretUnit <

(SELECT MAX(PretUnit) FROM liniifact WHERE PretUnit <

(SELECT MAX(PretUnit) FROM liniifact WHERE PretUnit <

(SELECT MAX(PretUnit) FROM liniifact)

)

)

)

) )

ORDER BY PretUnit DESC

Celor care nu au reuşit să fie impresionaţi de această ultimă variantă, le sugerez să încerce cu

primele 10, 20 s.a.m.d. preţuri unitare. Revenim însă la soluţia prezentată; iată rezultatul acesteia

(figura 7.2).

În PostgreSQL o clauză de mare ajutor este LIMIT prin care se extrag primele n valori ale

unei expresii dintr-un set de înregistrări:

SELECT NrFact, DenPr, PretUnit

FROM liniifact INNER JOIN produse ON liniifact.CodPr= produse.CodPr

WHERE PretUnit IN

Page 157: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 157

(SELECT DISTINCT PretUnit FROM liniifact ORDER BY PretUnit DESC LIMIT 3)

Figura 7.2. Cele mai mari cinci preţuri unitare

7.1.3 Operatorii ALL, SOME, ANY

Cei trei operatori prezentaţi în acest paragraf sunt grozav de utili în interogările cu iz

“cantitativist” mai pronunţat, deoarece permit utilizarea unui predicat de comparaţie care este aplicat

rezultatului unei sub-consultări, predicat bazat pe unul din operatorii: =, <, >, <=, >=, < > sau #. Dacă,

în cea mai mare parte a cazurilor de până acum, se compara un atribut (sau rezultatul unei

expresii/funcţii) cu o constantă, ALL, SOME şi ANY permit compararea valorii atributului/fun-

cţiei/expresiei cu un set de tupluri (absamblu de linii) extras printr-o subconsultare.

Care sunt produsele vândute la preţuri unitare superioare oricărui preţ unitar la care a fost

vândut „Produs 1‟ ?

SELECT DISTINCT DenPr FROM liniifact INNER JOIN produse ON liniifact.CodPr= produse.CodPr

WHERE PretUnit > ALL

(SELECT DISTINCT PretUnit FROM liniifact INNER JOIN produse

ON liniifact.CodPr= produse.CodPr

WHERE DenPr ='Produs 1')

Ca orice interogare pe două nivele, ostilităţile se derulează în doi paşi. Mai întâi se execută

subconsultarea şi se obţine o tabelă intermediară în care se găsesc toate preţurile unitare la care a fost

vândut, în decursul istoriei, Produs 1 – vezi figura 7.3.

Figura 7.3. Rezultatul subconsultării – preţurile unitare pentru Produs 1

Cum operatorul de conexiune a frazei SELECT principale cu subconsultarea este > ALL, din

joncţiunea tabelelor PRODUSE şi LINIIFACT vor fi extrase numai liniile care au valoarea atributului

PretUnit mai mare decât toate valorile din figura 7.3. Aşa încât rezultatul final se prezintă ca în figura

7.4.

Page 158: BD1 2010 FotacheM Integral

158 Baze de date I

Figura 7.4 Produse cu cel puţin un preţ unitar superior oricărui preţ unitar al Produsului 1

Care sunt produsele vândute la preţuri unitare superioare măcar unui preţ unitar al

„Produsului 1‟ ?

Este genul de situaţii în care se foloseşte SOME sau ANY (au aceeaşi funcţiune).

SELECT DISTINCT DenPr FROM liniifact INNER JOIN produse ON liniifact.CodPr= produse.CodPr

WHERE PretUnit > ANY

(SELECT DISTINCT PretUnit

FROM liniifact INNER JOIN produse ON liniifact.CodPr= produse.CodPr

WHERE DenPr ='Produs 1')

Rezultatul este cel din figura 7.5, deoarece, spre deosebire de ALL, şi ANY (şi SOME) selectează

liniile pentru care preţul unitar este mai mare decât măcar una din valorile obţinute prin subconsultare.

Figura 7.5. Produse cu cel puţin un preţ unitar superior măcar unui preţ unitar al Prod. 1

Operatorul =ANY este echivalent cu IN.

Câţi alţi angajaţi au salariul tarifar egal celui al ANGAJAT 2 ?

Soluţia:

SELECT COUNT(*) - 1 AS Nr FROM personal2 WHERE SalTarifar IN

(SELECT Saltarifar FROM personal2 WHERE NumePren='ANGAJAT 2')

este echivalentă cu:

SELECT COUNT(*) - 1 AS Nr FROM personal2 WHERE SalTarifar =ANY

(SELECT Saltarifar FROM personal2 WHERE NumePren='ANGAJAT 2')

Folosirea unuia din cei trei operatori nu este obligatorie atunci când subconsultarea conţine o

funcţie-agregat (ce întoarce o valoare dintr-un ansamblu de tupluri) - MIN, MAX, COUNT, SUM,

AVG.

Care este ultima factură întocmită (factura cea mai recentă) şi data în care a fost emisă ?

SELECT DataFact, NrFact AS UltimaFactura FROM facturi WHERE NrFact IN

(SELECT MAX(NrFact) FROM facturi)

sau

SELECT DataFact, NrFact AS UltimaFactura FROM facturi WHERE NrFact =

(SELECT MAX(NrFact) FROM facturi)

sau

SELECT DataFact, NrFact AS UltimaFactura FROM FACTURI WHERE NrFact =ANY

(SELECT MAX(NrFact) FROM facturi)

sau

Page 159: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 159

SELECT DataFact, NrFact AS UltimaFactura FROM FACTURI WHERE NrFact =ALL

(SELECT MAX(NrFact) FROM facturi)

Fără funcţia MAX, inteogarea este:

SELECT DataFact, NrFact AS UltimaFactura FROM facturi WHERE NrFact >= ALL

(SELECT NrFact FROM facturi)

7.2 SUBCONSULTĂRI ÎN CLAUZA HAVING

Predicatele incluse în clauza HAVING ale interogărilor de până acum compară o expresie cu o

constantă. În cele ce urmează vom discuta despre includerea în clauza HAVING a subconsultărilor.

Revenim asupra unei probleme formulate anterior:

Care sunt zilele în s-au emis mai multe facturi decât pe 2 august 2005 ?

SELECT DataFact AS Zi, COUNT(NrFact) AS Nr_Facturilor

FROM facturi GROUP BY DataFact HAVING COUNT(NrFact) >

(SELECT COUNT(NrFact) FROM facturi WHERE DataFact = '08/02/2005')

Care este ziua în care s-au emis cele mai multe facturi ?

SELECT DataFact, COUNT(*) AS Nr_Facturilor FROM facturi GROUP BY DataFact

HAVING COUNT(*) >= ALL (SELECT COUNT(*) FROM facturi GROUP BY DataFact)

Subconsultarea calculează numărul de facturi corespunzător fiecărei zile. Predicatul clauzei HAVING

compară numărul de facturi al fiecărei zile cu toate valorile extrase de subconsultare. Se obţine tabela

din figura 7.6.

Figura 7.6. Zilele în care s-au emis cele mai multe facturi

În PostgreSQL ne putem folosi fără ruşine de clauza LIMIT:

SELECT DataFact, COUNT(*) AS Nr_Facturilor

FROM facturi GROUP BY DataFact HAVING COUNT(*) =

(SELECT COUNT(*) FROM facturi GROUP BY DataFact ORDER BY COUNT(*) DESC LIMIT 1)

Care este clientul care a cumpărat cele mai multe produse ?

SELECT DenCl, COUNT(DISTINCT CodPr) AS CiteProduse

FROM clienti C INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.NrFact

GROUP BY DenCl

HAVING COUNT(DISTINCT CodPr) >= ALL

(SELECT COUNT(DISTINCT CodPr)

FROM facturi F INNER JOIN liniifact LF ON F.NrFact=LF.NrFact

GROUP BY CodCl)

Page 160: BD1 2010 FotacheM Integral

160 Baze de date I

Care este compartimentul cu cea mai bună medie a salariilor tarifare ?

Se exclude din discuţie compartimentul DIRECŢIUNE în care apare, singur şi ferice, directorul

general.

SELECT Compart, ROUND(AVG(SalTarifar),2) AS Medie_Sal

FROM personal2 WHERE Compart <> 'DIRECTIUNE' GROUP BY Compart

HAVING AVG(SalTarifar) >= ALL

(SELECT AVG(SalTarifar) FROM personal2 WHERE Compart <> 'DIRECTIUNE'

GROUP BY Compart)

Pentru aducerea aminte a structurilor alternative, putem folosi şi varianta:

SELECT Compart, AVG (CASE WHEN Compart <> 'DIRECTIUNE' THEN SalTarifar ELSE 0

END) AS Medie_Sal

FROM personal2

GROUP BY Compart

HAVING AVG (CASE WHEN Compart <> 'DIRECTIUNE' THEN SalTarifar ELSE 0 END) >= ALL

(SELECT AVG (CASE WHEN Compart <> 'DIRECTIUNE' THEN SalTarifar ELSE 0 END)

FROM personal2 GROUP BY Compart)

Care este judeţul în care berea s-a vândut cel mai bine ?

În tabela PRODUSE există un atribut care reprezintă grupa în care se încadrează produsul

respectiv. Berea este una dintre grupele îndrăgite.

SELECT Judet, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari_Bere

FROM judete J INNER JOIN coduri_postale cp ON j.Jud=cp.Jud

INNER JOIN clienti c ON cp.CodPost=c.CodPost

INNER JOIN facturi f ON c.CodCl=f.CodCl

INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

WHERE Grupa='Bere'

GROUP BY Judet

HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) >= ALL

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA))

FROM judete J INNER JOIN coduri_postale cp ON j.Jud=cp.Jud

INNER JOIN clienti c ON cp.CodPost=c.CodPost

INNER JOIN facturi f ON c.CodCl=f.CodCl

INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

WHERE Grupa='Bere'

GROUP BY Judet)

Care sunt clienţii cu valoarea vânzărilor peste medie ?

SELECT DenCl AS Client, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM clienti c INNER JOIN facturi f ON c.CodCl=f.CodCl

INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

GROUP BY DenCl

Page 161: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 161

HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) >=

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) / COUNT(DISTINCT F.CodCl)

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

)

Figura 7.7. Clienţi cu vânzări peste medie

Extrageţi factura cu valoarea imediat peste cea medie !

SELECT f.NrFact, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Valoare

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

GROUP BY f.NrFact

HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) <= ALL

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA))

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

GROUP BY f.NrFact

HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) >

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) / COUNT(DISTINCT F.NrFact)

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr))

AND SUM(Cantitate * PretUnit * (1+ProcTVA)) >

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) / COUNT(DISTINCT F.NrFact)

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr)

Element de noutate al acestei interogări este includerea, într-o consultare ce prezintă clauza

HAVING, a unei alte subconsultări în care apare, de asemenea, HAVING.

Din nou despre diviziunea relaţională

Revenind la cazul diviziunii relaţionale a R1 : R2 (altfel spus, găsirea tuturor icşilor care sunt

în R1 încombinaţie cu toţi igrecii din R2), se calculează în SQL şi astfel:

SELECT X FROM R1 GROUP BY X HAVING COUNT(Y) = (SELECT COUNT(Y) FROM R2)

Căror clienţi li s-a trimis măcar o factură în toate zilele cu vânzări din perioada 10-20

septembrie 2007 ?

SELECT DenCl AS Client, COUNT(DISTINCT DataFact) AS NrZile

FROM liniifact LF INNER JOIN produse P ON P.CodPr = LF.CodPr

INNER JOIN facturi F ON LF.NrFact = F.NrFact

INNER JOIN clienti C ON F.CodCl = C.CodCl

WHERE DataFact BETWEEN DATE'2007-09-10' AND DATE'2007-09-20'

GROUP BY DenCl

Page 162: BD1 2010 FotacheM Integral

162 Baze de date I

HAVING COUNT(DISTINCT DataFact) =

(SELECT COUNT(DISTINCT DataFact)

FROM facturi

WHERE DataFact BETWEEN DATE'2007-09-10' AND DATE'2007-09-20')

Ce produse au fost vândute tuturor clienţilor ?

SELECT DenPr

FROM facturi f

INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

GROUP BY DenPr

HAVING COUNT(DISTINCT CodCl) = (SELECT COUNT (CodCl) FROM clienti)

Răspunsul este Produs 2.

7.3 SUBCONSULTĂRI ÎN CLAUZA FROM

Posibilitatea de a defini tabele „ad-hoc”, în clauza FROM, este unul dintre cele mai importante

daruri pe care SQL-ul îl oferă utilizatorilor, deopotrivă profesionişti şi neprofesionişti în ale bazelor de

date. Vom intra abrupt în câteva exemple, urmând ca după formularea soluţiilor să discutăm

ingredintele acestui artificiu.

Care sunt valorile facturate şi încasate, precum şi situaţia (“fără nici o încasare”, “încasată

parţial” sau “încasată total”) pentru fiecare factură ?

SELECT VINZARI.Nrfact, Facturat, COALESCE(Incasat,0) AS Incasat,

Facturat - COALESCE(Incasat,0) AS Diferenta,

CASE WHEN COALESCE(Incasat,0) = 0 THEN 'Fara nici o incasare'

WHEN Facturat > COALESCE(Incasat,0) THEN 'Incasata partial'

ELSE 'INCASATA TOTAL'

END AS Situatiune

FROM (

SELECT NrFact, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Facturat

FROM liniifact LF INNER JOIN produse P ON LF.CodPr = P.CodPr

GROUP BY NrFact) VINZARI LEFT OUTER JOIN

(SELECT NrFact, SUM(Transa) AS Incasat FROM incasfact GROUP BY NrFact) INCASARI

ON VINZARI.NrFact = INCASARI.NrFact

În clauza FROM a frazei SELECT principale au fost definite două tabele în toată regula, VINZARI şi

INCASARI. Prima conţine valoarea totală a fiecărei facturi, în timp ce a doua valoarea încasată.

Aceste două tabele sunt joncţionate extern după atributul NrFact.

Să se obţină sporurile de noapte pentru al doilea trimestru al anului 2008, atât lunar, cât şi

cumulat.

SELECT SL1.Marca, NumePren, COALESCE(SL1.SporNoapte,0) AS Spor_Noapte_Aprilie,

COALESCE(SL2.SporNoapte,0) AS Spor_Noapte_Mai, COALESCE(SL3.SporNoapte,0) AS

Page 163: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 163

Spor_Noapte_Iunie, COALESCE(SL1.SporNoapte,0) + COALESCE(SL2.SporNoapte,0)+

COALESCE(SL3.SporNoapte,0) AS Spor_Noapte_Trim_II

FROM

(SELECT personal2.Marca, NumePren, SporNoapte FROM personal2

LEFT OUTER JOIN sporuri ON personal2.Marca= sporuri.Marca AND An=2008

AND Luna =4) SL1

INNER JOIN

(SELECT personal2.Marca, SporNoapte

FROM personal2 LEFT OUTER JOIN sporuri ON personal2.Marca= sporuri.Marca

AND An=2008 AND Luna =5) SL2 ON SL1.Marca=SL2.Marca

INNER JOIN

(SELECT personal2.Marca, SporNoapte FROM personal2 LEFT OUTER JOIN

Sporuri ON personal2.Marca= sporuri.Marca AND An=2008 AND Luna =6) SL3

ON SL1.Marca=SL3.Marca

ORDER BY NumePren

Clauza FROM principală “calculează” trei tabele ce conţin sporurile de noapte ale lunilor

aprilie (SL1), mai (SL2) şi iunie (SL3) 2005 ale fiecărui angajat (indiferent de data angajării

acestuia). Cele trei tabele sunt joncţionate după atributul Marca.

Revenim la diviziunea relaţională din figura 5.21 care poate fi realizată în următoarea variantă

SQL:

SELECT DISTINCT X

FROM R1

WHERE X NOT IN

(SELECT DISTINCT PROD_CART.X

FROM (SELECT DISTINCT R1.X, R2.Y FROM R1,R2) PROD_CART

LEFT OUTER JOIN R1 ON PROD_CART.X=R1.X AND PROD_CART.Y=R1.Y

WHERE R1.X IS NULL)

Putem încerca însă şi ceva mai elegant. Ce ziceţi de soluţia:

SELECT DISTINCT X

FROM

(SELECT X, COUNT(Y) AS Nr FROM R1 GROUP BY X) TEMP1,

(SELECT COUNT(Y) AS Nr FROM R2) TEMP2

WHERE TEMP1.Nr=TEMP2.Nr

Prima tabelă, TEMP1, conţine numărul valorilor lui Y pentru fiecare X din R1, iar a doua, TEMP2

numai numărul total al valorilor lui Y din R2 – figura 7.8.

Figura 7.8. Tabelele intermediare (ad-hoc) TEMP1 şi TEMP2

Page 164: BD1 2010 FotacheM Integral

164 Baze de date I

Care sunt clienţii pentru care există cel puţin câte o factură emisă în fiecare zi cu vânzări din

perioada 10-20 septembrie 2007 ?

Este tot exemplul 22 din algebra relaţională formulat pentru ilustrarea operatorului diviziune.

Soluţia 1:

SELECT DISTINCT DenCl FROM clienti WHERE CodCl NOT IN

(SELECT DISTINCT PROD_CART.CodCl FROM

(SELECT DISTINCT clienti.CodCl, facturi.DataFact

FROM clienti, facturi WHERE DataFact BETWEEN DATE‟2007-09-10‟ AND DATE‟2007-09-20‟)

PROD_CART LEFT OUTER JOIN

(SELECT * FROM facturi WHERE DataFact BETWEEN DATE‟2007-09-10‟ AND

DATE‟2007-09-20‟) FACTURI ON PROD_CART.CodCl= FACTURI.CodCl AND

PROD_CART.DataFact= FACTURI.DataFact

WHERE FACTURI.CodCl IS NULL)

Soluţia 2 (cea preferată):

SELECT DISTINCT DenCl FROM

(SELECT CodCl, COUNT(DISTINCT DataFact) AS Nr FROM facturi

WHERE DataFact BETWEEN DATE‟2007-09-10‟ AND DATE‟2007-09-20‟

GROUP BY CodCl) TEMP1,

(SELECT COUNT(DISTINCT DataFact) AS Nr FROM facturi

WHERE DataFact BETWEEN DATE‟2007-09-10‟ AND DATE‟2007-09-20‟) TEMP2,

CLIENTI

WHERE TEMP1.Nr=TEMP2.Nr AND TEMP1.CodCl=CLIENTI.CodCl

În ce zile s-au vândut şi produsul cu denumirea “Produs 1” şi cel cu denumirea “Produs 2” ?

Soluţia 1:

SELECT DISTINCT DataFact

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

WHERE DenPr IN ('Produs 1', 'Produs 2') AND DataFact NOT IN

(SELECT DISTINCT PROD_CART.DataFact

FROM

(SELECT DISTINCT DataFact, CodPr

FROM FACTURI, PRODUSE

WHERE DenPr IN ('Produs 1', 'Produs 2')) PROD_CART

LEFT OUTER JOIN

(SELECT F.DataFact, LF.CodPr

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact INNER JOIN produse p

ON lf.CodPr=p.CodPr

) TEMP1 ON PROD_CART.DataFact=TEMP1.DataFact

AND PROD_CART.CodPr=TEMP1.CodPr

WHERE TEMP1.DataFact IS NULL)

Soluţia 2:

SELECT DISTINCT DataFact

FROM

(SELECT F.DataFact, COUNT(DISTINCT LF.CodPr) AS Nr

Page 165: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 165

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact INNER JOIN produse p

ON lf.CodPr=p.CodPr

WHERE DenPr IN ('Produs 1', 'Produs 2')

GROUP BY F.DataFact) TEMP1

WHERE Nr = 2

Din nou, soluţia a doua contrastează evident, prin simplitate, cu prima. Tabela TEMP1

conţine, pentru fiecare dată calendaristică, numărul de produse, dintre Produs 1 şi Produs 2 (1 sau 2)

care au fost vândute în ziua respectivă.

Ce facturi conţin măcar produsele din factura 1112 ?

SELECT DISTINCT NrFact

FROM

(SELECT NrFact, COUNT(*) AS NrProd FROM liniifact WHERE CodPr IN

(SELECT CodPr FROM liniifact WHERE NrFact = 1112)

GROUP BY NrFact

) T1,

(SELECT COUNT(CodPr) AS NrP1112 FROM liniifact WHERE NrFact = 1112) T2

WHERE T1.NrProd = T2.NrP1112

T2 conţine numărul produselor din factura 1112. T1 conţine, pentru fiecare factură, câte

produse sunt comune acesteia şi facturii 1112. Prin joncţiunea T1 cu T2 prin condiţia T1.NrProd =

T2.NrP1112, se extrag acele linii din T1 care au acelaşi număr de produse prezente în factura 1112 ca

şi aceasta.

Care sunt clienţii cărora li s-au vândut cel puţin produsele vândute clientului CLIENT 4 ?

SELECT DISTINCT DenCl

FROM (SELECT DenCl, COUNT(*) AS NrProd

FROM clienti C INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.Nrfact

WHERE CodPr IN

(SELECT CodPr

FROM clienti C

INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.Nrfact

WHERE DenCl='Client 4' )

GROUP BY DenCl

) T1,

(SELECT COUNT(CodPr) AS NrProd

FROM clienti C INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.Nrfact

WHERE DenCl='Client 4'

) T2

WHERE T1.NrProd = T2.NrProd

Logica soluţiei este cât se poate se asemănătoare precedentei, doar că T2 conţine numărul

produselor vândute clientului 4, iar în T1 pe fiecare linie se găseşte un client şi numărul produselor

vândute clientului 4 care i-au fost vândute şi (dânsu)lui.

Page 166: BD1 2010 FotacheM Integral

166 Baze de date I

Să se afişeze câte facturi sunt: neîncasate deloc, încasate parţial şi încasate total ?

SELECT CASE

WHEN COALESCE(Incasat,0) = 0 THEN 'Fara nici o incasare'

WHEN Facturat > COALESCE(Incasat,0) THEN 'Incasata partial'

ELSE 'INCASATA TOTAL' END AS Situatiune, COUNT(*) AS Nr

FROM (SELECT NrFact, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Facturat

FROM liniifact LF INNER JOIN produse P ON LF.CodPr = P.CodPr

GROUP BY NrFact) VINZARI

LEFT OUTER JOIN

(SELECT NrFact, SUM(Transa) AS Incasat FROM incasfact

GROUP BY NrFact) INCASARI ON VINZARI.NrFact = INCASARI.NrFact

GROUP BY CASE WHEN COALESCE(Incasat,0) = 0 THEN 'Fara nici o incasare'

WHEN Facturat > COALESCE(Incasat,0) THEN 'Incasata partial'

ELSE 'INCASATA TOTAL' END

Joncţiunea externă la stânga dintre VÎNZĂRI şi ÎNCASĂRI este completată de o structură „alternativă

multiplă” CASE.

Care angajaţi au salariul tarifar egal cu cel al ANGAJAT2 ?

SELECT NumePren FROM

(SELECT NumePren, SalTarifar FROM personal2) TEMP1,

(SELECT Saltarifar FROM personal2 WHERE NumePren='ANGAJAT 2') TEMP2

WHERE TEMP1.SalTarifar = TEMP2.SalTarifar

Care este ziua în care s-au emis cele mai multe facturi ?

Din păcate (sau, din fericire !), soluţia:

SELECT TEMP1.DataFact, TEMP1.Nr

FROM

(SELECT DataFact, COUNT(Nrfact) AS Nr FROM facturi

GROUP BY DataFact) TEMP1,

(SELECT DataFact, COUNT(Nrfact) AS Nr FROM facturi GROUP BY DataFact) TEMP2

WHERE TEMP1.Nr >= ALL (SELECT Nr FROM TEMP2)

nu funcţionează! Aceasta înseamnă că o tabelă definită ad-hoc într-o frază SELECT nu este

recunoscută în subconsultări. Se poate, totuşi, utiliza varianta:

SELECT DataFact, COUNT(*) AS Nr_Facturi FROM facturi GROUP BY DataFact

HAVING COUNT(*) = (SELECT MAX(Nr) FROM (SELECT DataFact, COUNT(NrFact) AS Nr

FROM facturi GROUP BY DataFact) TEMP1)

În subconsultare s-a definit tabela intermediară TEMP1 al cărei atribut Nr este folosit în funcţia MAX din clauza

SELECT.

Care este clientul care a cumpărat cele mai multe produse ?

SELECT DenCl, COUNT(DISTINCT CodPr) AS Nr_Produse

FROM clienti C INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.Nrfact

GROUP BY DenCl

Page 167: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 167

HAVING COUNT(DISTINCT CodPr) = (SELECT MAX(Nr) FROM

(SELECT CodCl, COUNT(DISTINCT CodPr) AS Nr FROM facturi F

INNER JOIN liniifact LF ON F.NrFact=LF.Nrfact

GROUP BY CodCl)

TEMP1)

Care este judeţul în care berea s-a vândut cel mai bine ?

SELECT Judet, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari_Bere

FROM judete J INNER JOIN coduri_postale CP ON J.Jud=CP.Jud

INNER JOIN clienti C ON CP.CodPost=C.CodPost

INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.NrFact

INNER JOIN produse P ON LF.CodPr=P.CodPr

WHERE Grupa='Bere'

GROUP BY Judet

HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) =

(SELECT MAX(Vinzari)

FROM

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM coduri_postale CP INNER JOIN clienti C ON CP.CodPost=C.CodPost

INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.NrFact

INNER JOIN produse P ON LF.CodPr=P.CodPr

WHERE Grupa='Bere'

GROUP BY Jud) TEMP1)

Care sunt clienţii cu valoarea vânzărilor peste medie ?

Soluţia 1:

SELECT DenCl, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM clienti C INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.NrFact

INNER JOIN produse P ON LF.CodPr=P.CodPr

GROUP BY DenCl

HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) >=

(SELECT Vinzari / NrClienti FROM

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

) TEMP1,

(SELECT COUNT(DISTINCT CodCl) AS NrClienti FROM facturi) TEMP2)

Soluţia 2:

SELECT DenCl, VINZ_CL.Vinzari

FROM (SELECT DenCl, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM clienti C INNER JOIN facturi F ON C.CodCl=F.CodCl

INNER JOIN liniifact LF ON F.NrFact=LF.NrFact

INNER JOIN produse P ON LF.CodPr=P.CodPr

GROUP BY DenCl ) VINZ_CL,

Page 168: BD1 2010 FotacheM Integral

168 Baze de date I

(SELECT DISTINCT Vinzari / NrClienti AS Medie_Vinz

FROM (SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

) X, (SELECT COUNT(DISTINCT CodCl) AS NrClienti FROM facturi) Y

) MEDIE_VINZ

WHERE VINZ_CL.Vinzari >= MEDIE_VINZ.Medie_Vinz

Care este factura cu cea mai mică valoare peste cea medie ?

SELECT NrFact, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS ValFact

FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

GROUP BY NrFact HAVING SUM(Cantitate * PretUnit * (1+ProcTVA)) =

(SELECT MIN(ValFact)

FROM (SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) AS ValFact

FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

GROUP BY NrFact) TEMP1

WHERE ValFact >

(SELECT Vinzari / NrFacturi AS ValMedie

FROM (SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) AS

Vinzari, COUNT(DISTINCT NrFact) AS NrFacturi

FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

) TEMP1))

Apelăm şi la o soluţie care să afişeze toate facturile cu valoarea peste medie, în ordinea

crescătoare a acestei valori. Prima factură din rezultat – figura 7.9 - va fi răspunsul la întrebarea

formulată.

SELECT NrFact, ValFact, ValMedie

FROM (SELECT NrFact, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS ValFact

FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

GROUP BY NrFact) TEMP1,

(SELECT ROUND(Vinzari / NrFacturi,0) AS ValMedie

FROM

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari, COUNT(DISTINCT NrFact)

AS NrFacturi FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

) MEDIE1) MEDII

WHERE ValFact > ValMedie

ORDER BY ValFact

Figura 7.9. Facturile cu valori peste medie

Page 169: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 169

Care este clientul cel mai datornic (ce are cel mai mare rest de plată) ?

Fondul problemei este asemănător celei precedente. În interogare obţinem o tabelă ad-hoc cu

diferenţa de încasat pe clienţi (TEMP1), şi o alta ce conţine cea mai mare diferenţă de încasat pentru

un client (TEMP2). Cele două tabele sunt joncţionate după diferenţă şi, în final, pentru a afla

denumirea clientului, adăugăm în joncţiune tabela CLIENŢI.

SELECT DenCl, Vinzari, Incasari, DeIncasat

FROM

(SELECT FACTURAT.CodCl, Vinzari, COALESCE(Incasari, 0) AS Incasari,

Vinzari - COALESCE(Incasari, 0) AS DeIncasat

FROM

(SELECT CodCl, SUM(Cantitate * PretUnit * (1+ProcTVA))

AS Vinzari

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

GROUP BY CodCl) FACTURAT LEFT OUTER JOIN

(SELECT CodCl, SUM(Transa) AS Incasari FROM facturi f INNER JOIN

Incasfact i ON i.NrFact=f.NrFact

GROUP BY CodCl) INCASAT

ON FACTURAT.CodCl=INCASAT.CodCl

) TEMP1 INNER JOIN

(SELECT MAX (Vinzari - COALESCE(Incasari, 0)) AS DifMax

FROM (SELECT CodCl, SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM facturi f INNER JOIN liniifact lf ON f.NrFact=lf.NrFact

INNER JOIN produse p ON lf.CodPr=p.CodPr

GROUP BY CodCl) FACTURAT LEFT OUTER JOIN

(SELECT CodCl, SUM(Transa) AS Incasari FROM facturi f INNER JOIN

Incasfact i ON i.NrFact=f.NrFact GROUP BY CodCl) INCASAT

ON FACTURAT.CodCl=INCASAT.CodCl) TEMP2

ON TEMP1.DeIncasat=TEMP2.DifMax INNER JOIN CLIENTI

ON TEMP1.CodCl=CLIENTI.CodCl

7.4 SUBCONSULTĂRI SCALARE ÎN CLAUZA SELECT

Standardele SQL fac trimitere şi la interogările scalare ce pot fi definite ca fraze SELECT ce

obţin un rezultat alcătuit dintr-o singură linie şi o singură coloană. Utilitatea acestora este vizibilă în

expresii complexe. Deşi este prima oară când pomenim de interogări scalare, le-am folosit de multe ori

până acum în clauzele WHERE şi HAVING. Ceea ce vom parcurge în continuare se referă la

includerea unei interogări scalare în clauza SELECT a unei interogări.

Care sunt totalurile salariilor tarifare şi ale sporurilor pe luna iulie 2008 pentru întreaga

firmă ?

Soluţia „clasică” este:

SELECT SUM(SalTarifar) AS Total_Sal_Tarifar,

SUM ( COALESCE(SporVechime,0) + COALESCE(SporNoapte,0)+

Page 170: BD1 2010 FotacheM Integral

170 Baze de date I

COALESCE(SporCD,0)+COALESCE(AlteSpor,0) ) AS Total_Sporuri_Iulie

FROM personal2 INNER JOIN sporuri ON personal2.Marca= sporuri.Marca

WHERE An=2008 AND Luna=7

Întrucât toate persoanele din tabela PERSONAL au lucrat în luna iulie 2008 (nu a plecat nici

un angajat din organizaţie), se poate formula şi interogarea:

SELECT SUM(SalTarifar) AS Total_Sal_Tarifar,

(SELECT SUM ( COALESCE(SporVechime,0) + COALESCE(SporNoapte,0)+

COALESCE(SporCD,0)+ COALESCE(AlteSpor,0))

FROM sporuri

WHERE An=2008 AND Luna=7) AS Total_Sporuri_Iulie

FROM PERSONAL2

A doua coloană a rezultatului este obţinută printr-o interogare scalară care operează oarecum

independent de fraza SELECT principală, furnizându-i însă o valoare.

Care sunt totalurile vânzărilor şi încasărilor ?

Formulăm o soluţie curioasă PostgreSQL:

SELECT

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari

FROM liniifact lf INNER JOIN produse p ON lf.CodPr=p.CodPr) AS Facturat,

(SELECT SUM (Transa) FROM incasfact) AS Incasat

Fraza SELECT conţine două subconsultări scalare, una care extrage totalul vânzărilor, cealaltă, totalul

încasărilor (Reamintim că în PostgreSQL clauza FROM poate lipsi).

Care este ziua în care s-au emis cele mai multe facturi ?

SELECT DataFact, COUNT(*) AS Nr_Facturi,

(SELECT MAX(NrF) FROM (

SELECT COUNT(*) AS NrF FROM facturi GROUP BY DataFact) T1 ) AS NrMax

FROM facturi

GROUP BY DataFact

HAVING COUNT(*) >= (SELECT MAX(NrF) FROM

(SELECT COUNT(*) AS NrF FROM facturi GROUP BY DataFact) T2 )

Valorile coloanelor Nr_Facturi şi NrMax ale rezultatului din figura 7.10 sunt furnizate de două

subconsultări scalare, una care calculează numărul zilnic al facturilor, iar a doua a doua numărul

maxim de facturi emise într-o zi.

Figura 7.10. Zilele în care s-au întocmit cele mai multe facturi

Care sunt localităţile în care se află sediul fiecărui client ?

Revenim la un exemplu banal, rezolvat atât de simplu prin joncţiune sau subconsultare pentru a

demonstra cât de mult ne putem complica viaţa în SQL (deşi au fost exemple mai convingătoare). Ei

bine, această problemă supărător de simplă poate fi rezolvată şi prin subconsultări scalare:

SELECT DenCl,

(SELECT Loc FROM coduri_postale WHERE CodPost=CLIENTI.CodPost) AS Loc

Page 171: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 171

FROM CLIENTI ORDER BY 2

Figura 7.11 este edificatoare în ceea pe priveşte corectitudinea rezultatului interogării. Unicul

merit al exemplului este, probabil, de a demonstra că o subconsultare scalară poate fi corelată cu tabela

din clauza FROM a frazei principale.

Figura 7.11. Subconsultare scalară corelată cu tabela din fraza principală

Pentru cât la sută dintre clienţi s-au întocmit, zilnic, facturi ?

SELECT DataFact, COUNT(DISTINCT CodCl) AS Nr_Clienti,

(SELECT COUNT(*) FROM clienti) AS Nr_Total_Clienti,

(COUNT(DISTINCT CodCl) * 100) / (SELECT COUNT(*) FROM CLIENTI) AS Procent

FROM facturi

GROUP BY DataFact

Figura 7.12. Procentajul zilnic al clienţilor pentru care există facturi

Pentru a obţine procentul care interesează se împarte rezultatul calculat de funcţia COUNT

pentru fiecare grup (zi calendaristică) la valoarea extrasă de interogarea scalară.

Care este evoluţia zilnică a vânzărilor, raportat la ziua de vânzări anterioară ?

În problema de mai sus diferenţa era calculată între ziua curentă şi ziua precedentă. Astfel încât, toate

zilele de luni erau raportate la zero, deoarece, pentru cea mai mare parte a firmelor, duminica nu se

lucrează. Acum dorim ca diferenţa să fie calculată între vânzările din ziua curentă şi cele din

precedenta zi de vânzări, adică între luni şi vineri s.a.m.d., după cum se observă în figura 7.13.

Page 172: BD1 2010 FotacheM Integral

172 Baze de date I

Figura 7.13. Diferenţele dintre două zile consecutive de vânzări

SELECT DataFact AS Zi,

SUM(Cantitate * PretUnit * (1+ProcTVA)) AS Vinzari_Zi_Curenta,

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA))

FROM facturi F2 INNER JOIN liniifact LF2 ON F2.NrFact=LF2.NrFact

INNER JOIN produse P2 ON LF2.CodPr=P2.CodPr

WHERE DataFact IN (SELECT MAX(DataFact) FROM facturi F3

INNER JOIN liniifact LF3 ON F3.NrFact=LF3.NrFact

INNER JOIN produse P3 ON LF3.CodPr=P3.CodPr

WHERE DataFact < F.DataFact AND (Cantitate * PretUnit * (1+ProcTVA)) > 0)

) AS Vinzari_Zi_Precedenta,

SUM(Cantitate * PretUnit * (1+ProcTVA)) - COALESCE(

(SELECT SUM(Cantitate * PretUnit * (1+ProcTVA))

FROM facturi F2 INNER JOIN liniifact LF2 ON F2.NrFact=LF2.NrFact

INNER JOIN produse P2 ON LF2.CodPr=P2.CodPr

WHERE DataFact IN

(SELECT MAX(DataFact) FROM facturi F3

INNER JOIN liniifact LF3 ON F3.NrFact=LF3.NrFact

INNER JOIN produse P3 ON LF3.CodPr=P3.CodPr

WHERE DataFact < F.DataFact AND (Cantitate * PretUnit * (1+ProcTVA)) > 0)

),0) AS Diferenta

FROM facturi F INNER JOIN liniifact LF ON F.Nrfact=LF.NrFact

INNER JOIN produse P ON LF.CodPr=P.CodPr

GROUP BY DataFact

Artificiul necesar extragerii precendentei zi de vânzări se găseşte în subconsultările din cele

două interogări scalare. Prn joncţiunea instanţelor F3 şi LF3 ale tabelelor FACTURI şi LINIIFACT şi

condiţia DataFact < F.DataFact, vor fi extrase toate liniile din facturi întocmite înaintea datei curente

(F este instanţa principală a tabelei FACTURI – cea care indică ziua curentă). Ca măsură suplimentară

se precauţie se verifică dacă Cantitate * PretUnit * (1+ProcTVA)) > 0 (nu cumva ca să fie vreo zi în care

apare o factură în care liniile să prezinte Cantitate = 0, deşi situaţia aceasta este greu de imaginat).

Page 173: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 173

Dintre zilele de vânzare ce precedă ziua curentă, cea mai apropiată (calendaristic) se extrage prin

funcţia MAX.

Nu ştiu ce ziceţi dumneavoastră, dar mie-mi place…

7.5 ACTUALIZAREA TABELELOR PRIN SUBCONSULTĂRI

Pentru o mai bună priză la public, în cele ce urmează apelăm la ceea ce se numeşte de-

normalizarea bazei de date. În practică, redundanţa datelor este, uneori, condamnată cu jumătate de

gură sau chiar apreciată de mulţi specialişti. Aceasta deoarece un atribut redundant, adăugat unei

tabele, poate duce la evitarea joncţiunilor frecvente, mari consumatoare de resurse. Aplecându-ne

asupra bazei noastre de date, dacă tot ne apucăm de treabă, în tabela FACTURI adaugăm nu mai puţin

de trei atribute:

ValTotală reprezintă valoarea totală (inclusiv TVA) a facturii;

Reduceri: într-o ţară civilizată, ca a noastră, dacă un client plăteşte o factură înainte de

termenul obişnuit (să zicem zece zile), îi putem acorda o reducere de 5% pentru că ne-am

procopsit rapid cu lichidităţi.

Penalităţi: este opusul atributului anterior. Prin contract sau prin lege, dacă un client este mai

reţinut în a plăti facturile la timp, i se pot aplica penalităţi, fără a avea însă certitudinea că le-

am putea încasa vreodată.

Practic, prin aceste noi atribute, urmărirea încasării facturilor se schimbă sensibil. Valoarea de încasat

dintr-o factură este valoarea totală plus penalităţi minus reduceri. Asta e vestea bună. Vestea proastă

ţine de faptul că atributul ValTotală n-ar trebui să se modifice decât la actualizarea tabelei

LINIIFACT, nu ? Altminteri, dacă utilizatorul ar modifica, prin UPDATE sau alt mijloc, acest atribut,

ar apărea un decalaj supărător între liniile din facturi şi valorile acestora. Actualizarea automată a unor

atribute calculate este unul din scopurile declarate ale declanşatoarelor (trigger-elor). Adăugăm cele

trei atribute tabelei FACTURI:

ALTER TABLE FACTURI ADD ValTotala DECIMAL(16,2) ;

ALTER TABLE FACTURI ADD Reduceri DECIMAL(15,2) ;

ALTER TABLE FACTURI ADD Penalizari DECIMAL(15,2) ;

Acum, dacă tot le-am creat, să le “umplem”. Astfel, pentru calculul valorii totale a unei facturi

avem nevoie de o interogare corelată după cum urmează:

UPDATE facturi

SET ValTotala = (

SELECT SUM(Cantitate * PretUnit * (1+ProcTVA))

FROM liniifact LF INNER JOIN produse P ON LF.CodPr=P.CodPr

WHERE NrFact = facturi.NrFact

)

Subconsultarea ia în calcul cantitatea, preţul unitar şi procentul TVA pentru fiecare produs

vândut. Prin corelare se vor lua în considerare numai liniile din LINIIFACT (şi PRODUSE)

corespunzătoare facturii curente (linia curentă din FACTURI).

Cât priveşte atributul Reduceri, instituim următoarea regulă: se acordă o reducere de 5%

pentru toate tranşele unei facturi încasate în termen de 15 zile de la data vânzării.

UPDATE FACTURI SET Reduceri = (

Page 174: BD1 2010 FotacheM Integral

174 Baze de date I

SELECT SUM ( CASE WHEN DataInc <= DataFact + INTERVAL '15' DAY

THEN Transa * 0.05 ELSE 0 END)

FROM incasfact INCF INNER JOIN incasari I ON INCF.CodInc=I.CodInc

INNER JOIN facturi F2 ON INCF.Nrfact=F2.NrFact

WHERE F2.NrFact = facturi.NrFact

)

Complicăm un pic cazul. Acordăm 10% pentru tranşele încasate în mai puţin de 15 zile de la

data vânzării, 9% pentru 16 zile şi 8% pentru 17 zile. Soluţia este:

UPDATE facturi SET Reduceri = (

SELECT SUM ( CASE

WHEN DataInc <= DataFact + INTERVAL '15' DAY

THEN Transa * 0.1

WHEN DataInc <= DataFact + INTERVAL '16' DAY

THEN Transa * 0.09

WHEN DataInc <= DataFact + INTERVAL '17' DAY

THEN Transa * 0.08

ELSE 0 END)

FROM incasfact INCF INNER JOIN incasari I

ON INCF.CodInc=I.CodInc

INNER JOIN facturi F2 ON INCF.Nrfact=F2.NrFact

WHERE F2.NrFact = facturi.NrFact

)

Nu numai modificările pot fi operate utilizând subconsultări, corelate sau nu, ci şi ştergerile.

Spre exemplu, vrem să ştergem din tabela FACTURI liniile care nu au nici un copil în LINIIFACT:

DELETE FROM facturi WHERE NOT EXISTS

(SELECT 1 FROM liniifact WHERE liniifact.NrFact = facturi.NrFact)

O soluţie mai puţin impresionantă utilizează tot o subconsultare, dar ceva mai puţin corelată:

DELETE FROM facturi WHERE NrFact NOT IN

(SELECT DISTINCT NrFact FROM liniifact)

Page 175: BD1 2010 FotacheM Integral

Invăţământ la distanţă – Anul II – Contabilitate şi Informatică de Gestiune - 2009/2010 175