Documente Academic
Documente Profesional
Documente Cultură
administrator
întreprindere
a
administratorul c procesor c administratorul
bazei de date schema aplicaţiilor
DESCRIERE
conceptuală
b
f d
g e
procesor dicţionarul procesor
schema datelor schema
internă externă
l i
sistem
PRELUCRARE
program
de aplicaţii
alocare extern
h h
m
programator
aplicaţie utilizatori
memorii
secundare
Multibaze de date
Diferite departamente ale unei organizaţii mai mari pot folosi diferite
sisteme de gestiune. Cu toate că fiecare sistem este dezvoltat pentru a
satisface nevoile propriului său departament, informaţiile cu care lucrează
pot fi utile şi altor departamente. De aceea, pentru ca organizaţia să
funcţioneze bine, trebuie să existe o modalitate globală da a vedea datele
din fiecare sistem. Există două caracteristici ale unor astfel de sisteme care
fac acceasarea datelor în acest mediu integrat greoaie, uneori chiar
imposibilă:
• autonomie – fiecare SGBD are o autonomie completă, ceea ce
înseamnă că fiecare manager are control deplin asupra sistemului;
• eterogenitate – sistemele pot opera pe diferite platforme, cu diferite
modele de date şi limbajele de interogare.
Una dintre soluţiile folosite pentru a depăşi dificultăţile întâmpinate în
respectarea autonomiei şi a eterogenităţii este utilizarea sistemelor multibaze
de date.
20
Un sistem multibaze de date (SMB) este alcătuit din mai multe sisteme
de baze de date privite integrat, în care se construiesc una sau mai multe
scheme globale pe baza schemelor fiecărei baze de date componente, astfel
încât să se poată realiza accesul uniform şi integrat la fiecare din bazele de
date componente. Fiecare schemă globală este construită pe baza unui model
particular de date. De exemplu, se poate construi o schemă globală ce are la
bază modelul relaţional pentru utilizatorii care sunt familiarizaţi cu acest
model, dar se poate construi o schemă globală bazată pe modelul orientat
obiect pentru utilizatorii bazelor de date orientate obiect.
Pentru o schemă globală dată, un sistem multibaze de date constă din
sistemele componente împreună cu un sistem front-end, care suportă un
singur model de date şi un singur limbaj de interogare. Principalele sarcini ale
sistemului front-end sunt gestionarea schemei globale şi procesarea cererilor
globale.
Un avantaj major al acestui model, faţă de altele, este faptul că o
singură interogare poate accesa date din mai multe baze de date într-un mod
integrat, fără să afecteze nici o aplicaţie care este scrisă utilizând una dintre
bazele de date componente.
Sursa 1 Metadate
Date operationale
Date cu nivel
mare de agregare
Utilitare
Administrator Administrator OLAP
FLUX incarcare date cereri
INTERN
FLUX
Date cu nivel EXTERN
mic de agregare FLUX
ASCENDENT
Date detaliate Utilitare
SGBD Data mining
Sursa n
Date operationale Administrator Depozit de date
Utilitare pentru
Arhive/ date FLUX accesul
backup DESCENDENT utilizatorilor finali
• simplitate;
• independenţa de platformă;
• interfaţa grafică cu utilizatorul;
• acces transparent în reţea;
• standardizare (HTML standard de facto).
Arhitectura de calcul în reţea a sistemului Oracle (NCA Network
Computing Architecture) se axează în principal pe furnizarea
extensibilităţii pentru mediile distribuite. Arhitectura este construită pe
baza tehnologiei CORBA pentru manipularea obiectelor. Este o structură
three tier care se bazează pe utilizarea de:
• cartuşe de software care permit utilizatorilor să adauge
funcţionalităţi individuale în aplicaţii (cartuşele pot fi construite
în Java, C/C++, Visual Basic, SQL şi pot fi conectate la oricare
din cele 3 straturi);
• protocoale deschise şi interfeţe standardizate care permit
comunicarea între cartuşe (distribuite într-o reţea) prin
intermediul unui program magistrală (ICX);
• clienţi extensibili, server-e de aplicaţie, server-e de baze
de date;
• dezvoltarea şi administrarea integrată a cartuşelor.
Un cartus utilizeaza un limbaj de definire a interfetelor (IDL)
pentru a putea fi identificat de alte obiecte intr-un sistem distribuit. De
exemplu, PL/SQL este un astfel de cartus.
HTTP
2.1. Preliminarii
Una dintre cele mai cunoscute abordări ale modelării semantice (cu
siguranţă una dintre cele mai utilizate) este cea bazată pe modelul entitate-
relaţie (E/R). Acesta a fost introdus de către Peter.P. Chen în 1976 şi rafinat
de atunci în diverse moduri de către acesta şi de mulţi alţi cercetători, ca un
model de date conceptual, pentru a uşura proiectarea bazelor de date. Pentru
reprezentarea grafică a modelului sunt utilizate diagramele E/R, care sunt
modele neformalizate pentru reprezentarea unui model, unui sistem din lumea
reală.
Diagramele E/R constituie o tehnică de reprezentare a structurii logice a
bazei de date, într-o manieră grafică. Aceste diagrame oferă un mijloc simplu şi
inteligibil de comunicare a caracteristicilor importante ale designului unei
anumite baze de date.
Diagrama E/R este un model de date conceptual de nivel înalt,
independent de platforma hardware utilizată şi de tipul SGBD-ului. Modelul
este constituit din concepte care descriu structura bazei de date şi tranzacţiile de
regăsire sau reactualzare asociate.
Popularitatea modelului E/R ca modalitate de abordare a proiectării
bazelor de date poate fi atribuită în principal tehnicii de realizare a diagramelor
E/R. Această tehnică, ca şi modelul E/R însuşi, a evoluat de-a lungul timpului
datorită noilor problematici care au apărut în proiectarea bazelor de date.
Studiu de caz
Exemplele din acest capitol se referă la proiectarea unui model de date ce
furnizează informaţii despre prezentări de modă, evenimente care reprezintă
momente importante în lumea caselor de modă.
Vom prezenta modelul de date, restricţiile pe care trebuie să le respecte şi
vom încerca, într-o manieră didactică, să construim diagrama E/R
corespunzătoare. Vom considera, în abordarea iniţială, anumite situaţii care nu
sunt optime, în sensul că pot genera redundanţă, anomalii la reactualizări sau nu
permit rezolvarea anumitor interogări asupra modelului. Vom încerca să arătăm
care sunt deficienţele modelului, situaţiile care le-au generat şi cum pot fi
corectate (parţial sau total) anomaliile respective.
Modelul de date va gestiona informaţii legate de organizarea şi
funcţionarea prezentărilor de modă. Există firme organizatoare care se ocupă de
buna desfăşurare a acestor prezentări. O firmă organizatoare poate fi contactată
prin angajaţi specializaţi pe diferite domenii (financiar, social, publicitate,
securitate, sisteme de iluminare, coregrafie, sisteme de sonorizare, cazare,
primiri/plecări aeroport etc.).
Firme specializate sunt angajate pentru soluţionarea problemelor legate
de securitate, publicitate, asigurări, iar restul problemelor sunt rezolvate cu
salariaţii proprii ai caselor de modă şi a firmei organizatoare. Modalităţile de
securitate, asigurări, publicitate proprii caselor de modă, modelelor sau
agenţiilor de modele nu intră în proiectarea modelului.
Prezentările pot fi sponsorizate, considerându-se doar informaţiile
referitoare la persoane (fizice sau juridice) care au contribuit efectiv la
finanţarea prezentărilor de modă. Mai exact, nu sunt incluşi sponsorii posibili.
La aceste evenimente participă case de modă, care prezintă vestimentaţii
concepute de creatorii casei respective. Creatorii pot fi cei care concep
vestimentaţia (designeri) sau cei care o realizează efectiv, incluzând croitori,
lucrători care se ocupă cu broderia sau cu realizarea diverselor accesorii.
În cadrul prezentării de modă, vestimentaţiile sunt purtate de manechine
care aparţin anumitor agenţii de modele. Casele de modă angajează modele care
să le prezinte vestimentaţiile cu prilejul acestor evenimente. Modelul de date
prezintă şi un istoric al activităţii manechinelor (în cadrul diverselor agenţii).
Casele de modă angajează pentru o prezentare stilişti care se ocupă cu
machiajul şi coafura modelelor.
De asemenea, modelul analizează informaţii legate de localizarea şi
accesarea firmelor de publicitate, a persoanelor de contact din firmele
organizatoare, a caselor de modă, a agenţiilor şi a modelelor, a sponsorilor, a
societăţilor de asigurare şi a firmelor care asigură securitatea evenimentelor.
Modelul de date respectă anumite restricţii de funcţionare.
• Casele de modă pot fi organizatori de prezentări.
• Chiar dacă o casă de modă este organizator, ea apelează la serviciile
unei firme specializate în organizarea unor astfel de evenimente.
60 PROIECTAREA BAZELOR DE DATE
• Un model sau un stilist este angajat (temporar) pentru o prezentare, de
o singură casă de modă.
• O casă de modă poate angaja mai multe modele şi mai mulţi stilişti
pentru o prezentare.
• Orice prezentare de modă are un organizator unic, care poate apela la
serviciile altor instituţii specializate (securitate, asigurări, publicitate).
• Pentru organizator s-a considerat un singur cont în/din care se pot face
plăţile.
• O casă de modă poate avea mai mulţi designeri, dar un designer poate
lucra pentru o singură casă de modă.
• Pentru contactarea unei persoane fizice sau juridice s-a considerat câte
un singur număr de telefon fix, telefon mobil, fax şi o singură adresă de
mail.
• Pentru localizarea unei persoane fizice sau juridice s-a considerat o
singură adresă de bază.
• Sunt luaţi în considerare doar angajaţii firmei de pază şi protecţie care
participă efectiv la asigurarea securităţii prezentărilor de modă.
• Creatorii de accesorii pot fi angajaţi permanenţi ai unei case de modă.
Dacă pentru anumite vestimentaţii sunt necesare accesorii create de
specialişti care nu aparţin casei de modă, aceştia vor fi consideraţi
angajaţi special pentru evenimentul respectiv.
• S-a considerat că proprietarul unei case de modă fie este unic, fie, dacă
sunt mai mulţi, va fi considerat drept proprietar cel care deţine numărul
maxim de acţiuni.
Entitate
Entitatea este un obiect sau un concept, care este semnificativ pentru
modelul real analizat. O entitate poate fi dependentă (slabă), existenţa sa
depinzând de altă entitate sau independentă (tare), caz în care ea nu depinde de
existenţa altei entităţi.
Entitatea poate fi persoană, loc, concept, activitate etc. Prin urmare, ea
poate fi un obiect cu existenţă fizică, reală sau poate fi un obiect cu existenţă
conceptuală, abstractă.
Pentru o analiză didactică a problematicii abordate, construirea
diagramelor E/R, se vor considera şi aspecte ale modelului real analizat, care nu
apar în diagrama finală.
Pentru modelul de date referitor la prezentările de modă, structurile
PUBLICITATE, ORGANIZATOR, PERS_CONTACT, PREZENTARE,
SPONSOR, FIRMA_PUB, CASA_MODA, CREATOR, VESTIMENTATIE,
MODEL, ACCESORIU, LOCALIZARE, AGENTIE, ISTORIC, FIRMA_SEC,
ANGAJAT_SEC,SOCIETATE_ASIG, ANGAJAT_TEMP, INFO_CONTACT,
LOCATIE reprezintă entităţi.
Observaţii
• Entităţile devin tabele în modelele relaţionale.
• În general, entităţile se scriu cu litere mari.
Modelarea entitate-relaţie 61
Relaţie
Relaţia (asocierea) este o comunicare între două sau mai multe entităţi. O
valoare a unei relaţii este o comunicare între valorile entităţilor pe care le leagă.
Relaţia exprimă un raport care există între aceste entităţi. Gradul unei
relaţii este dat de numărul de entităţi participante într-o relaţie (de exemplu,
relaţie binară, ternară, cvadruplă, n-ară).
Existenţa unei relaţii este subordonată existenţei entităţilor pe care le
leagă. Între două entităţi pot exista mai multe relaţii.
O relaţie în care aceeaşi entitate participă mai mult decât o dată în diferite
roluri defineşte o relaţie recursivă. Uneori, aceste relaţii sunt numite unare.
Observaţii
• În modelul relaţional, relaţiile devin tabele speciale sau coloane
speciale care referă chei primare.
• Relaţiile sunt verbe, dar nu orice verb este o relaţie.
• Pentru fiecare relaţie este important să se dea o descriere detaliată.
• În aceeaşi diagramă pot exista relaţii diferite cu acelaşi nume. În acest
caz, ele sunt diferenţiate de către entităţile care sunt asociate prin
relaţia respectivă.
• Pentru fiecare relaţie trebuie stabilită cardinalitatea (maximă şi
minimă) relaţiei, adică numărul de tupluri ce aparţin relaţiei.
Asupra entităţilor participante într-o relaţie pot fi impuse constrângeri
care trebuie să reflecte restricţiile care există în lumea reală asupra relaţiilor. O
clasă de constrângeri, numite constrângeri de cardinalitate, este definită de
numărul de înregistrări posibile pentru fiecare entitate participantă (raport de
cardinalitate). Cel mai întâlnit tip de relaţii este cel binar, iar în acest caz
rapoartele de cardinalitate sunt, în general, one-to-one (1:1), one-to-many (1:n)
sau many-to-many (m:n).
De exemplu, în modelul analizat este definită relaţia organizeaza care
leagă entităţile ORGANIZATOR şi PREZENTARE, relaţie care poate fi scrisă
sub forma ORGANIZATOR_organizeaza_PREZENTARE, pentru a percepe mai
uşor asocierile existente. Relaţia are cardinalitatea minimă 1:1 şi cardinalitatea
maximă 1:n.
Vom prezenta relaţiile modelului de date, dând o descriere completă a
fiecăreia. De fapt, denumirile acestor legături sunt sugestive, reflectând
64 PROIECTAREA BAZELOR DE DATE
conţinutul acestora şi entităţile pe care le leagă. Pentru fiecare relaţie se va
preciza cardinalitatea minimă şi maximă.
FIRMA_PUB_face_PUBLICITATE = relaţie care leagă entităţile FIRMA_PUB
şi PUBLICITATE, reflectând legătura dintre acestea (ce publicitate face o
anumită firmă de publicitate). Ea are cardinalitatea minimă 1:1 (o firmă de
publicitate trebuie să realizeze cel puţin o publicitate şi o publicitate trebuie
făcută de cel puţin o firmă de publicitate) şi cardinalitatea maximă 1:n (o firmă
de publicitate poate asigura mai multe publicităţi, iar o publicitate poate fi
făcută de o singură firmă specializată).
PUBLICITATE_se_face_PREZENTARE = relaţie care leagă entităţile
PUBLICITATE şi PREZENTARE, reflectând legătura dintre acestea (pentru o
prezentare, ce publicitate se face). Relaţia are cardinalitatea minimă 1:1 şi
cardinalitatea maximă 1:n.
ORGANIZATOR_are_PERS_CONTACT = relaţie dintre ORGANIZATOR şi
PERS_CONTACT, reflectând legătura dintre acestea (ce persoane din firma
organizatoare pot fi contactate pentru soluţionarea diverselor probleme). Ea are
cardinalitatea minimă 1:1 şi cardinalitatea maximă 1:n.
FIRMA_SEC_are_ANGAJAT_SEC = relaţie dintre entităţile FIRMA_SEC şi
ANGAJAT_SEC, reflectând legătura dintre acestea (ce angajaţi, implicaţi în
acţiunea de pază a prezentărilor de modă, au firmele de securitate). Ea are
cardinalitatea minimă 1:1 şi cardinalitatea maximă 1:n.
ANGAJAT_SEC_paza_PREZENTARE = relaţie de tip many-to-many dintre
entitatea PREZENTARE şi ANGAJAT_SEC, reflectând legătura dintre acestea
(ce angajaţi ai firmei de securitate asigură buna desfăşurare a prezentărilor de
modă). Ea are cardinalitatea minimă 1:1 şi cardinalitatea maximă m:n.
SOC_ASIG_asigura_PREZENTARE = relaţie dintre entitatea PREZENTARE şi
SOC_ASIG, reflectând legătura dintre acestea (ce societate este angajată pentru
asigurarea diverselor aspecte din cadrul prezentării de modă). Ea are
cardinalitatea minimă 1:1 şi cardinalitatea maximă 1:n.
CASA_MODA_participa_PREZENTARE = relaţie de tip many-to-many dintre
entităţile CASA_MODA şi PREZENTARE (ce case de modă participă la
prezentări). Ea arată şi dacă respectivele case participă, sau nu, ca organizator la
prezentari. Relaţia are cardinalitatea minimă 1:1 şi cardinalitatea maximă m:n.
CASA_MODA_lucreaza_CREATOR = relaţie dintre entităţile CASA_MODA şi
CREATOR (la ce case de modă lucrează creatorii). Relaţia are cardinalitatea
minimă 1:1 şi cardinalitatea maximă 1:n.
CREATOR_creeaza_VESTIMENTATIE = relaţie dintre entităţile CREATOR şi
VESTIMENTATIE (ce creatori realizează vestimentaţiile). Relaţia are
cardinalitatea minimă 1:0 şi cardinalitatea maximă1:n.
Modelarea entitate-relaţie 65
INFO_CONTACT
1
FIRMA_PUB are
1 1
are M(1)
face ORGANIZATOR 1 PERS_CONTACT
1 M(1)
M(1) are
M(1)
PUBLICITATE 1
organizeaza LOCALIZARE
FIRMA SEC LOCATIE
1 se_face
1
are are
M(1) M(1) M(1)
M(1) paza M(1) 1 M(1) finanteaza M(0)
ANGAJAT SEC PREZENTARE SPONSOR
M(1) 1 M(1)
1 asigura participa
SOC ASIG
M(1)
CASA_MODA primeste
prezinta 1
lucreaza
ANGAJAT_TEMP
M(1)
1 CREATOR MODEL 1(0) ISA 1
1 1 1 M(1)
creeaza prezinta
lucreaza_la
M(1) M(0) 1
face VESTIMENTATIE M(1) are AGENTIE
1 1
are refera
M(0) M(0)
M(0) ISTORIC M(1)
ACCESORIU
Entitate-relaţie UML
Tip entitate Clasă
Asociere (relaţie) Asociere (relaţie)
Entitate Obiect
Cardinalitate Multiplicitate
Model conceptual de Diagramă de clase
date
DEPARTAMENT
SARCINA
lucreaza_in conduce
apartine_la
atasat_la
SALARIAT PROIECT
Observaţii:
• Entităţile devin tabele în modelele relaţionale.
• În general, entităţile se scriu cu litere mari.
• Entităţile sunt substantive, dar nu orice substantiv este o entitate.
• Pentru fiecare entitate este obligatoriu să se dea o descriere
detaliată.
• Nu pot exista, în aceeaşi diagramă, două entităţi cu acelaşi nume,
sau o aceeaşi entitate cu nume diferite.
Cheia primară este un identificator unic în cadrul entităţii, făcând
distincţie între valori diferite ale acesteia.
2
Cheia primară:
• trebuie să fie unică şi cunoscută la orice moment;
• trebuie să fie controlată de administratorul bazei;
• trebuie să nu conţină informaţii descriptive, să fie simplă, fără
ambiguităţi;
• să fie stabilă;
• să fie familiară utilizatorului.
SALARIAT PROIECT
cod_salariat nr_proiect
nume M(0) atasat_la M(0) descriere
prenume buget_alocat
data_initiala
sex
functia
salariu 1
1 M(0)
apartine_la
conduce lucreaza_in
1(0) 1 M
DEPARTAMENT SARCINA
cod_departament nr_proiect
nume nr_sarcina
nr_cladire data_inceperii
stare
Diagrama E/R.
4
SALARIAT
cod_salariat PROIECT
nume job_cod nr_proiect
atasat_la descriere
M(0) M(0) buget_alocat
ISA AGENT_TERITORIAL
zona data_initiala
1 1(0) comision 1
functia
PROGRAMATOR apartine_la
ISA limbaj
nivel 1(0)
1 1(0)
M(1)
SARCINA
1 M(0) 1(0) nr_proiect
nr_sarcina
data_inceperii
conduce lucreaza_in stare
casatorit
1(0) 1
DEPARTAMENT
cod_departament
nume
nr_cladire
Diagrama E/R.
6
CITITOR
CARTE M(1) M(0)
codec#
codel# imprumuta nume
titlu dep
autor
pret
nrex
1 DOMENIU
M(0)
coded#
apartine intdom
7
FRAME
SALARIAT nr_publicatie#
cod_salariat# nr_capitol#
nume job tip nr_frame#
M(1) M(0)
CAPITOL
TEHNOREDACTOR 1 M(0) nr_publicatie#
ISA tip_editor nr_capitol#
1 1(0)
1 realizează
M(1)
cuprinde
1
ISA
1 1(0) REDACTOR_SEF 1 M(0) PUBLICATIE
experienta coordoneaza nr_publicatie#
stil
limba
SANTIER
CONTRACTANT nr_santier#
cod_contractant# specialitate
adresa sef
telefon
cont 1
executa
banca
tip_contractant
M(0)
executa
SUBANTREPENOR LUCRARE
nume 1 M( cod_obiectiv#
nume_adm 1) cod_lucrare#
functie_adm adresa
1 ISA 1(0) M(1)
INVESTITOR necesita
tip_investitor
1
investeste_in
PERS_FIZICA OBIECTIV_
nume INVESTITIE
prenume 1 M(1) cod_obiectiv#
ISA
bi denumire
1 ISA adresa
1(0)
atasat_la
PERS_JURIDICA 1
tip_juridic
incheie CONTRACT
ISA nume
nr_contract#
functie
1 M(1) tip_contract
data_avans
petrecut_in EVENIMENT
PUNCT
gasita_in
stantata_cu
ARTICOL publicata MONEDA STANTA
inclusa_in
pastrata_la
TEZAUR MUZEU
Completaţi cardinalitatea!
SCOALA CLIENT
cod_scoala# cod_client#
INSTRUCTOR EXAMEN
cod_instructor# cod_examen#
EXAMINATOR
MASINA cod_examinator#
cod_masina#
ECHIPA
M(1) sustine M(1) SPONSOR
Cod_echipa#
Nume Cod_sponsor#
Oras Nume
joaca
M(1)
MECI
Tara#
Nr_etapa#
Cod_meci#
M(1)
apartine_de
ETAPA
Tara
Nr_etapa
M(1)
atasata_la
CAMPIONAT
Tara#
11
Modelul relaţional
Modelul relaţional a fost conceput şi dezvoltat de E.F. Codd. El este
un model formal de organizare conceptuală a datelor, destinat reprezentării
legăturilor dintre date, bazat pe teoria matematică a relaţiilor. Modelul
relaţional este alcătuit numai din relaţii şi prin urmare, orice interogare
asupra bazei de date este tot o relaţie.
Calităţi:
• este simplu;
• riguros din punct de vedere matematic;
• nu este orientat spre sistemul de calcul.
Modalităţi pentru definirea unui SGBD relaţional:
• prezentarea datelor în tabele supuse anumitor operaţii de tip
proiecţie, selecţie, reuniune, compunere, intersecţie etc.
• un sistem de baze de date ce suportă un limbaj de tip SQL –
Structured Query Language;
• un sistem de baze de date care respectă principiile modelului
relaţional introdus de E.F. Codd.
Caracteristicile unui model relaţional:
• structura relaţională a datelor;
• operatorii modelului relaţional;
• regulile de integritate care guvernează folosirea cheilor în model.
Aceste trei elemente corespund celor trei componente ale ingineriei
software: informaţie, proces, integritate.
Structura datelor
Definirea noţiunilor de domeniu, relaţie, schemă relaţională, valoare
null şi tabel vizualizare (view).
Conceptele utilizate pentru a descrie formal, uzual sau fizic
elementele de bază ale organizării datelor sunt date în următorul tabel:
Formal Uzual Fizic
relaţie tablou fişier
tuplu linie înregistrare
atribut coloană câmp
domeniu tip de dată tip de dată
12
Fie schemele relaţionale R1(P1, S1) şi R2(S1, S2), unde P1 este cheie
primară pentru R1, S1 este cheie secundară pentru R1, iar S1 este cheie
primară pentru R2. În acest caz, vom spune că S1 este cheie externă (cheie
străină) pentru R1.
Modelul relaţional respectă trei reguli de integritate structurală.
Regula 1 – unicitatea cheii. Cheia primară trebuie să fie unică şi
minimală.
Regula 2 – integritatea entităţii. Atributele cheii primare trebuie
să fie diferite de valoarea null.
Regula 3 – integritatea referirii. O cheie externă trebuie să fie ori
null în întregime, ori să corespundă unei valori a cheii primare
asociate.
Proiectarea modelului relaţional (exemple curs!)
Transformarea entităţilor
Entităţile independente devin tabele independente. Cheia
primară nu conţine chei externe.
Entităţile dependente devin tabele dependente. Cheia primară a
entităţilor dependente conţine cheia primară a entităţii de care
depinde (cheie externă) plus unul sau mai multe atribute
adiţionale.
Subentităţile devin subtabele. Cheia externă se referă la
supertabel, iar cheia primară este această cheie externă (cheia
primară a subentităţii PROGRAMATOR este cod_salariat care
este o cheie externă).
Transformarea relaţiilor
Relaţiile 1:1 şi 1:n devin chei externe. Relaţia conduce devine
coloană în tabelul DEPARTAMENT, iar relaţia lucreaza_in
devine coloană în tabelul SALARIAT. Simbolul „ד indică
plasamentul cheii externe, iar simbolul „ד exprimă faptul că
această cheie externă este conţinută în cheia primară. Relaţia 1:1
plasează cheia externă în tabelul cu mai puţine linii.
Relaţia m:n devine un tabel special, numit tabel asociativ, care
are două chei externe pentru cele două tabele asociate. Cheia
primară este compunerea acestor două chei externe plus
eventuale coloane adiţionale. Tabelul se desenează punctat.
15
Transformarea atributelor
Un atribut singular devine o coloană.
Atributele multiple devin tabele dependendente ce conţin cheia
primară a entităţii şi atributul multiplu. Cheia primară este o
cheie externă, plus una sau mai multe coloane adiţionale.
Entităţile devin tabele, iar atributele lor devin coloane în aceste
tabele. Ce devin atributele relaţiilor? Pentru relaţii 1:1 şi 1:n,
atributele relaţiilor vor aparţine tabelului care conţine cheia
externă, iar pentru relaţii m:n şi de tipul trei, atributele vor fi
plasate în tabelele asociative.
SALARIAT
cod_salariat#
PROIECT
nr_proiect#
DEPARTAMENT
cod_departament#
SARCINA
nr_proiect#
nr_sarcina#
atasat_la
SALARIAT cod_salariat# PROIECT
cod_salariat# nr_proiect#
nr_proiect#
TELEFON
cod_salariat#
SALARIAT nr_telefon#
cod_salariat#
SALARIAT
cod_salariat# PROIECT
salariu job_cod nr_proiect#
nume ATASAT_LA descriere
sex AGENT_TERITORIAL cod_salariat# buget_alocat
zona nr_proiect#
comision functie
PROGRAMATOR
limbaj
nivel
apartine_la
CONTRACTANT
cod_contractant#
adresa
telefon
cont
banca tip_contractant
LUCRARE ŞANTIER
SUBANTREPENOR cod_lucrare# nr_şantier#
nume executa cod_obiectiv# executa specialitate şef
nume_adm
funcţie_adm
necesita
INVESTITOR
tip_investitor OBIECTIV_INVESTITIE
cod_obiectiv#
denumire
PERS_FIZICA
adresa
investeste_in
nume
prenume
bi
atasat_la
CONTRACT
PERS_JURIDICA nr_contract#
tip_contract
tip_juridic data_avans
nume incheie
functie
19
REALIZEAZA FRAME
SALARIAT cod_salariat# nr_publicatie#
cod_salariat# nr_publicatie# nr_capitol#
nume job nr_capitol# nr_frame#
nr_frame# tip
GRAFICIAN
tip include
TEHNOREDACTOR CAPITOL
tip_editor scrie nr_publicatie#
nr_capitol#
REDACTOR_SEF dimensiune
experienta
coordoneaza cuprinde
PUBLICATIE
TELEFON LIMBA nr_publicatie#
cod_salariat# cod_salariat# stil
nr_telefon# limba_cun#
Algebra relaţională
Limbajul de definire a datelor (LDD) precizează entităţile, relaţiile
dintre ele, atributele, structura atributelor, cheile, constrângerile, prin
urmare defineşte structura obiectelor bazei de date (schema bazei).
Limbajul de prelucrare a datelor (LMD) dintr-o bază de date
relaţionale cuprinde aspecte referitoare la introducerea, eliminarea,
modificarea şi căutarea datelor.
• Introducerea datelor – permite adăugarea de tupluri la o relaţie.
Tuplurile pot fi introduse de utilizator sau pot fi obţinute din alte
relaţii existente în baza de date.
• Eliminarea datelor – permite ştergerea tuplurilor ce satisfac
condiţii date.
• Modificarea datelor – permite reactualizarea tuplurilor ce
satisfac condiţii date cu noi valori ale atributelor sau cu rezultate
ale unor operaţii aritmetice efectuate asupra unor valori existente.
• Căutarea datelor – permite găsirea tuplurilor sau a unor părţi ale
tuplurilor ce satisfac condiţii date.
Modelul relaţional oferă două mulţimi de operatori pe relaţii:
• algebra relaţională (filtrele se obţin aplicând operatori specializaţi
asupra uneia sau mai multor relaţii din cadrul bazei relaţionale);
• calculul relaţional (filtrele se obţin cu ajutorul unor formule
logice pe care tuplurile rezultatului trebuie să le satisfacă).
rezultatele sunt relaţii ieşirea unei operaţii poate deveni intrare pentru
alta) posibilitatea imbricării expresiilor în algebra relaţională).
Operatorii algebrei relaţionale sunt:
• operatori tradiţionali pe mulţimi (UNION, INTERSECT,
PRODUCT, DIFFERENCE);
• operatori relaţionali speciali (PROJECT, SELECT, JOIN,
DIVISION).
Calculul relaţional reprezintă o adaptare a calculului predicatelor la
domeniul bazelor de date relaţionale. Ideea de bază este de a identifica o
relaţie cu un predicat. Pe baza unor predicate iniţiale, prin aplicarea unor
operatori ai calculului cu predicate (conjuncţia, disjuncţia, negaţia,
cuantificatorul existenţial şi cel universal) se pot defini noi relaţii.
Calculul relaţional poate să fie orientat pe tupluri sau orientat pe
domenii.
Echivalenţa dintre algebra relaţională şi calculul relaţional a fost
demonstrată de J.D.Ullman. Această echivalenţă arată că orice relaţie
posibil de definit în algebra relaţională poate fi definită şi în cadrul calcului
relaţional, şi reciproc.
Operatorii (unari sau binari) algebrei relaţionale realizează
următoarele funcţii:
• SELECT (selecţie) – extrage tupluri ce satisfac o condiţie specificată;
• PROJECT (proiecţie) – extrage atributele specificate;
• DIFFERENCE (diferenţă) – extrage tupluri care apar într-o relaţie, dar
nu apar în cealaltă;
• PRODUCT (produs cartezian) – generează toate perechile posibile de
tupluri, primul element al perechii fiind luat din prima relaţie, iar cel de-
al doilea element din cealaltă relaţie;
• UNION (reuniune) – reuneşte două relaţii;
• INTERSECT (intersecţie) – extrage tupluri care apar în ambele relaţii;
• DIVISION (diviziune) – extrage valorile atributelor dintr-o relaţie, care
apar în toate valorile atributelor din cealaltă relaţie;
• JOIN (compunere) – extrage tupluri din mai multe relaţii corelate:
• NATURAL JOIN (compunere naturală) – combină tupluri din două
relaţii, cu condiţia ca atributele comune să aibă valori identice;
• SEMI-JOIN (semi-compunere) – selectează tupluri ce aparţin unei
singure relaţii, care sunt corelate cu tupluri din cea de a doua relaţie;
22
Operatorul PROJECT
Proiecţia este o operaţie unară care elimină anumite atribute ale unei
relaţii producând o submulţime „pe verticală“ a acesteia. Suprimarea unor
atribute poate avea ca efect apariţia unor tupluri duplicate, care trebuie
eliminate.
Prin proiecţie se construieşte dintr-o relaţie R, o nouă relaţie:
a) ştergând din R atributele care nu sunt menţionate în parametrii
proiecţiei;
b) eliminând dublurile care apar după ştergere.
Operatorul SELECT
Selecţia (restrictia) este o operaţie unară care produce o submulţime
pe „orizontală“ a unei relaţii R. Această submulţime se obţine prin
extragerea tuplurilor din R care satisfac o condiţie specificată.
Sunt utilizate diferite notaţii:
σcondiţie(R) R[condiţie]
SELECT(R, condiţie) RESTRICT(R, condiţie).
Exemplu. Să se obţină informaţii complete despre angajaţii de sex
masculin.
1. Selecţie în algebra relaţională:
Rezultat = SELECT(SALARIAT, sex = ‘m’)
2. Selecţie în SQL:
SELECT *
FROM salariat
WHERE sex = ’m’;
Operatorul UNION
Reuniunea a două relaţii R şi S este mulţimea tuplurilor aparţinând
fie lui R, fie lui S, fie ambelor relaţii.
Sunt utilizate notaţiile:
R∪S
UNION(R, S)
OR(R, S)
APPEND(R, S).
Exemplu. Să se obţină lista cu numele persoanelor fizice şi a
subantreprenorilor.
SELECT nume
FROM subantreprenor
UNION
SELECT nume
FROM pers_fizica;
24
Operatorul DIFFERENCE
Diferenţa a două relaţii R şi S este mulţimea tuplurilor care aparţin
lui R, dar nu aparţin lui S. Diferenţa este o operaţie binară necomutativă
care permite obţinerea tuplurilor ce apar numai într-o relaţie.
Sunt utilizate diferite notaţii:
R–S
DIFFERENCE(R, S)
REMOVE(R, S)
MINUS(R, S).
Exemplu. Să se obţină lista cu numărul contractului, tipul
contractului, valoarea investiţiei şi durata lucrării pentru contractele de
subantrepriză pentru care valoarea investiţiei nu depăşeşte 60000$.
1. Diferenţă în algebra relaţională:
R=PROJECT(SELECT(CONTRACT, tip_contract=T),
nr_contract, tip_contract, val_investitie, durata_lucrare);
S=PROJECT(SELECT(CONTRACT, val_investitie > 60000),
nr_contract, tip_contract, val_investitie, durata_lucrare);
Rezultat = DIFFERENCE(R, S)
2. Diferenţa în SQL:
SELECT nr_contract,tip_contract,
val_investitie,durata_lucrare
FROM contract
WHERE tip_contract
MINUS
SELECT nr_contract,tip_contract,
val_investitie,durata_lucrare
FROM contract
WHERE val_investitie>60000;
Evident diferenţa se poate referi la tabele diferite! Implementaţi
cererea prin care se listează toate oraşele în care se află o filială, dar nici o
proprietate.
Operatorul INTERSECT
Intersecţia a două relaţii R şi S este mulţimea tuplurilor care aparţin
şi lui R şi lui S. Operatorul INTERSECT este un operator binar, comutativ,
derivat:
25
R ∩ S = R – (R – S)
R ∩ S = S – (S – R).
Sunt utilizate diferite notaţii:
INTERSECT(R, S)
R∩S
AND(R, S).
În anumite dialecte SQL există operator special (INTERSECT), care
realizează această operaţie. Operatorii INTERSECT şi DIFFERENCE pot
fi simulaţi în SQL (în cadrul comenzii SELECT) cu ajutorul opţiunilor
EXISTS, NOT EXISTS, IN, != ANY.
Exemplu. Utilizând tabelele agent_teritorial şi programator să se
obţină lista codurilor salariaţilor care sunt programatori, dar care lucrează şi
ca agenţi teritoriali.
1. Intersecţie în algebra relaţională:
R = PROJECT(AGENT_TERITORIAL, cod_salariat);
S = PROJECT(PROGRAMATOR, cod_salariat),
Rezultat = INTERSECT(R, S).
2. Intersecţie în SQL:
SELECT cod_salariat
FROM agent_teritorial
INTERSECT
SELECT cod_salariat
FROM programator;
3. Simularea intersecţiei în SQL:
SELECT cod_salariat
FROM programator p
WHERE EXISTS
(SELECT cod_salariat
FROM agent_teritorial a
WHERE p.cod_salariat=a.cod_salariat);
Operatorul PRODUCT
Fie R şi S relaţii de aritate m, respectiv n. Produsul cartezian al lui R
cu S este mulţimea tuplurilor de aritate m + n unde primele m componente
formează un tuplu în R, iar ultimele n componente formează un tuplu în S.
Sunt utilizate diferite notaţii:
26
R×S
PRODUCT(R, S)
TIMES(R, S).
Exemplu. Să se obţină lista tuturor posibilităţilor de investiţie în
diverse obiective de către o firmă care este persoană juridică.
1. Produs cartezian în algebra relaţională:
R = PROJECT(PERS_JURIDICA, nume, cod_contractant);
S = PROJECT(OBIECTIV_INVESTITIE, denumire);
Rezultat = PRODUCT(R, S).
2. Produs cartezian în SQL:
SELECT cod_contractant, nume, denumire
FROM obiectiv_investitie, pers_juridica;
Operatorul DIVISION
Diviziunea este o operaţie binară care defineşte o relaţie ce conţine
valorile atributelor dintr-o relaţie care apar în toate valorile atributelor din
cealaltă relaţie.
Sunt utilizate diferite notaţii:
DIVIDE(R, S)
DIVISION(R, S)
R ÷ S.
Diviziunea conţine acele tupluri de dimensiune n – m la care, adăugând
orice tuplu din S, se obţine un tuplu din R.
Operatorul diviziune poate fi exprimat formal astfel:
R(n) ÷ S(m) = {t(n-m) ∀ s ∈ S, (t, s) ∈ R} unde n > m şi S ≠ ∅.
Operatorul DIVISION este legat de cuantificatorul universal (∀) care
nu există în SQL. Cuantificatorul universal poate fi însă simulat cu ajutorul
cuantificatorului existenţial (∃) utilizând relaţia:
∀x P(x) ≡ ¬ ∃ x ¬ P(x).
Prin urmare, operatorul DIVISION poate fi exprimat în SQL prin
succesiunea a doi operatori NOT EXISTS.
Exemplu. Să se obţină codurile salariaţilor ataşaţi tuturor proiectelor
pentru care s-a alocat un buget egal cu 1000.
1. Diviziune în algebra relaţională:
27
Operatorul JOIN
Operatorul de compunere (uniune) permite regăsirea informaţiei din
mai multe relaţii corelate. Operatorul combină produsul cartezian, selecţia
şi proiecţia.
Operatorul θ-JOIN
Operatorul θ-JOIN combină tupluri din două relaţii (nu neapărat
corelate) cu condiţia ca valorile atributelor specificate să satisfacă o
anumită condiţie specificată explicit în cadrul operaţiei.
Operatorul θ-JOIN este un operator derivat, fiind o combinaţie de
produs scalar şi selecţie:
JOIN(R, S, condiţie) = σcondiţie (R × S)
Exemplu. Să se afişeze pentru fiecare salariat, codul acestuia şi grila
sa de salarizare.
SELECT empno, level
FROM salgrade, emp
WHERE sal BETWEEN losal AND hisal;
Exemplu. Să se obţină informaţii despre contractanţi (codul şi banca)
şi obiectivele de investiţie asociate acestora (denumire, număr certificat de
urbanizare) cu condiţia ca obiectivele să nu fie la aceeaşi adresă ca şi
contractanţii.
29
Operatorul SEMI-JOIN
Operatorul SEMI-JOIN conservă atributele unei singure relaţii
participante la compunere şi este utilizat când nu sunt necesare toate
atributele compunerii. Operatorul este asimetric.
Tupluri ale relaţiei R care participă în compunerea (naturală sau
θ-JOIN) dintre relaţiile R şi S.
SEMI-JOIN este un operator derivat, fiind o combinaţie de proiecţie
şi compunere naturală sau proiecţie şi θ-JOIN:
SEMIJOIN(R, S) = ΠM (JOIN(R, S))
SEMIJOIN(R, S, condiţie) = ΠM (JOIN(R, S, condiţie)),
unde am notat prin M atributele relaţiei R.
Exemplu. Să se obţină informaţii referitoare la persoanele fizice
(nume, buletin) care investesc în obiective cu caracter recreativ.
1. Operatorul SEMI-JOIN în algebra relaţională:
R = SELECT(OBIECTIV_INVESTITIE, denumire = ’cabana’ OR
denumire = ’casa de vacanta’)
S = JOIN(PERS_FIZICA, R)
Rezultat = PROJECT(S, nume, buletin).
2. Operatorul SEMI-JOIN în SQL:
SELECT nume,bi
FROM pers_fizica a,obiectiv_investitie b
WHERE a.cod_contractant = b.cod_contractant
AND (denumire=’cabana’)OR (denumire= ’casa
de vacanta’);
30
Ideea generală:
cerere arbore algebric (nu este unic) plan de executie optimizare
Un plan de execuţie implică o secvenţă de paşi pentru evaluarea cererii
(în mod obişnuit, fiecare pas din planul de execuţie corespunde unei operaţii
relaţionale) precum şi metoda care va fi folosită pentru evaluarea operaţiei.
De obicei, pentru o operaţie relaţională dată, există mai multe metode ce pot
fi folosite pentru evaluarea acesteia.
Două planuri de execuţie diferite care au întotdeauna acelaşi rezultat se
numesc echivalente. Planuri de execuţie echivalente pot avea diferite
costuri. Scopul optimizării cererilor este de a găsi, printre diversele planuri
de execuţie echivalente, pe acela de cost minim. Într-un sistem centralizat,
costul evaluării unei cereri este suma a două componente, costul I/O
(transferuri de date) şi costul CPU (verificare de condiţii, operaţii join etc.).
33
NORMALIZAREA RELAŢIILOR
În procesul modelării unei baze de date relaţionale, o etapă importantă
o reprezintă normalizarea relaţiilor conceptuale (Codd, 1972), adică
obţinerea de relaţii „moleculare“fără a pierde nimic din informaţie pentru a
elimina:
• redundanţa;
• anomaliile reactualizării informaţiilor.
Tehnica normalizării permite obţinerea unei scheme conceptuale
rafinate printr-un proces de ameliorare progresivă a unei scheme conceptuale
iniţiale a bazei de date relaţionale. După fiecare etapă de ameliorare, relaţiile
bazei de date ating un anumit grad de perfecţiune, deci se află într-o anumită
formă normală. Trecerea unei relaţii dintr-o formă normală în alta, presupune
eliminarea unei anumit tip de dependenţe nedorite, care sunt transformate în
dependenţe admisibile, adică dependenţe care nu provoacă anomalii.
39
Dependenţe funcţionale
O relaţie universală este o relaţie ce grupează toate atributele care
modelează sistemul real cercetat. Fie E, mulţimea dependenţelor
considerate de proiectantul bazei pentru o schemă relaţională sau pentru o
relaţie universală. Plecând de la o mulţime de proprietăţi formale ale
dependenţelor, proprietăţi considerate drept reguli de deducţie (axiome),
poate fi obţinută mulţimea maximală de dependenţe asociate lui E. Această
mulţime defineşte închiderea lui E.
Fie E mulţimea dependenţelor unei relaţii şi p1, p2, ..., pr, r ≥ 1,
proprietăţi formale ale acestor dependenţe. Dacă există o mulţime E′, astfel
încât orice dependenţă a mulţimii E este derivabilă din E′ prin aplicarea
proprietăţilor p1, p2, ..., pr, atunci mulţimea E′ defineşte acoperirea lui E
pentru proprietăţile p1, p2, ..., pr.
E′ este o acoperire minimală pentru E, dacă nu există nici o
submulţime proprie, nevidă a lui E′ care să fie o acoperire pentru E.
40
localitate
W#
calitate
varsta
regiune
tara
data cantitate
D#
nume
localitate
W#
calitate
varsta
43
regiune
tara
data cantitate
D#
nume
2. Graful dependenţelor funcţionale pentru schema relaţională
OBIECTIV_INVESTITIE. Dependentele sunt deduse din regulile impuse de
beneficiar!
aria_construita
denumire
nr_certificat_urbanizare
cod_obiectiv# adresa
nr_aut_construtie
nr_contract cod_contractant
Necesitatea normalizării
Constrângere:
toate avioanele cu acelaşi nume au aceeaşi capacitate.
Datorită dependenţei introduse pot exista: anomalii la inserare,
modificare sau ştergere, redundanţă în date, probleme de reconexiune.
44
Relatia universala
FN1
FN2
FN3
BCNF
FN4
FN5
AVION2
A# Nume Localitate
1 AIRBUS PARIS
2 AIRBUS PARIS
3 AIRBUS LONDRA
4 CAR PARIS
5 B707 LONDRA
6 B707 LONDRA
Varianta 2
Persoana Vehicul
Eu R25
Tu 205
El R5
Noi BX
Masina_34
Persoana Vehicul
Noi R25
atasat_2a
Cod_salariat# Nr_proiect# Functia Suma
S1 P1 Supervizor 60
S1 P2 Cercetator 25
S1 P3 Auxiliar 10
S3 P3 Supervizor 60
48
S5 P3 Supervizor 60
atasat_2b
Cod_salariat# Job_cod
S1 Programator
S3 Vanzator
S5 Inginer
K1 K2
K X1 X2
X3
50
X
Exemplu:
ADRESA(cod_parsoana#, telefon#, adresa)
cod_persoana
adresa
55
telefon
x y z
x y' z'
x y z'
x y' z
tupluri <x, y, z> şi <x, y′, z′>, se obţine y = y′. Prin urmare în relaţie apar
tuplurile <x, y′, z> şi <x, y, z′> şi deci X →→ Y (MVD).
Fie W, V, X, Y şi Z submulţimi de atribute ale unei scheme relaţionale
R. Fiind dată o mulţime T de multidependenţe există o mulţime completă
de axiome (Ax1–Ax8) care permit obţinerea tuturor multidependenţelor ce
se pot deduce din mulţimea T:
Ax1. Dacă Y ⊂ X, atunci X → Y.
Ax2. Dacă X → Y, atunci X ∪ Z → Y ∪ Z.
Ax3. Dacă X → Y şi Y → Z, atunci X → Z.
Ax4. Dacă X →→ Y, atunci X →→ R – {X ∪ Y}.
Ax5. Dacă X →→ Y şi V ⊂ W, atunci W ∪ X →→ V ∪ Y.
Ax6. Dacă X →→ Y şi Y →→ Z, atunci X →→ (Z – Y).
Ax7. Dacă X → Y, atunci X →→ Y.
Ax8. Dacă X →→ Y, Z → W, W ⊂ Y şi Y ∩ Z = ∅, atunci X → W.
O multidependenţă elementară este o multidependenţă care are
părţi stângi şi drepte minimale (nu există X′ ⊂ X şi Y′ ⊂ Y a.i. X′ →→ Y′).
Formal, relaţia R este în a patra formă normală dacă şi numai dacă:
• R este în BCNF;
• orice dependenţă multivaloare este o dependenţă funcţională.
O relaţie BCNF este în FN4 dacă pentru orice multidependenţă
elementară de forma X →→ Y, X este o supercheie a lui R. Aducerea
relaţiilor în FN4 presupune eliminarea dependenţelor multivaloare atunci
când sunt mai mult de una în cadrul unei relaţii.
Regula de descompunere în relaţii FN4. Fie R(X, Y, Z) o schemă
relaţională care nu este în FN4 şi fie X →→ Y o multidependenţă
elementară care nu este de forma „CHEIE →→ atribut“. Această relaţie
este descompusă prin proiecţie în două relaţii:
R = JOIN(ΠX∪Y(R), ΠX∪Z(R)).
Aplicând recursiv această regulă, se obţin relaţii FN4.
Exemplu. Fie relaţia INVESTITIE(cod_contractant#, denumire,
telefon) şi presupunem că un investitor poate avea mai multe numere de
telefon şi că poate investi în mai multe obiective. Între atributele relaţiei
există multidependenţele:
cod_contractant# →→ denumire;
cod_contractant# →→ telefon.
58
Exemplu.
Fie schema relaţională:
EXECUTANT(nr_santier#, cod_obiectiv#, cod_lucrare#, data_inceput,
data_sfarsit). Un şantier poate executa mai multe lucrări referitoare la
acelaşi obiectiv sau poate executa o lucrare pentru un obiectiv în intervale
de timp distincte. Se presupune că mai multe şantiere pot executa aceeaşi
lucrare, în acelaşi interval de timp sau în intervale de timp distincte.
Relaţia, datorită dependenţelor formulate anterior, nu este în FN5. Ea
se poate desface prin proiecţie în trei relaţii:
EX1(nr_santier#, cod_obiectiv#, cod_lucrare#);
EX2(nr_santier#, data_inceput, data_sfarsit);
EX3(cod_obiectiv#, cod_lucrare#, data_inceput, data_sfarsit).
Sunt evidente relaţiile:
EXECUTANT ≠ JOIN(EX1, EX2),
EXECUTANT ≠ JOIN(EX1, EX3),
EXECUTANT ≠ JOIN(EX2, EX3),
EXECUTANT = JOIN(JOIN(EX1, EX2), EX3).
Concluzii:
1. FN1 → FN2 elimină redundanţele datorate dependenţei netotale
a atributelor care nu participă la o cheie, faţă de cheile lui R. Se
suprimă dependenţele funcţionale care nu sunt totale.
2. FN2 → FN3 elimină redundanţele datorate dependenţei
tranzitive. Se suprimă dependenţele funcţionale tranzitive.
3. FN3 → BCNF elimină redundanţele datorate dependenţei
funcţionale. Se suprimă dependenţele în care partea stângă nu
este o supercheie.
4. BCNF → FN4 elimină redundanţele datorate multidependenţei.
Se suprimă toate multidependenţele care nu sunt şi dependenţe
funcţionale.
61
ANSI, IBM, Microsoft, Borland, etc. Din păcate, lipsa unui standard unic
SQL are drept consecinţe creşterea costurilor programelor de gestiune a
bazelor de date şi îngreunarea întreţinerii arhitecturilor client/server.
Un grup de specialişti în baze de date s-au reunit sub numele SAG
(SQL Access Group) cu scopul de a construi un limbaj SQL comun şi de a
realiza pentru fiecare dialect un program de conversie din dialect în SQL, şi
invers.
Rezultatul a fost că în:
• 1992, Microsoft a lansat ODBC (Open Database Connectivity)
care este o mulţime de primitive bazate pe activitatea lui SAG;
• 1994 Borland a lansat IDAPI (Integrated Database Application
Programming Interface) care este o bibliotecă de funcţii SQL ce
se pot integra într-un program gazdă.
Limbaje algebrice
În abordarea algebrică, o relaţie este considerată ca o mulţime de
tupluri, iar o bază de date este considerată ca o mulţime de relaţii pe care
sunt definiţi operatorii algebrici.
Există două tipuri de operatori algebrici: mulţime (intersecţie,
reuniune, diferenţă, produs cartezian) şi relaţionali (proiecţie, selecţie,
diviziune, compunere). Operatorii selecţie, proiecţie, produs cartezian,
reuniune şi diferenţă sunt ireductibili şi sunt consideraţi drept operatori de
bază, iar operatorii intersecţie, diviziune şi compunere pot fi deduşi din
operatorii anteriori şi sunt consideraţi operatori derivaţi.
Operatorii limbajului algebric permit exprimarea unor cereri
nerecursive. Dacă cererea este recursivă este necesar un operator special şi
anume închiderea tranzitivă a unei relaţii.
SEQUEL (Structured English as a Query Language) este un
limbaj algebric definit pentru prototipul relaţional SYSTEM–R. Operaţia
fundamentală a limbajului este SELECT, care are forma generală clasică.
Operatorii clasici UNION, INTERSECTION, DIFFERENCE şi
INCLUSION sunt implementaţi în limbaj. SEQUEL este singurul limbaj
relaţional care are integrată închiderea tranzitivă. Limbajul permite
reactualizări asupra bazelor (UPDATE, INSERT, DELETE) şi calculul
unor funcţii elementare (COUNT, SUM, AVG, MAX, MIN). Conceptul de
partiţionare a fost de asemenea introdus în SEQUEL şi realizat cu ajutorul
clauzelor GROUP BY şi HAVING.
O extensie comercială a acestui limbaj, adoptată de aproape toate
SGBD şi considerată drept standard în prelucrarea datelor relaţionale, este
SQL.
69
Limbaje predicative
Limbajele predicative se bazează pe calculul predicatelor. Cererile
sunt exprimate sub forma unor mulţimi de tupluri sau valori pentru care se
specifică, sub forma unor predicate, proprietăţile pe care trebuie să le
îndeplinească.
QUEL este un limbaj predicativ orientat pe tupluri, utilizat de
sistemul INGRES. Limbajul QUEL poate fi utilizat independent sau inclus
în limbajul de programare C. Limbajul este caracterizat de:
• declararea unei variabile tuplu pentru fiecare relaţie (prin
RANGE),
• absenţa cuantificatorilor în expresii,
• utilizarea unor operatori speciali pe mulţimi,
• integrarea operaţiilor aritmetice.
Exemplu.
Să se listeze numele cititorilor care au împrumutat cărţi scrise de
Cioran.
RANGE OF a IS carte
RANGE OF b IS cititor
RANGE OF v IS imprumuta
RETRIEVE b.nume
WHERE (b.codec = v.codec)
AND (v.codel = a.codel)
AND (a.autor = ’Cioran’)
Limbajul acceptă comenzi de inserare (APPEND), reactualizare
(REPLACE), suprimare (DELETE), precum şi funcţii de calcul (MAX, MIN,
SUM, COUNT, AVG). Cuantificatorul existenţial este reprezentat implicit
prin declaraţia RANGE, iar cuantificatorul universal este simulat cu ajutorul
funcţiei COUNT.
Exemplu.
Să se listeze numele cititorilor care au împrumutat toate cărţile din
bibliotecă scrise de Cioran.
RANGE OF a IS carte
RANGE OF b IS cititor
RANGE OF v IS imprumuta
RETRIEVE b.nume
WHERE
COUNT (v.codel
WHERE (b.codec=v.codec)
70
AND (a.codel=v.codel)
AND (a.autor=’Cioran’))
=
COUNT (a.codel WHERE (a.autor=’Cioran’))
Algebric:
R1 = SELECT(carte, autor = 'Popa')
R2 = SELECT(carte, titlu = Geometrie')
R3 = R1 ∪ R2
Rezultat = PROJECT (R3, codel)
SEQUEL:
SELECT codel
FROM carte
WHERE autor = ’Popa’
OR titlu = ’Geometrie’
QBE:
Carte Codel Titlu Autor Pret Nrex Coded
P.X 'Popa'
P.Y 'Geometrie'
Exemplu.
Numele cititorilor care au împrumutat cel putin o carte scrisa de
Cioran.
Algebric:
R1 = PROJECT(cititor, codec, nume)
R2 = SELECT(carte, autor = 'Cioran')
R3 = PROJECT(R2, codel)
R4 = PROJECT(imprumuta, codel, codec)
R5 = JOIN(R4, R3, codel)
R6 = JOIN(R5, R1, codec)
Rezultat = PROJECT(R6, nume)
QBE:
Cititor Codec Nume Dep
Y P.X
SELECT codec
FROM imprumuta
WHERE codel IS IN
SELECT codel
FROM carte
WHERE autor = ’Cioran’
Exemplu.
Să se obţină o relaţie având numele rezultat, ce conţine toate cărţile
scrise de Cioran care au preţul mai mare de 150000.
VAR rezultat: abc;
…
BEGIN
rezultat:=[];
FOR EACH x IN carte DO
IF(x.autor = ’Cioran’) AND (x.pret >= 150000)
THEN rezultat:=rezultat + [x]
END;
Relaţia rezultat poate fi construită şi direct prin atribuirea:
rezultat := [EACH (x) IN carte:
(x.autor = ’Cioran’) AND (x.pret >= 150000)];
PROIECTAREA BAZELOR DE DATE
RELAŢIONALE
3.1. Preliminarii
1
Modelul relaţional, deşi are unele imperfecţiuni, a fost adoptat în
ultimele decenii de majoritatea programatorilor din domeniu, tocmai datorită
acestor trei calităţi: este simplu, riguros din punct de vedere matematic şi nu
este orientat spre sistemul de calcul.
Definirea unui SGBD relaţional impune analizarea caracteristicilor pe
care trebuie să le prezinte un model de date pentru a fi considerat relaţional.
Există diferite modalităţi pentru a defini acest concept:
• prezentarea datelor în tabele supuse anumitor operaţii de tip
proiecţie, selecţie, reuniune, compunere, intersecţie etc. (definiţie
simplă);
• un sistem de baze de date ce suportă un limbaj de tip SQL –
Structured Query Language (definiţie practică);
• un sistem de baze de date care respectă principiile modelului
relaţional introdus de E.F. Codd (definiţia folosită cel mai
frecvent).
E.F. Codd a publicat un set de 13 reguli, numite reguli de fidelitate, în
raport cu care un SGBD poate fi apreciat ca relaţional. Ulterior, cele 13
reguli de fidelitate ale lui Codd au fost extinse la un număr de 100. Trebuie
remarcat că nu există un SGBD care respectă toate regulile definite de Codd.
Nu trebuie să apreciem un SGBD ca fiind relaţional sau nu, ci măsura în care
acesta este relaţional, deci numărul regulilor de fidelitate pe care le respectă.
2
• existenţa unor structuri de date simple,
• minimizarea redundanţei,
• supleţea în comunicarea cu utilizatorul neinformatician etc.
Ca limite ale modelului relaţional pot fi menţionate:
• rămâne totuşi redundanţă,
• ocupă spaţiu,
• apar fenomene de inconsistenţă,
• nu există mecanisme pentru tratarea optimă a cererilor recursive,
• nu lucrează cu obiecte complexe,
• nu există mijloace perfecţionate pentru exprimarea constrângerilor
de integritate,
• nu realizează gestiunea cunoştinţelor etc.
Un model relaţional este caracterizat de trei elemente:
3
Tipurile pot fi oricât de simple sau de complexe. Se pot distinge tipuri
de date ale căror valori sunt numere, şiruri, date calendaristice, înregistrări
audio sau video, hărţi, puncte geometrice etc.
Orice tip de date este scalar (atomic, încapsulat) sau nescalar. Tipul
nescalar are componente vizibile utilizatorului şi direct accesibile.
Trebuie făcută distincţia între un tip şi reprezentarea sa fizică,
deoarece tipurile sunt legate de model, iar reprezentarea fizică este un aspect
legat de implementare.
Fie D1, D2, ..., Dn domenii finite, nu neapărat disjuncte. Produsul
cartezian D1 × D2 × ... × Dn al domeniilor D1, D2, ..., Dn este definit de
mulţimea tuplurilor (V1, V2, ..., Vn), unde V1 ∈ D1, V2 ∈ D2, ..., Vn ∈ Dn.
Numărul n defineşte aritatea tuplului.
O relaţie R pe mulţimile D1, D2, ..., Dn este o submulţime a produsului
cartezian D1 × D2 × ... × Dn, deci este o mulţime de tupluri. Există un alt mod
de a defini relaţia, şi anume ca o mulţime finită de funcţii. Asociem fiecărui
domeniu Di un atribut Ai şi definim relaţia R = {f1, f2, ..., fm}, unde
fi : {A1, A2, ..., An} → D1 ∪ D2 ∪ ... ∪ Dn şi fi(Aj) ∈ Dj pentru orice valori ale
lui i şi j. În această definiţie nu mai este restricţionată ordinea.
Definirea unei relaţii ca o mulţime de tupluri sau ca o mulţime de funcţii
se referă la mulţimi care variază în timp (se adaugă, se şterg sau se modifică
elemente). Pentru a caracteriza o relaţie este necesară existenţa unui element
invariant în timp, iar acest invariant este dat de structura relaţiei (schema
relaţională). Mulţimea numelor atributelor corespunzătoare unei relaţii R
defineşte schema relaţională a relaţiei respective. Vom nota schema
relaţională prin R(A1, A2, ..., An).
De exemplu, pentru modelul de date analizat în această lucrare,
VESTIMENTATIE(cod_vestimentatie, cod_designer, cod_model,
cod_prezentare, denumire, valoare, descriere) reprezintă o schemă relaţională.
Putem reprezenta o relaţie printr-un tabel bidimensional în care fiecare
linie corespunde unui tuplu şi fiecare coloană corespunde unui domeniu din
produsul cartezian. O coloană corespunde unui atribut. Numărul atributelor
defineşte gradul relaţiei, iar numărul de tupluri din relaţie defineşte
cardinalitatea relaţiei.
Bazele de date relaţionale sunt percepute de către utilizatori ca o
mulţime de tabele. Tabelul reprezintă structura logică dintr-un sistem
relaţional, nu structura fizică. La nivel fizic, sistemul poate stoca datele în
diferite moduri, folosind fişiere secvenţiale, indexări, înlănţuiri de pointeri etc.,
cu condiţia să poată realiza corespondenţa dintre reprezentarea stocată şi
tabelele de la nivelul logic.
4
Bazele de date relaţionale respectă principiul informaţiei (principiul
reprezentării uniforme), care afirmă că întregul conţinut informaţional al bazei
este reprezentat într-un mod unic, şi anume, ca valori explicite ale celulelor
unui tabel. Această modalitate de reprezentare este singura disponibilă, la nivel
logic, într-un sistem relaţional.
Când se inserează tupluri într-o relaţie, de multe ori valoarea unui
atribut este necunoscută sau nu este aplicabilă tuplului respectiv. Pentru a
reprezenta valoarea acestui atribut a fost introdusă o valoare convenţională
în relaţie, şi anume null. De fapt, null nu reprezintă o valoare, ci absenţa
uneia. Un null nu este acelaşi lucru cu o valoare numerică egală cu zero sau
cu un şir de caractere vid. Zerourile şi spaţiile libere (şirul vid) sunt valori,
pe când null semnifică absenţa unei valori. Prin urmare, null trebuie tratat în
mod diferit faţă de alte valori şi trebuie înţeles că termenul „valoare nulă“
este depreciativ.
Evident, este necesară o aritmetică şi o logică (polivalentă) nouă
(fig.3.1) care să cuprindă acest element. De exemplu, ce valoare are „10 >
null“ ? Răspunsul este null. În general, rezultatul operaţiilor aritmetice sau
logice este null când unul din operanzi este null. Prin urmare, „null = null“
are valoarea null, iar ¬ null este null.
Întroducerea null-urilor în modelul relaţional constituie o problemă
controversată, deşi Codd tratează null ca parte integrantă a modelului. Alţii
consideră această abordare greşită şi prematură. Trebuie remarcat că nu toate
sistemele relaţionale acceptă null-urile.
T T F Null T T T T
F F F F F T F Null
5
Dacă baza de date conţine tabele reale depuse pe disc, o vizualizare
este virtuală deoarece datele pe care le conţine nu sunt în realitate memorate
într-o bază de date. Este memorată numai definiţia vizualizării. Vizualizarea
nu este definită explicit, ca relaţiile de bază, prin mulţimea tuplurilor
componente, ci implicit, pe baza altor relaţii obţinute prin intermediul unor
expresii relaţionale. Stabilirea efectivă a tuplurilor care compun vizualizarea
se realizează prin evaluarea expresiei atunci când utilizatorul se referă la
acest tabel.
Utilizarea vizualizărilor este avantajoasă, deoarece:
• asigură securitatea tabelului iniţial (care este protejat de ştergeri,
modificări etc.) prin capacitatea de a ascunde datele;
• permit vizualizarea simultană a aceloraşi date de către diverşi
utilizatori;
• sunt concise, punând la dispoziţie capacităţi „macro“;
• asigură independenţa logică faţă de date (imunitatea programelor
de aplicaţie faţă de modificările din structura logică a bazei de
date).
Există totuşi limitări în utilizarea acestor tabele, în special legate de
problema reactualizării. De exemplu, coloanele calculate nu pot fi
reactualizate. De asemenea, inserarea, reactualizarea şi ştergerea nu sunt, în
general, recomandate şi sunt permise numai cu anumite restricţii care sunt
specifice fiecărui SGBD. De exemplu, Oracle9i rezolvă problema dificilă a
reactualizării vizualizărilor, în anumite situaţii, utilizând o clasă specială de
declanşatoare (TRIGGER de tip INSTEAD OF).
6
Prin urmare, o cheie a unei relaţii R este o mulţime K de atribute,
astfel încât:
(1) pentru orice două tupluri t1, t2 ale lui R ⇒ t1(K) ≠ t2(K);
(2) nu există nicio submulţime proprie a lui K având proprietatea (1).
Fiecare relaţie are cel puţin o cheie. Dacă există diferite chei posibile,
ele se numesc chei candidat. Una dintre cheile candidat va fi aleasă pentru a
identifica efectiv tupluri şi ea va primi numele de cheie primară. Cheia
primară nu poate fi reactualizată. Restul cheilor candidat vor purta numele de
chei alternative sau secundare. Atributele care reprezintă cheia primară pot fi
subliniate sau urmate de semnul „#“.
O cheie identifică linii şi este diferită de un index care localizează
liniile. O cheie secundară este folosită ca index pentru a accesa tupluri. Un
grup de atribute din cadrul unei relaţii care conţine o cheie a relaţiei poartă
numele de supercheie.
Fie schemele relaţionale R1(P1, S1) şi R2(S1, S2), unde P1 este cheie
primară pentru R1, S1 este cheie secundară pentru R1, iar S1 este cheie
primară pentru R2. În acest caz, vom spune că S1 este cheie externă (cheie
străină) pentru relaţia R1.
De exemplu, cod_casa_moda este cheie externă pentru relaţia
CREATOR şi este cheie primară pentru relaţia CASA_MODA. Cheia
primară poate conţine cheia externă. De exemplu, relaţia ACCESORIU are
cheia primară formată din atributele cod_accesoriu şi cod_vestimentatie, iar
atributul cod_vestimentatie fiind cheie primară în relaţia
VESTIMENTATIE, devine cheie externă în relaţia ACCESORIU.
Modelul relaţional respectă trei reguli de integritate structurală.
7
mai dificil de optimizat de către sistem şi, de asemenea, sunt executate doar
atunci când apare evenimentul specificat (declanşator).
Declanşatorii trebuie utilizaţi cu precauţie, sau deloc, dacă există o
modalitate alternativă de rezolvare a problemei respective. Ei pot crea
probleme practice datorită fenomenului „declanşatori în cascadă“ (lanţ de
declanşatori). De asemenea, dacă acelaşi eveniment determină pornirea mai
multor declanşatori diferiţi, atunci ordinea în care pornesc poate fi
importantă sau poate fi chiar nedefinită. Există posibilitatea ca un
declanşator să se declanşeze singur, recursiv. Dacă sunt disponibile, soluţiile
declarative sunt de preferat celor procedurale.
8
3.3. Proiectarea modelului relaţional
9
3.3.1. Transformarea entităţilor
10
şi cod_prezentare. Practic, în acest caz, este preferabil să fie
introdusă o cheie primară artificială.
11
LIMBA INFO CONTACT
ORGANIZATOR
are ×
PERS CONTACT
face ×
are
PUBLICITATE×
organizeaza LOCALIZARE
FIRMA_SEC LOCATIE
se_face
are
are
×
ANGAJAT_SEC PAZA ×
PREZENTARE
× FINANTEAZA SPONSOR
×
asigura PARTICIPA
SOC ASIG
are refera
×
× ACCESORIU ISTORIC ×
12
Cele patru tipuri de tabele (independente, dependente, subtabele şi
asociative) se deosebesc prin structura cheii primare (figura 3.2.).
În figura 3.3 este prezentată diagrama conceptuală pentru proiectarea
modelului relaţional comentat. Ea a fost construită din diagrama E/R prin
adăugarea tabelelor asociative şi prin marcarea cheilor externe.
În continuare va fi prezentată modalitatea efectivă de trecere de la
diagrama entitate-relaţie la diagrama conceptuală pentru modelul de date
real analizat în capitolul 2.
Entităţile independente PERS_CONTACT, ORGANIZATOR,
PREZENTARE, PUBLICITATE, SPONSOR, FIRMA_PUB,
CASA_MODA, CREATOR, ANGAJAT_TEMP, ACCESORIU,
LOCALIZARE, FIRMA_SEC, ANGAJAT_SEC, SOCIETATE_ASIG,
INFO_CONTACT, AGENTIE, LOCATIE devin tabele independente.
Cheile primare ale fiecărei entităţi au fost specificate în capitolul 2.
Entităţile dependente ISTORIC şi ACCESORIU, care intervin în
model, devin tabele dependente, iar cheile primare au fost specificate în
capitolul 2.
Subentitatea MODEL devine subtabel, având aceeaşi cheie primară cu
superentitatea ANGAJAT_TEMP, adică atributul cod_angajat.
Relaţiile de tip one-to-one şi one-to-many devin chei externe.
Relaţia PERS_CONTACT_are_LOCALIZARE devine cheie externă în
tabelul PERS_CONTACT. Dacă restricţia că o persoană de contact are o
singură localizare este eliminată, atunci cardinalitatea relaţiei devine 1:n, iar
atributul cod_pers_contact devine cheie externă în tabelul LOCALIZARE.
Toate comentariile anterioare rămân valabile şi pentru entităţile
FIRMA_PUB, FIRMA_SEC, PREZENTARE, ORGANIZATOR,
CASA_MODA, SPONSOR, SOC_ASIG, CREATOR, LOCATIE,
ANGAJAT_TEMP şi AGENTIE, care sunt legate de entitatea
LOCALIZARE, permiţând astfel localizarea tuturor structurilor modelului.
Relaţia PERS_CONTACT_are_INFO_CONTACT devine cheie externă în
tabelul PERS_CONTACT. Dacă restricţia că pentru o persoană de contact
există o singură posibilitate de contactare este eliminată, atunci
cardinalitatea relaţiei devine 1:n, iar atributul cod_pers_contact devine cheie
externă în tabelul INFO_CONTACT. Toate comentariile anterioare rămân
valabile şi pentru entităţile FIRMA_PUB, FIRMA_SEC,
ANGAJAT_TEMP, PREZENTARE, ORGANIZATOR, CASA_MODA,
SPONSOR, SOC_ASIG, CREATOR, LOCATIE şi AGENTIE, care sunt
legate de entitatea INFO_CONTACT, permiţând astfel accesarea tuturor
structurilor modelului.
13
Relaţia FIRMA_PUB_face_PUBLICITATE devine cheie externă în tabelul
PUBLICITATE.
Relaţia PUBLICITATE_se_face_pentru_PREZENTARE devine cheie
externă în tabelul PUBLICITATE.
Relaţia ORGANIZATOR_are_PERS_CONTACT devine cheie externă în
tabelul PERS_CONTACT.
Relaţia FIRMA_SEC_are_ANGAJAT_SEC devine cheie externă în tabelul
ANGAJAT_SEC.
Relaţia SOC_ASIG_asigura_PREZENTARE devine cheie externă în tabelul
PREZENTARE.
Relaţia CASA_MODA_lucreaza_CREATOR devine cheie externă în tabelul
CREATOR.
Relaţia CREATOR_creeaza_VESTIMENTATIE devine cheie externă în
tabelul VESTIMENTATIE.
Relaţia VESTIMENTATIE_are_ACCESORIU devine cheie externă în tabelul
ACCESORIU.
Relaţia MODEL_prezintă_VESTIMENTATIE devine cheie externă în tabelul
VESTIMENTATIE.
Relaţia CREATOR_face_ACCESORIU devine cheie externă în tabelul
ACCESORIU.
Relaţia PREZENTARE_prezinta_VESTIMENTATIE devine cheie externă în
tabelul VESTIMENTATIE.
Relaţia PREZENTARE_are_LOCATIE devine cheie externă în
PREZENTARE.
Relaţia MODEL_lucreaza_AGENTIE devine cheie externă în tabelul
MODEL.
Relaţia MODEL_are_ISTORIC devine cheie externă în tabelul ISTORIC.
Relaţia ISTORIC_refera_AGENTIE devine cheie externă în tabelul
ISTORIC.
Relaţiile de tip many-to-many devin tabele asociative, având două
chei externe pentru cele două tabele asociate.
Relaţia ANGAJAT_SEC_paza_PREZENTARE devine tabel asociativ
(PAZA).
SPONSOR_finanteaza_PREZENTARE devine tabel asociativ
(FINANTEAZA).
CASA_MODA_participa_PREZENTARE devine tabel asociativ
(PARTICIPA).
Relaţiile de tipul trei (adică cele care leagă cel puţin trei entităţi)
devin tabele asociative, având chei externe pentru fiecare dintre tabelele
asociate.
14
Relaţia primeste, care leagă entităţile ANGAJAT_TEMP, PREZENTARE şi
CASA_MODA, devine tabel asociativ (PRIMESTE). În acest caz, s-a
considerat o cheie primară artificială (atributul cod_primeste). Tabelul
asociativ are drept chei externe atributele: cod_angajat, cod_casa_moda şi
cod_prezentare.
Atributele multiple devin tabele dependendente ce conţin cheia
primară a entităţii şi atributul multiplu.
Atributul limba_cunoscuta este multiplu (relativ la entitatea
PERS_CONTACT) şi va genera tabelul dependent LIMBA. Acesta va avea
drept cheie primară atributele cod_pers_contact şi limba_cunoscuta. Tabelul
mai conţine trei atribute ce reprezintă nivelul de cunoaştere (scris, citit,
vorbit) a limbii.
Atributele entităţilor devin coloane în tabelele corespunzătoare. Pentru
relaţii 1:1 şi 1:n, atributele relaţiilor vor aparţine tabelului care conţine cheia
externă, iar pentru relaţii m:n şi de tipul trei, atributele vor fi plasate în
tabelele asociative.
Tabelul asociativ PRIMESTE va avea ca atribute, pe lângă cele ce constituie
cheia primară, suma, data_achitare, cont, banca.
Schemele relaţionale corespunzătoare diagramei conceptuale din
figura 3.3. sunt următoarele:
MODEL(cod_angajat#, cod_agentie, inaltime, nr_pantof, info)
ANGAJAT_TEMP(cod_angajat#, nume, prenume, data_nastere,
nationalitate, sex, cod_localizare, cod_info_acces, tip)
PUBLICITATE(cod_publicitate#, cod_firma_pub, cod_prezentare, tip,
nume, suma, observatii)
FIRMA_PUB(cod_firma_pub#, nume, info, director, observatii,
cod_localizare, nume_pers_contact, cod_info_acces)
ORGANIZATOR(cod_organizator#, denumire, banca, cont, cod_info_acces,
informatii, cod_localizare)
PERS_CONTACT(cod_pers_contact#, cod_organizator, nume, prenume,
directie, cod_localizare, cod_info_acces)
PREZENTARE(cod_prezentare#, denumire, data_start, data_final,
cod_soc_asig, cod_organizator, cod_locatie)
LOCATIE(cod_locatie#, denumire, tip, cod_localizare, cod_info_acces,
capacitate)
SPONSOR(cod_sponsor#, tip, nume, info, cod_localizare, cod_info_acces)
15
CASA_MODA(cod_casa_moda#, nume, cifra_afaceri, proprietar, director,
istoric, data_creare, cod_localizare, cod_info_acces)
CREATOR(cod_creator#, nume, prenume, data_nastere, data_angajare, tip,
mod_angajare, info, cod_casa_moda, cod_localizare, cod_info_acces)
VESTIMENTATIE(cod_vestimentatie#, denumire, valoare, descriere,
cod_creator, cod_model, cod_prezentare)
ACCESORIU(cod_vestimentatie#, cod_accesoriu#, cod_creator, descriere,
tip, valoare)
AGENTIE(cod_agentie#, nume, data_creare, director, cifra_afaceri, info,
cod_localizare, cod_info_acces)
ISTORIC(cod_model#, data_angajare#, data_final, cod_agentie, conditii)
LOCALIZARE(cod_localizare#, adresa, cod_postal, oras, tara)
INFO_CONTACT(cod_info_acces#, telefon_fix, telefon_mobil, mail, fax)
FIRMA_SEC(cod_firma_sec#, nume_firma, tip_servicii, director,
cod_localizare, cod_info_acces, observatii)
ANGAJAT_SEC(cod_angajat#, nume, prenume, data_nastere, specializare,
nivel, observatii, cod_info_acces, cod_firma_sec)
PAZA(cod_angajat#, cod_prezentare#, tip_paza, dotare, observatii)
SOC_ASIG(cod_soc_asig#, conditii, suma, director, observatii,
cod_localizare, nume_pers_contact_firma, cod_info_acces)
PARTICIPA(cod_prezentare#, cod_casa_moda#, tip, data)
FINANTEAZA(cod_sponsor#, cod_prezentare#, suma, banca, cont_emitent,
data_emitere, cod_ordin_plata)
PRIMESTE(cod_primeste#, cod_angajat, cod_prezentare, cod_casa_moda,
data_achitare, suma, cont, banca)
LIMBA(cod_pers_contact#, limba_cunoscuta#, niv_scris, niv_citit,
niv_vorbit)
16
3.4. Regulile lui Codd
17
Regulile pot fi organizate în următoarele cinci domenii de
funcţionalitate: reguli fundamentale, reguli structurale, reguli de integritate,
reguli de prelucrare a datelor şi reguli privind independenţa datelor.
Regula 1 – regula gestionării datelor. Un SGBD relaţional trebuie să
fie capabil să gestioneze o bază de date prin posibilităţile sale relaţionale.
Practic, nicio implementare curentă de SGBD nu respectă această regulă,
deoarece implementările conţin atât caracteristici relaţionale cât şi
nerelaţionale.
Regula 2 – regula reprezentării informaţiei. Într-o bază de date
relaţională, informaţia este reprezentată la nivel logic sub forma unor
tabele ce poartă numele de relaţii. Este regula cea mai importantă şi
conform lui Codd, un SGBD care nu respectă această regulă, nu poate fi
considerat relaţional. Chiar şi meta-datele, conţinute în catalogul de sistem,
trebuie să fie stocate ca relaţii.
Regula 3 – regula accesului garantat la date. Fiecare valoare dintr-o
bază de date relaţională trebuie să poată fi adresată în mod logic printr-o
combinaţie formată din numele relaţiei, valoarea cheii primare şi numele
atributului.
Regula 4 – regula reprezentării informaţiei necunoscute. Un sistem
relaţional trebuie să permită utilizatorului definirea unui tip de date numit
„null“ pentru reprezentarea unei informaţii necunoscute la momentul
respectiv. Într-un SGBD relaţional trebuie să putem face diferenţa între
valoarea zero, un şir vid de caractere şi o valoare necunoscută.
Regula 5 – regula dicţionarelor de date. Asupra descrierii bazelor de
date (informaţii relative la relaţii, vizualizări, indecşi etc.) trebuie să se
poată aplica aceleaşi operaţii ca şi asupra datelor din baza de date.
Descrierea bazei de date este reprezentată la nivel logic sub forma unor
tabele care pot fi accesate în acelaşi mod ca şi datele efective. Prin urmare
există un singur limbaj de prelucrare atât a meta-datelor, cât şi a datelor.
Regula 6 – regula limbajului de interogare. Trebuie să existe cel puţin
un limbaj pentru prelucrarea bazei de date. În general, toate implementările
SQL respectă această regulă. Limbajul permite utilizatorilor să definească
relaţii şi vizualizări, să prelucreze datele interactiv sau prin intermediul
programului, să regăsească informaţia şi să o poată actualiza, să verifice şi să
corecteze datele de intrare, să implementeze constrângeri, să stabilească
limite pentru tranzacţii etc.
Regula 7 – regula de actualizare a vizualizării. Un SGBD trebuie să
poată determina dacă o vizualizare poate fi actualizată şi să stocheze
rezultatul interogării, ce defineşte vizualizarea, într-un dicţionar de tipul
18
unui catalog de sistem. Trebuie să existe un mecanism prin care să se poată
determina dacă anumite vizualizări pot fi actualizate sau nu. Regula
stabileşte că toate vizualizările care sunt teoretic reactualizabile pot fi
reactualizate şi de către sistemul de gestiune. Nu au fost încă descoperite
condiţiile pentru identificarea tuturor vizualizărilor care pot fi teoretic
reactualizate.
Regula 8 – regula limbajului de nivel înalt. Capacitatea de tratare a
unei relaţii de bază sau a unei vizualizări ca pe un singur operand se aplică
atât pentru operaţiile de regăsire a datelor, cât şi asupra operaţiilor de
inserare, actualizare şi ştergere a datelor. Un SGBD relaţional nu trebuie să
oblige utilizatorul să caute într-o relaţie, tuplu cu tuplu, pentru a regăsi
informaţia dorită. Operaţiile de prelucrare a datelor pot să fie aplicate atât în
mod interactiv cât şi prin program, într-un limbaj gazdă.
Regula 9 – regula independenţei fizice a datelor. Programele de
aplicaţie şi activităţile utilizatorilor nu depind de modul de depunere a
datelor sau de modul de acces la date. Într-un SGBD relaţional trebuie să se
separe aspectul fizic al datelor (stocare sau acces la date) de aspectul logic al
datelor.
Regula 10 – regula independenţei logice a datelor. Programele de
aplicaţie trebuie să fie transparente la modificările de orice tip efectuate
asupra datelor. Orice modificare efectuată asupra unei relaţii nu trebuie să
afecteze operaţiile de prelucrare a datelor.
Regula 11 – regula independenţei datelor din punct de vedere al inte-
grităţii. Regulile de integritate trebuie să fie definite într-un sublimbaj
relaţional de date, nu în programul de aplicaţie. SQL permite definirea de
restricţii privind integritatea datelor şi stocarea lor în catalogul de sistem. Cu
cât sunt mai multe constrângeri de integritate care pot fi întreţinute mai
degrabă de către SGBD, decât în cadrul fiecărui program aplicaţie, cu atât
garantarea calităţii datelor este mai bună.
Regula 12 – regula independenţei datelor din punct de vedere al
distribuirii. Distribuirea datelor pe mai multe calculatoare dintr-o reţea de
comunicaţii de date nu trebuie să afecteze programele de aplicaţie. ANSI-
SQL nu menţionează regula în specificaţiile sale, deoarece este destul de
greu de respectat. De observat că regula nu cere ca SGBD-ul să accepte o
bază de date distribuite pentru a fi relaţional, dar stabileşte că limbajul de
interogare va rămâne acelaşi atunci când se va introduce această capacitate,
iar datele vor fi distribuite.
19
Regula 13 – regula versiunii procedurale a unui SGBD. Orice
componentă procedurală a unui SGBD trebuie să respecte aceleaşi restricţii
de integritate ca şi componenta relaţională. De exemplu, dacă în partea de
prelucrare a datelor a limbajului relaţional valoarea dintr-o coloană este de
tipul not null, orice altă metodă procedurală de accesare a acestei coloane nu
trebuie să permită introducerea unui null în această coloană. Prin urmare,
regulile de integritate exprimate într-un limbaj relaţional de un anumit nivel
nu pot fi distruse de un limbaj de nivel inferior.
Deoarece regulile lui Codd sunt prea severe pentru a fi respectate de
un SGBD operaţional, s-au formulat criterii minimale de definire a unui
SGBD relaţional.
Un SGBD este minimal relaţional dacă:
• toate datele din cadrul bazei sunt reprezentate prin valori în tabele;
• nu există pointeri observabili de către utilizator;
• sistemul suportă operatorii relaţionali de proiecţie, selecţie şi
compunere naturală, fără limitări impuse din considerente interne.
Un SGBD este complet relaţional dacă este minimal relaţional şi
satisface în plus condiţiile:
• sistemul suportă restricţiile de integritate de bază (unicitatea cheii
primare, constrângerile referenţiale, integritatea entităţii);
• sistemul suportă toate operaţiile de baza ale algebrei relaţionale.
20
4. NORMALIZAREA RELAŢIILOR
4.1. Preliminarii
În procesul modelării unei baze de date relaţionale, o etapă importantă o
reprezintă normalizarea relaţiilor conceptuale. Aceasta presupune obţinerea de
relaţii „moleculare“, fără a pierde nimic din informaţie, având scopul de a
elimina redundanţa şi anomaliile reactualizării informaţiilor. Tehnica
normalizării permite determinarea unei scheme conceptuale rafinate, printr-un
proces de ameliorare progresivă a unei scheme conceptuale iniţiale a bazei de
date relaţionale. După fiecare etapă de ameliorare, relaţiile bazei de date ating
un anumit grad de perfecţiune, deci se află într-o anumită formă normală.
Trecerea unei relaţii dintr-o formă normală în alta presupune eliminarea unui
anumit tip de dependenţe nedorite, care sunt transformate în dependenţe
admisibile, adică dependenţe care nu provoacă anomalii.
Procesul de ameliorare progresivă a schemei conceptuale trebuie să
satisfacă următoarele cerinţe:
• să garanteze conservarea datelor, adică în schema conceptuală
finală trebuie să figureze toate datele din cadrul schemei iniţiale;
• să garanteze conservarea dependenţelor dintre date, adică în
schema finală fiecare dependenţă trebuie să aibă determinantul şi
determinatul în schema aceleiaşi relaţii;
• să reprezinte o descompunere minimală a relaţiilor iniţiale, adică
nici una din relaţiile care compun schema finală nu trebuie să fie
conţinută într-o altă relaţie din această schemă.
Există două metode pentru a modela baze de date relaţionale fără
anomalii sau pierderi de informaţie.
• Schema descompunerii pleacă de la o schemă relaţională
universală care conţine toate atributele bazei de date. Schema se
descompune prin proiecţii succesive în subrelaţii. Descompunerea
se opreşte atunci când continuarea ei ar duce la pierderi de
informaţie. Algoritmii de descompunere se bazează, în general, pe
descrierea formală a dependenţei dintre atribute.
• Schema sintezei pleacă de la o mulţime de atribute independente.
Utilizând proprietăţi de semantică şi legături între atribute se pot
compune noi relaţii, astfel încât acestea să nu sufere de anumite
anomalii pe care dorim să le evităm. Algoritmii de sinteză se
bazează în general pe teoria grafurilor pentru a reprezenta
legăturile între atribute.
21
4.2. Dependenţe funcţionale
22
Fie X, Y, Z, W mulţimi de atribute ale unei scheme relaţionale R şi fie
următoarele reguli de inferenţă (axiome) prin care noi dependenţe
funcţionale pot fi deduse din cele date:
Ax1 – reflexivitate. X → X. Mai general, dacă Y ⊆ X, atunci X → Y.
Ax2 – creşterea determinantului. Pot fi considerate trei formulări echi-
valente pentru această axiomă.
1. Dacă X → Y şi X ⊆ Z, atunci Z → Y.
2. Dacă X → Y şi W ⊆ Z, atunci X ∪ Z → Y ∪ W.
3. Dacă X → Y atunci X ∪ Z → Y ∪ Z.
Ax3 – tranzitivitate. Dacă X → Y şi Y → Z, atunci X → Z.
23
2. Anomalie la inserţie. Dacă se doreşte includerea unei prezentări
de modă, care va incepe în luna aprilie şi va avea denumirea
„veselie“, atunci perechea (veselie, aprilie) poate fi inserată în
relaţia VP doar dacă se defineşte o nouă valoare pentru cheia
primară.
3. Anomalie la ştergere. Dacă este ştearsă înregistrarea pentru care
codul prezentării are valoarea 4, atunci se pierde informaţia că
prezentarea având denumirea „iarna“ a început în luna martie.
4. Anomalie la modificare. Dacă se modifică luna de început a
prezentării „primavara“ de la mai la februarie, atunci costul
modificării este mare pentru a modifica toate înregistrările, iar
dacă se modifică doar o înregistrare atunci constrângerea nu va
mai fi verificată.
Anomaliile au apărut datorită dependenţei funcţionale (constrângerii)
introduse anterior.
Normalizarea are drept scop: suprimarea redundanţei logice, evitarea
anomaliilor la reactualizare, rezolvarea problemei reconexiunii. Există o
teorie matematică a normalizării al cărei autor este E.F. Codd. Soluţia lui
E.F. Codd este construirea unor tabele standard (forme normale).
Normalizarea este procesul reversibil de transformare a unei relaţii, în
relaţii de structură mai simplă. Procesul este reversibil în sensul că nicio
informaţie nu este pierdută în timpul transformării. O relaţie este într-o formă
normală particulară dacă ea satisface o mulţime specificată de constrângeri.
Procesul normalizării se realizează plecând de la o relaţie universală ce
conţine toate atributele sistemului de modelat, plus o mulţime de anomalii.
Orice formă normală se obţine aplicând o schemă de descompunere.
24
Prima formă normală (FN1)
25
Pentru un tabel R care se descompune prin proiectie în mai multe tabele: R1,
R2, … Rn, conditia de descompunere fără pierdere de informatie presupune
ca în urma operatiei de compunere naturală a tabelelor R1, R2, … Rn să se
obtină tabelul R.
În SQL:
26
Exemplu
Fie schema relaţională PARTICIPA (cod_prezentare#,
cod_casa_moda#, tip, data, data_start, data_final, denumire)
Alt Exemplu:
27
A2 C2 P1 Ionescu 596322 09.10.04 camasa 400000 3
A2 C2 P3 Ionescu 596322 C2 09.10.04 tricou 200000 2
A2 C2 P2 Ionescu 596322 09.10.04 pantaloni 800000 1
A1 C3 P3 Popescu 415355 10.10.04 tricou 200000 3
A3 C4 P1 Marinescu 546229 C4 10.10.04 P1 camasa 400000 1
a) redundante în date:
- informatia (P1, camasa, 400000) este specificata de 3 ori
- informatia (A1, Popescu, 415355) este specificata de 3 ori
- informatia (A2, Ionescu, 196322) este specificata de 3 ori
- s. a. m. d.
b) anomalii la actualizare:
- anomalie la insertie
Dacă magazinul achizitionează un nou articol (P4, pantofi, 980000)
informatia nu poate fi introdusă în tabel (un nou tuplu) pentru că s-ar
introduce o valoare Null în cheia primară (cod_comanda).
- anomalie la stergere
Dacă este anulat articolul P2 în cadrul comenzii C2 se pierde informatia
referitoare la numele si costul articolului respectiv.
- anomalie la modificare
Dacă se modifică numărul de telefon al unui client, modificarea trebuie
facută în toate tuplurile (liniile) unde apare numele acelui client.
În tabelul VANZARI există următoarele dependente functionale în
care determinantul nu este cheie a tabelului:
28
Forma normală 3 (FN3)
Intuitiv, o relaţie R este în a treia formă normală dacă şi numai dacă:
• relaţia R este în FN2;
• fiecare atribut care nu este cheie (nu participă la o cheie) depinde
direct de cheia primară.
Fie R o relaţie, X o submulţime de atribute ale lui R şi A un atribut al
relaţiei R. A este dependent tranzitiv de X dacă există Y astfel încât X → Y şi
Y → A (unde A nu aparţine lui Y şi Y nu determină pe X). De exemplu, dacă
au loc dependenţele K1, K2, K3 → A1 şi K1, K2, A1 → A2, atunci K1, K2, K3 →
K1, K2, A1 şi K1, K2, A1 → A2. Prin urmare, A2 este dependent tranzitiv de K1,
K2, K3.
Formal, o relaţie R este în a treia formă normală dacă şi numai dacă:
• relaţia R este în FN2;
• fiecare atribut care nu este cheie (nu participă la o cheie) nu este
dependent tranzitiv de nicio cheie a lui R.
A doua condiţie interzice utilizarea dependenţelor funcţionale tranzitive
în cadrul relaţiei R.
Prin urmare, o relaţie este în FN3 dacă şi numai dacă fiecare
atribut care nu este cheie depinde de cheie, de întreaga cheie şi numai de
cheie.
Pentru a obţine o relaţie FN3 se poate aplica regula de descompunere
Casey-Delobel. Fie relaţia R(K, X1, X2, X3), unde atributul X2 depinde
tranzitiv de K, iar K este cheia primară a lui R. Presupunem că K → X1 →
X2. Din cauza dependenţei funcţionale X1 → X2 care arată că R nu este în
FN3, se înlocuieşte R (fără pierdere de informaţie) prin două proiecţii R1(K,
X1, X3) şi R2(X1, X2).
Dependenţa tranzitivă poate fi mai complexă. Fie K1 o parte a cheii K.
Tranzitivitatea poate fi de forma K → Y → X2 unde Y = {K1, X1}. În acest
caz, R poate fi descompusă în R1(K, X1, X3) şi R2(K1, X1, X2).
Algoritm AFN3 (aducerea unei relaţii FN2 în FN3 prin eliminarea
dependenţelor funcţionale tranzitive)
1. Pentru fiecare dependenţă funcţională tranzitivă se transferă
atributele implicate în dependenţa tranzitivă într-o nouă relaţie.
2. Se determină cheia primară a fiecărei noi relaţii create la pasul 1.
3. Se introduc în relaţia iniţială, în locul atributelor transferate, cheile
primare determinate la pasul 2.
29
4. Se reanalizează relaţia iniţială. Dacă în cadrul ei există noi
dependenţe tranzitive, atunci se face transfer la pasul 1, altfel
procesul de aducere la FN3 s-a terminat.
Exemplu
Fie schema relaţională PREZENT(cod_prezentare#, data_start,
data_final, denumire, cod_locatie, capacitate, cod_info_acces).
Pentru relaţia PREZENT sunt adevărate dependenţele:
{cod_prezentare} → {data_start, data_final, denumire, cod_locatie}
{cod_locatie} → {capacitate, cod_info_acces}
Relaţia PREZENT este în FN2, dar nu este în FN3 deoarece atributele
capacitate, cod_info_acces depind indirect de cheia primară, prin
intermediul atributului cod_locatie.
Pentru a obţine o relaţie în FN3 se aplică regula Casey-Delobel şi
relaţia PREZENT se proiectează în două relaţii, prin eliminarea
dependenţelor funcţionale tranzitive.
PREZENT1(cod_prezentare#, data_start, data_final, denumire, cod_locatie)
PREZENT2(cod_locatie#, capacitate, cod_info_acces)
30
cealaltă va avea schema formată din toate atributele relaţiei
iniţiale, mai puţin Y.
4. Se reiau paşii 1, 2, 3 pentru relaţiile obţinute la pasul 3.
Exemplu
Se consideră relaţia FINANTEAZA1, ce leagă entităţile
PREZENTARE şi SPONSOR. Ea are cardinalitatea many to many şi va
genera un tabel asociativ. Se presupune că acest tabel are schema
relaţională:
FINANTEAZA1(cod_prezentare#, cod_sponsor#, nume_prezentare,
cod_ordin_plata).
Pentru exemplul analizat se presupune că numele prezentărilor sunt
unice. Prin urmare, în orice moment fiecare prezentare are un cod unic şi
un nume unic. Cheile candidat sunt {cod_prezentare, cod_sponsor},
respectiv {nume_prezentare, cod_sponsor}.
Între atributele relaţiei există dependenţele:
{cod_prezentare, cod_sponsor} → {cod_ordin_plata}
{cod_prezentare} → {nume_prezentare}
Tabelul nu este în BCNF deoarece conţine doi determinanţi,
cod_prezentare şi nume_prezentare, care nu sunt chei candidat pentru
tabelul respectiv. Ambele atribute sunt determinanţi deoarece fiecare îl
determină pe celălalt.
Soluţia problemei constă în divizarea relaţiei în două proiecţii
conform tehnicii Casey-Delobel.
PREZENTARE(cod_prezentare#, nume_prezentare)
FINANTEAZA(cod_prezentare#, cod_sponsor#, cod_ordin_plata)
31
cod_pers# →→ limba_cunoscuta
cod_pers# →→ nr_telefon.
Pentru a aduce relaţia PERS_CONTACT (care este în BCNF) în FN4,
aceasta se va diviza în două proiecţii :
PERS_CONTACT1(cod_pers#, limba_cunoscuta)
PERS_CONTACT1(cod_pers#, nr_telefon).
Exemplu
Se consideră o relaţie ce conţine informaţii despre creatori,
vestimentaţiile create de aceştia şi accesoriile corespunzătoare. Se consideră
schema relaţională CREARE(cod_vestimentatie#, cod_creator#,
cod_accesoriu#).
Se presupune că fiecare creator poate crea una sau mai multe
vestimentaţii. Fiecare vestimentaţie poate fi creată de unul sau mai mulţi
creatori. Similar, fiecare creator este responsabil de crearea unuia sau a mai
multor accesorii, iar fiecare accesoriu este creat de unul sau mai mulţi
creatori. Fiecare accesoriu apare în una sau mai multe vestimentaţii, iar
fiecărei vestimentaţii i se ataşează unul sau mai multe accesorii.
Mai mult chiar, dacă creatorul C creează vestimentaţia V, iar
accesoriul A este ataşat lui V, iar C este este responsabil de A, atunci C
creează accesoriul A pentru vestimentaţia V.
Ţinând seama de constrângerile impuse modelului se obţin
dependenţele:
{cod_vestimentatie#, cod_creator#} → {cod_accesoriu}
{cod_vestimentatie#, cod_accesoriu#} → {cod_creator}
{cod_accesoriu#, cod_creator#} → {cod_vestimentatie}.
Datorită dependenţelor formulate anterior, relaţia nu este în FN5. Ea
se poate rupe prin proiecţie în trei relaţii:
CREARE1(cod_vestimentatie#, cod_creator#)
32
CREARE2(cod_vestimentatie#, cod_accesoriu#)
CREARE3(cod_creator#, cod_accesoriu#).
În acest caz, sunt evidente relaţiile:
CREARE ≠ JOIN(CREARE1, CREARE2)
CREARE ≠ JOIN(CREARE1, CREARE3)
CREARE ≠ JOIN(CREARE2, CREARE3)
CREARE = JOIN(JOIN(CREARE1, CREARE2), CREARE3).
Concluzii:
1. FN1 → FN2 elimină redundanţele datorate dependenţei netotale a
atributelor care nu participă la o cheie, faţă de cheile lui R. Se
suprimă dependenţele funcţionale care nu sunt totale.
2. FN2 → FN3 elimină redundanţele datorate dependenţei tranzitive.
Se suprimă dependenţele funcţionale tranzitive.
3. FN3 → BCNF elimină redundanţele datorate dependenţei
funcţionale. Se suprimă dependenţele în care partea stângă nu este
o supercheie.
4. BCNF → FN4 elimină redundanţele datorate multidependenţei. Se
suprimă toate multidependenţele care nu sunt şi dependenţe
funcţionale.
5. FN4 → FN5 elimină redundanţele datorate dependenţei ciclice. Se
suprimă toate join-dependenţele care nu sunt implicate de o cheie.
6. BCNF, FN4 şi FN5 corespund la regula că orice determinant este
o cheie, dar de fiecare dată dependenţa cu care se defineşte deter-
minantul este alta şi anume dependenţa funcţională,
multidependenţa sau join-dependenţa.
7. Descompunerea unei relaţii FN2 în FN3 conservă datele şi
dependenţele, pe când descompunerea unei relaţii FN3 în BCNF
şi, respectiv, a unei relaţii BCNF în FN4 conservă doar datele.
33
Partea a II-a. LIMBAJUL SQL
Ce este SQL?
SQL (Structured Query Language -limbaj structurat de
interogare) este un limbaj standard folosit pentru crearea, actualizarea şi
regăsirea informaŃiilor stocate în baze de date prin intermediul sistemelor
de gestionare a bazelor de date ( SGBD-uri).
Numele limbajului poate fi pronunŃat pe litere (es-q-el) sau la fel ca
şi cuvântul englezesc „sequel". O interogare (query) este o simplă cerere
transmisă către baza de date, la care aceasta răspunde într-o anumită
formă. SQL este limbajul folosit cel mai frecvent pentru interogarea
bazelor de date. SQL este considerat un limbaj neprocedural sau
declarativ, ceea ce înseamnă că-i spuneŃi calculatorului ce rezultate vreŃi,
fară să-i spuneŃi cum să le obŃină. De exemplu, dacă vreŃi să obŃineŃi
media numerelor de pe o coloană, folosiŃi funcŃia AVG. Nu este nevoie să
număraŃi valorile din coloană şi să împărŃiŃi suma acestora la numărul
obŃinut - procesorul limbajului SQL din SGBD se ocupă de toate aceste
lucruri în locul dumneavoastră.
Este important să înŃelegi că SQL nu este un limbaj procedural, ca
C, Pascal, Basic, FORTRAN, COBOL sau Ada. Un limbaj procedural
foloseşte o serie de instrucŃiuni executate secvenŃial. De asemenea,
limbajele procedurale includ instrucŃiuni care pot modifica secvenŃa de
1
execuŃie, prin ramificarea la alte porŃiuni ale procedurii sau prin
parcurgerea ciclică a unui set de instrucŃiuni din procedură. MulŃi
producători de sisteme SGBD oferă extensii procedurale ale limbajului
SQL de bază, cum ar fi Oracle PL/SQL (Procedural Language/SQL) sau
Microsoft Transact-SQL, dar reŃineŃi că acestea sunt extensii SQL care
formează noi limbaje - codul SQL pe care-1 conŃin rămâne neprocedural.
De asemenea, SQL nu trebuie confundat cu limbajele orientate spre
obiecte, precum Java şi C++.
Simplu spus, SQL este un limbaj neprocedural pentru gestionarea
şi întreŃinerea bazelor de date relaŃionale, nu un limbaj potrivit pentru
programarea generală a aplicaŃiilor, cum ar fi sistemele de prelucrare a
comenzilor sau a plăŃilor.
SQL este deseori folosit în combinaŃie cu limbajele procedurale sau
orientate spre obiecte menŃionate anterior pentru a manipula stocarea şi
extragerea datelor, folosind instrucŃiuni din limbajul de programare cu
destinaŃie generală pentru alte sarcini de programare, precum prezentarea
datelor pe o pagină web sau furnizarea răspunsurilor la informaŃiile
introduse de utilizatori de la tastatură sau mouse. Atunci când este
necesară interacŃionarea cu baza de date, instrucŃiunile din limbajul
procedural formează instrucŃiunea SQL, o transmit către SGBD în
vederea prelucrării, primesc rezultatele returnate de SGBD şi le
prelucrează într-un mod corespunzător.
Folosind SQL puteŃi transforma întrebări obişnuite :”Din ce oras
sunt studentii notri?” în instrucŃiuni pe care le înŃelege soft-ul pentru baze
de date: “SELECT oras FROM studenti”.
SQL se poate folosi nu numai pentru interogare, dar şi pentru a
adăuga, a modifica sau a şterge înregistrări din bazele de date.
Majoritatea SGBD -urilor populare, ca de exemplu Microsoft
Accsess, Oracle şi MySQL , asigură suport pentru SQL, chiar dacă acest
nivel de suport diferă de la produs la produs.
2
• Sistemul rulează pe un server, care este un sistem de calcul
partajat. Pentru scopurile acestei definiŃii, un sistem mainframe poate fi
considerat un server de dimensiuni mari.
• Fişierele care compun baza de date din punct de vedere fizic sunt
stocate pe discuri conectate la serverul de baze de date.
• Utilizatorii care au acces la baza de date folosesc staŃii de lucru,
numite clienŃi. Clientul trebuie să aibă o conexiune de reŃea la baza de
date, care poate fi o reŃea privată, instalată acasă sau la birou, ori o reŃea
publică, precum Internet.
• Componentele software furnizate de producătorul SGBD rulează
pe staŃiile de lucru alte clienŃilor pentru a oferi utilizatorilor posibilitatea
să introducă instrucŃiuni SQL, să le transmită sistemului SGBD în
vederea prelucrării şi să vadă rezultatele returnate de DBMS. În general,
acest software se numeşte client SQL.
ReŃineŃi că nimic nu vă opreşte să instalaŃi clientul SQL pe acelaşi
calculator cu sistemul SGBD. De fapt, mulŃi dezvoltatori care utilizează
sisteme SGBD precum MySQL, Microsoft SQL Server şi Oracle fac în
mod obişnuit acest lucru, deoarece este foarte convenabil să aibă întregul
mediu de dezvoltare pe un singur calculator, cum ar fi un laptop. Totuşi,
în momentul în care este necesar accesul partajat al mai multor utilizatori,
este mult mai convenabil şi mai eficient să aveŃi o singură copie a
sistemului SGBD pe un server partajat şi să aveŃi numai clientul SQL
instalat pe staŃia de lucru a fiecărui client.
În funcŃie de interfaŃa cu utilizatorul de pe staŃia de lucru client,
clienŃii SQL sunt clasificaŃi în trei categorii: în linia de comandă, grafici
şi bazaŃi pe web. O interfaŃă în linia de comandă se bazează exclusiv pe
intrări şi ieşiri de tip text, cu comenzile introduse de la tastatură şi
răspunsurile afişate ca mesaje de tip text. Principalul avantaj al
interfeŃelor în linia de comandă este că pot fi rulate pe aproape orice
sistem de operare. De exemplu, clientul SQL*Plus rulat într-o fereastră de
comandă Microsoft Windows.
3
O interfaŃă grafică cu utilizatorul (GUI - Graphical User Interface)
rulează sub un tip oarecare de sistem bazat pe ferestre, cum ar fi :
X Window System, Mac OS sau Microsoft Windows, şi afişează datele
sau opŃiunile comenzilor folosind elemente grafice, precum pictograme,
butoane şi casete de dialog. În figura de mai jos se prezintă clientul
SQL*Plus(unul din clienții SQL furnizați de ORACLErulat ca aplicație
GUI sub Microsoft Windows.
4
Producător SGBD Client SQL Descriere
Microsoft Access este o bază de date de uz
personal, cu clientul SQL integrat în
Microsoft Access Nu există
DBMS, toate fiind rulate local pe staŃia de
lucru a utilizatorului.
Client SQL care rulează ca aplicaŃie în
Microsoft SQL Server iSQL linia de comandă într-un nucleu de
comenzi Microsoft Windows.
Client SQL care rulează ca
Microsoft SQL Server Query Analyzer
aplicaŃie Microsoft Windows.
Client SQL care rulează ca
aplicaŃie în linia de comandă sub
MySQL MySQL MySQL diferite sisteme de operare, inclusiv
Microsoft Windows, Linux, Mac OS
X şi diferite implementări Unix.
Client SQL bazat pe web -
Oracle Oracle iSQL*Plus acceptat în versiunile Oracle 9i
si mai noi.
Client SQL care rulează ca
aplicaŃie Microsoft Windows sau
ca aplicaŃie în linia de comandă
Oracle Oracle SQL*Plus sub diferite sisteme de operare,
inclusiv Microsoft Windows,
Linux, Mac OS X, diferite
implementări Unix şi altele.
Client SQL scris în Java -
disponibil în Oracle 8i şi 9i, dar
Oracle Oracle SQL Worksheet
înlocuit de iSQL*Plus în Oracle
10g.
Clientul SQL care rulează ca aplicaŃie
5
Un scurt istoric al limbajului SQL
6
pentru prelucrarea datelor comerciale, dar dezvoltatorii produselor SGBD
originale erau academicieni şi oameni de ştiinŃă, nu specialişti în
prelucrări comerciale, aşa că această cerinŃă nu a fost anticipată. Ca
rezultat, primele dialecte SQL nu asigurau un suport special pentru datele
calendaristice. Pe măsură ce au apărut produsele comerciale, furnizorii au
răspuns cererilor lansate de clienŃii importanŃi şi au adăugat în grabă
suportul pentru date calendaristice. Din nefericire, din cauza grabei
fiecare a făcut-o în felul propriu. Codul SQL este foarte compatibil şi
portabil între produsele diferiŃilor furnizori, dar sistemele complete de
baze de date pot fi rareori transferate fară anumite ajustări.
SQL a sistemului Oracle este o extensie a normei SQL89 şi o
implementare a normei SQL92.
7
separarea elementelor de limbaj. Un element de limbaj, asemănător cu un
cuvânt dintr-o propoziŃie, poate fi un cuvânt cheie (SELECT, FROM,
WHERE), numele unui obiect al bazei de date (FILM, FILM_ID,
TITLU_FILM), un operator (=) sau o constantă ('PG') care apare într-o
instrucŃiune.
• InstrucŃiunile sunt scrise într-o formă liberă, ceea ce înseamnă că
nu există reguli stricte privind poziŃia elementelor de limbaj pe o linie sau
locul în care se poate face trecerea la o linie nouă. Totuşi, în general nu
este o idee bună să împărŃiŃi un element de limbaj pe mai multe linii. Din
punct de vedere logic, instrucŃiunea de mai jos este identică cu cea
prezentată la începutul acestei secŃiuni, dar nu este la fel de uşor de citit şi
de înŃeles:
SELECT FILM_ID, TITLU_FILM FROM FILM WHERE
MPAA_RATING_COD ='PG';
8
• Numele obiectelor bazei de date sunt formate folosind numai
litere, cifre şi liniuŃe de subliniere. Caracterul underscore (liniuŃa de
subliniere) este folosit, de obicei, ca separator între cuvinte, pentru
îmbunătăŃirea lizibilităŃii. Aşa cum am menŃionat anterior, unele
implementări permit folosirea numelor care fac diferenŃa între literele
mari şi cele mici, cum ar fi PersonMiddleName, un stil numit deseori
„scriere de tip cămilă", dar acest stil nu este recomandabil dacă doriŃi ca
instrucŃiunile SQL să fie portabile pe alte implementări. în definitiv, un
nume precum „PERSONMIDDLENAME" nu este foarte uşor de citit.
• In fiecare implementare SQL este definit un set de cuvinte
rezervate, care sunt cuvinte cu o semnificaŃie specifică pentru procesorul
SQL al sistemului SGBD şi, care urmare, nu trebuie folosite într-un alt
context - de exemplu ca nume pentru obiectele bazei de date. Scopul
acestei restricŃii este de a evita interpretarea greşită a instrucŃiunilor SQL
de către SGBD. Aşa cum probabil bănuiŃi, lista cuvintelor rezervate diferă
semnificativ de la o implementare SQL la alta, aşa că este bine să
consultaŃi documentaŃia implementării pe care o folosiŃi pentru a vă
familiariza cu aceste cuvinte.
• Un comentariu pe o singură linie începe cu două liniuŃe de
despărŃire (--). Cele două liniuŃe se pot afla la începutul unei linii, ceea ce
înseamnă că întreaga linie este considerată comentariu, sau oriunde în
cadrul liniei, caz în care restul liniei, până la sfârşit, este considerat
comentariu. De exemplu:
-- Acesta este un comentariu pe o singură linie în SQL.
• Un comentariu pe mai multe linii începe cu combinaŃia dintre o
diagonală la dreapta (slash) şi un asterisc (/*) şi continuă până la
întâlnirea combinaŃiei invers (*/). AveŃi grijă să terminaŃi corect
comentariile, altfel multe linii SQL pe care le-aŃi scris vor fi tratate de
către SGBD ca şi cum ar fi comentarii. Iată un exemplu de comentariu pe
mai multe linii:
/* Acesta este un comentariu pe mai multe linii.
Continuă până la întâlnirea combinaŃiei de caractere care
marchează sfârşitul comentariului. */
9
• Un utilizator nu trebuie să definească două obiecte cu acelaşi
nume.
• În general este bine ca numele unui obiect să fie descriptiv şi
fără prescurtări excesive.
10
Limbajul de interogare a datelor (DQL)
11
Specificarea bazelor de date şi a obiectelor care le compun se
realizează prin intermediul limbajului de definire a datelor (DDL).
Definirea unui obiect presupune crearea, modificarea şi suprimarea sa.
Limbajul de definire a datelor cuprinde instrucŃiunile SQL care permit
realizarea acestor operaŃii (CREATE, ALTER, DROP).
InstrucŃiunile DDL au efect imediat asupra bazei de date şi
înregistrează informaŃia în dicŃionarul datelor. De asemenea, DDL contine
instructiunile RENAME, TRUNCATE si COMMENT.
În cadrul unei scheme se pot defini obiecte de tip: tabel (table),
vizualizare (view), vizualizare materializată (materialized view), secvenŃă
(sequence), index (index), sinonim (synonym), grupare (cluster),
procedură (procedure) şi funcŃie (function) stocată, declanşator (trigger),
pachet stocat (package), legătură a bazei de date (database link),
dimensiune (dimension) etc.
Se vor prezenta în continuare instrucŃiunile SQL folosite pentru
definirea şi gestionarea obiectelor dintr-o bază de date relaŃională.
InstrucŃiunile CREATE, ALTER şi DROP formează o categorie a
limbajului SQL numită limbaj de definire a datelor (DDL– Data
Definition Language).
Se prezintă DDL înaintea DQL, DML deoarece trebuiesc create
obiectele bazei de date înainte de a insera date în baza de date.
ConvenŃii de sintaxă
InstrucŃiunile SQL DDL au mai multe opŃiuni decât alte
instrucŃiuni SQL. Următoarele convenŃii sunt pentru a prezenta sintaxa
instrucŃiunilor DDL:
• Cuvintele cheie şi cuvintele rezervate din SQL sunt scrise cu
majuscule, cum ar fi CREATE TABLE.
• InformaŃiile pe care ar trebui să fie furnizate la scrierea
instrucŃiunilor sunt scrise cu italic, cum ar fi nume_coloană.
• Elementele opŃionale sunt încadrate în paranteze pătrate, cum ar
fi [WITH TIME ZONE].
• OpŃiunile dintr-o listă de elemente posibile sunt separate de o
bară verticală (simbolul logic pentru „sau"), cum ar fi TABLE |
VIEW | INDEX. Se pot întâlni uneori ca listă de elemente
opŃionale, cum ar fi [NULL | NOT NULL].
• Elementele de grup care sunt explicate sau analizate ulterior pe
componente (de obicei după descrierea unui tip principal de
instrucŃiune) sunt încadrate de caracterele „mai mic decât" şi
12
„mai mare decât", cum ar fi <specificaŃii_pentru_coloană>.
• Un element care se poate repeta este urmat de trei puncte, cum ar
fi. [,<restricŃie_pentru_tabel>...].
Toate celelalte simboluri, în special virgulele şi parantezele, fac
parte din sintaxa SQL obligatorie şi, ca urmare, trebuie să fie incluse aşa
cum sunt scrise aici.
Tipuri de date
O coloană este cea mai mică unitate denumită care poate fi referită
într-o bază de date relaŃională. Fiecare, coloană trebuie să aibă asociate
un nume unic şi un tip de date. Un tip de date este o categorie pentru
formatul folosit de o anumită coloană. Tipurile de date asigură câteva
avantaje importante:
• RestricŃionarea, datelor din coloana respectivă la caracterele care
au sens pentru tipul de date specificat.
• Asigurarea unor comportamente utile pentru utilizatorul datelor.
De exemplu, dacă se scade un număr dintr-un alt număr, se
obŃine ca rezultat un număr; dar dacă se scade o dată dintr-o altă
dată, se obŃine ca rezultat diferenŃa în zile dintre cele două date
calendaristice.
• Creşterea eficienŃei sistemului SGBD la stocarea datelor din
coloane.
SQL acceptă trei categorii de tipuri de date: tipuri predefinite,
tipuri construite şi tipuri definite de utilizator.
Tipurile de date predefinite sunt cele furnizate de către producător
ca parte nativă a sistemului SGBD(vor fi tratate în continuare).
Tipurile de date construite, cunoscute şi ca tipuri de colecŃii, conŃin
matrice sau seturi de tipuri de date predefinite, în scopul reprezentării în
SGBD a construcŃiilor de date orientate spre obiect.
Tipurile de date definite de utilizator permit utilizatorului bazei de
date să definească propriile tipuri de date, adaptate unor scopuri specifice.
Ultimele două tipuri de date nu vor fi tratate , fiind prea
complicate pentru intenŃiile cursului.
Tipuri de date predefinite din standardul SQL:2003
13
a tipurilor de date definite de standardul SQL:2003.
Avem următoarele clase de tipuri:
Tipuri de date pentru caractere, tipuri de date numerice,
tipuri de date temporale şi tipuri de date pentru obiecte mari.
14
Acestea sunt utile mai ales pentru atributele folosite în calcule.
Toate tipurile numerice au o precizie (un număr de cifre). De asemenea
unele tipuri numerice au şi o scală (numărul de cifre aflate în dreapta
punctului zecimal). Tipurile întregi şi tipurile numerice care include o
scală sunt numite tipuri numerice exacte, în timp ce numerele reale care
nu include o scală (numerele cu virgulă mobilă) sunt numite numerice
aproximative.
Tipurile numerice standard sunt:
• Numeric - Un tip numeric exact care include o precizie şi o
scală. Sintaxa SQL este:
NUMERIC (precizie, scală)
Exemplu: PLATA_PE_ORA_ANGAJAT NUMERIC(5,2)
• Zecimal - Un tip numeric exact care include o precizie şi o scală.
Diferența față de Numeric, constă în faptul că precizia este mai
mare , având scopul de a permite maparea tipului peste un tip al
al platformei pe care rulează SGBD. Sintaxa SQL este:
DECIMAL(precizie, scală)
Exemplu: PLATA_PE_ORA_ANGAJAT DECIMAL(5,2)
• Întreg - Un tip numeric exact care include numai precizia, scris
INTEGER sau INT. Numerele întregi nu au cifre zecimale, aşa
că scala nu este necesară, deoarece este întotdeauna zero.
Sintaxa SQL este:
INTEGER (precizie) | INT (precizie)
Exemplu: ID_CONT_CLIENT INTEGER
• Întreg mic - O variantă a tipului INTEGER, scrisă SMALLINT,
care stochează numere mai mici şi, ca urmare, ocupă mai puŃin
spaŃiu. Sintaxa SQL este:
SMALLINT (precizie)
Exemplu: ID_CONT_CLIENT SMALLINT
• Întreg mare - O variantă a tipului INTEGER, scrisă BIGINT,
care stocheză numere mai mari şi ocupă mai mult spaŃiu.
Sintaxa SQL este:
BIGINT (precizie)
Exemplu: ID_CONT_CLIENT BIGINT
• Număr în virgulă mobilă - Un tip numeric aproximativ, cu
precizia mai mare sau egală cu precizia specificată. Specificarea
15
preciziei este opŃională. Este scrisă FLOAT. Sintaxa SQL este:
FLOAT (precizie)
Exemple: RATA_DOBANDA FLOAT(16)
RATA_DOBANDA FLOAT
• Număr real - Un tip numeric aproximativ, cu precizie definită de
implementare. Sintaxa SQL este:
REAL
Exemplu: RATA_DOBANDA REAL
• Număr real cu precizie dublă - Un tip numeric aproximativ, cu
precizie definită de implementare, dar mai mare sau egală cu
precizia definită pentru tipul REAL. Sintaxa SQL este:
DOUBLE PRECISION | DOUBLE
Exemplu: RATA_DOBANDA DOUBLE PRECISION
16
Tipurile de date temporale sunt:
17
Există restricŃii referitoare la folosirea tipului de date LONG.
• Într-un tabel poate să fie o singură coloană de tip LONG.
• Nu pot fi comparate două şiruri de caractere de tip LONG.
• O coloană de tip LONG nu poate fi parametru într-o procedură.
• O funcŃie nu poate întoarce ca rezultat o valoare de tip LONG.
• O coloană de tip LONG nu poate fi folosită în clauzele WHERE,
ORDER BY, GROUP BY, CONNECT.
• Operatorii sau funcŃiile Oracle nu pot fi folosiŃi în SQL pentru a
modifica coloane de tip LONG.
• O coloană de tip LONG nu poate fi indexată.
Microsoft Access
Microsoft Access este baza de date care respectă în cea mai mică
măsură standardul, din toate sistemelm SGBD frecvent folosite.
Tipurile de date acceptate de Microsoft Access sunt:
18
• Currency - Un tip de date numeric echivalent cu tipul NUMERIC
(19,4), adică un număr cu 15 cifre în stânga punctului zecimal şi
cel mult 4 cifre în dreapta punctului zecimal.
• AutoNumber - Un câmp pe 4 sau 16 octeŃi (în funcŃie de valoarea
Field Size) incrementat automat cu o unitate de fiecare dată când
în tabel este inserat un nou rând.
• Yes/No - Aproximativ echivalent cu tipul de date standard
BOOLEAN. Totuşi, Microsoft Access permite ca acest tip de
date să fie formatat ca Yes/No, On/Off sau True/False.
• OLE Object - Similar cu tipul de date standard BLOB, acest tip de
date permite stocarea unui obiect Microsoft OLE cu dimensiunea
maximă de 1GB (gigaoctet).
• Hyperlink Un tip de date specializat care poate conŃine o adresă
web din Internet.
• Lookup wizard - Un tip de date specializat care creează o legătură
între o coloană din tabelul curent şi conŃinutul unei coloane dintr-
un alt tabel. Acest tip de date poate fi folosit pentru legarea
dinamică a tabelelor la crearea formularelor în Microsoft Access.
Oracle
19
• INTERVAL - Un interval de timp, dar sunt acceptate din
standard numai variantele INTERVAL YEAR TO MONTH şi
INTERVAL DAY TO SECOND.
• NCHAR - Şiruri de caractere cu lungime fixă într-o limbă
naŃională, cu dimensiunea maximă de 2000 de octeŃi.
• NCLOB - Şiruri de caractere cu lungime fixă într-o limbă
naŃională, cu dimensiunea maximă de (4GB-1) x (dimensiunea
unui bloc din baza de date).
• NUMERIC - Implementat NUMBER(precizie,scală).
• NVARCHAR - Date de tip caracter cu lungime variabilă într-o
limbă naŃională, cu dimensiunea maximă de 4000 de octeŃi .
• REAL - Implementat ca NUMBER.
• SMALLINT - Implementat ca NUMBER(38).
• TIMESTAMP - Data şi ora în ani, luni, zile, ore, minute şi
secunde.
• VARCHAR - Date de tip caracter cu lungime variabilă,
conŃinând cel mult 4000 de caractere.
Precizari si exemple
20
Pentru memorarea şirurilor de caractere, cele mai frecvent tipuri
de date utilizate sunt: CHAR, NCHAR, VARCHAR2 , NVARCHAR2 şi
LONG.
InformaŃii relative la timp sau dată calendaristică se obŃin
utilizând tipul DATE. Pentru fiecare dată de tip DATE sunt depuse:
secolul, anul, luna, ziua, ora, minutul, secunda. Pentru o coloană de tip
DATE sistemul rezervă 7 bytes, indiferent dacă se memorează doar
timpul, sau doar data calendaristică.
Formatul implicit al datei se defineşte cu ajutorul parametrului de
iniŃializare NLS_DATE_FORMAT. În general, acest parametru este setat
la forma DD-MON-YY. Dacă nu este specificat timpul, timpul implicit
este 12:00:00.
Oracle9i introduce noi tipuri de date pentru timp:
• TIMESTAMP(precizie_fracŃiuni_secundă) cuprinde
valori pentru anul, luna şi ziua unei date calendaristice, dar şi valori
pentru oră, minut, secundă
• INTERVAL YEAR (precizie_an) TO MONTH stochează o
perioadă de timp specificată în ani şi luni, unde precizie_an reprezintă
numărul de cifre din câmpul YEAR.
• INTERVAL DAY (zi) TO SECOND (prec_frac_sec)
stochează o perioadă de timp reprezentată în zile, ore, minute şi secunde.
Exemple
21
VALUES (TIMESTAMP '2009-10-31 09:26:50.30',
INTERVAL '23 7:44:22' DAY TO SECOND,
INTERVAL '19-02' YEAR TO MONTH);
Ex. 2
INSERT INTO timp
VALUES (TIMESTAMP '2010-02-20 09:20:40.20',
INTERVAL '23 7:44:22' DAY TO SECOND,
INTERVAL '200-02' YEAR TO MONTH);
Ex.3
INSERT INTO timp
VALUES (TIMESTAMP '2010-01-25 09:26:50.30',
INTERVAL '23 7:44:22' DAY TO SECOND,
INTERVAL '20-100' YEAR TO MONTH);
22
INTERVAL '20-100' eroare, deoarece 100 de luni adauga ani
Ex.4
INSERT INTO timp
VALUES (TIMESTAMP '2010-01-25 09:26:50.30',
INTERVAL '23 2:2:2' DAY TO SECOND,
INTERVAL '20-10' YEAR TO MONTH);
Ex.5
INSERT INTO timp
VALUES (TIMESTAMP '2010-01-25 09:26:50.30',
INTERVAL '23 2:2:2' DAY TO SECOND,
INTERVAL '20-12' YEAR TO MONTH);
Ex.6
23
INSERT INTO timp
VALUES (TIMESTAMP '2010-01-25 09:26:50.30',
INTERVAL '23 2:2:2' DAY TO SECOND,
INTERVAL ‘120’ MONTH);
Ex.7
INSERT INTO timp
VALUES (TIMESTAMP '2010-01-25 09:26:50.30',
INTERVAL '360' SECOND,
INTERVAL ‘144’ YEAR);
24
Se observa că inregistrarea introdusa are :
• Durata_1 este de 6 min(360 sec) si
• Durata_2 de 12 ani(144 de zile)
Ex.8
INSERT INTO timp
VALUES (TIMESTAMP '2010-01-25 09:00:00.30',
INTERVAL '20' DAY(2),
INTERVAL ‘10’ YEAR);
25
Exemplu
CREATE TABLE exemplu
(durata INTERVAL YEAR(3) TO MONTH);
Exemplu
CREATE TABLE noi_carti
(codel NUMBER,
start_data TIMESTAMP);
26
Pentru informatii de tip TIMESTAMP, numarul maxim de digiti
pentru fractiuni de secunda este 9, implicit este 6.
Rezultatele cererii:
1
31-OCT-09 09.26.50.300000 AM
5
08-FEB-12 12.00.00.000000 AM
27
Modele de format
Un model de format este un literal caracter care descrie formatul
valorilor de tip DATE sau NUMBER stocate într-un şir de caractere.
Atunci când se converteşte un şir de caractere într-o dată calendaristică
sau într-un număr, modelul de format indică sistemului cum să
interpreteze şirul respectiv.
În instrucŃiunile SQL se poate folosi un model de format ca
argument al funcŃiilor TO_CHAR şi TO_DATE. În felul acesta se poate
specifica formatul folosit de sistemul Oracle pentru a returna sau a stoca
o valoare în/din baza de date. Un model de format nu schimbă
reprezentarea internă a valorii în baza de date.
O parte dintre elementele cel mai frecvent întâlnite ale unui format
de tip numeric sunt sintetizate în tabelul următor.
28
sau ultima poziŃie a modelului de format numeric.
Element ExplicaŃie
AD sau A.D. Indicatorul AD (Anno Domini) cu sau fără puncte.
BC sau B.C. Indicatorul BC (Before Christ) cu sau fără puncte.
Numărul zilei din săptămână (1-7). Duminica este considerată prima
D
zi a săptămânii.
DAY Numele zilei completat cu spaŃii, până la lungimea de 9 caractere.
DD Numărul zilei din lună (1-31).
DDD Numărul zilei din an (1-366).
DY Numele zilei din săptămână, printr-o abreviere de 3 litere.
FF FracŃiunile de secundă.
HH sau HH12 Ora din zi (1-12).
HH24 Ora din zi (0-23).
MI Minutele din oră (0-59).
MM Luna din an (01-12).
MON Numele lunii, printr-o abreviere de 3 litere.
29
MONTH Numele lunii completat cu spaŃii, până la lungimea de 9 litere.
RM Luna în cifre romane (I-XII).
RR Anul cel mai apropiat de data curentă.
Acceptă intrarea atât cu 2, cât şi cu 4 cifre. Dacă anul de intrare se dă
RRRR
cu 2 cifre, furnizează acelaşi lucru ca şi formatul RR.
SS Secundele din minut (0-59).
SSSSS Secundele trecute de la miezul noptii (0-86399).
TZH Ora regiunii.
TZM Minutul regiunii.
Y,YYY Anul scris cu virgulă după prima cifră.
YEAR sau SYEAR Anul în litere („S“ prefixează anii i.Hr. cu semnul minus).
YYYY sau SYYYY Anul cu 4 cifre.
YYY, YY sau Y Ultimele cifre ale anului.
Exemplu:
Nume Angajare
Pop January 21,2009
Ene October 10,2009
Exemplu
SELECT nume, TO_CHAR(data_ang,’ DDTH ”of” Month YYYY’)
angajare
FROM angajati
WHERE TO_CHAR(data_ang, ’YYYY’)=’2009’;
30
Nume Angajare
Pop 21st of January 2009
Ene 10TH of October 2009
Exemplu
SELECT TO_CHAR(data_ang,’ fmDDspth Month.Year’) angajare
FROM angajati
WHERE id_ang=100;
Angajare
Twenty_NineTH July, Two Thousand Nine
Exemplu
SELECT ’sesiune incepe pe ’|| TO_DATE(’20-09-2010’,’DD-MM-
YYYY’) from dual;
Sesiunea incepe pe 20-09-2010
Pseudocoloane
31
urma execuŃiei unei cereri. Pseudocoloana poate fi utilizată pentru a
limita numărul de linii returnate. Dacă este folosită clauza ORDER BY
într-o subcerere, iar condiŃia în care apare ROWNUM este plasată în
cererea de nivel superior, atunci condiŃia va fi aplicată după ordonarea
liniilor.
Exemplu:
Să se afişeze informaŃii despre operele de artă având cele mai mici
10 coduri.
SELECT *
FROM (SELECT * FROM opera ORDER BY cod_opera)
WHERE ROWNUM < 11;
32
Standardul SQL prevede şi o instrucŃiune CREATE SCHEMA,
care permite crearea unor grupuri de obiecte din baza de date, pentru
simplificarea administrării.
FILM
FILM_ID PK
MPAA_RATING FILM_COD_GEN FK1 FILM_GEN
MPAA_COD_RATING PK MPAA_COD_RATING FK2 FILM_COD_GEN PK
MPAA_DESCRIERE_VARSTE FILM_NUME FILM_DESCRIERE_GEN
RETAIL_PRET_VHS
RETAIL_PRET_DVD
AN_PRODUS
FILM_COPII FILM_INCHIRIERE
FILM_LIMBA FILM_ID PK,FK1 FILM_ID PK,FK1
FILM_ID PK,FK1 NUMAR_COPIE PK NUMAR_COPIE PK,FK1
COD_LIMBA PK,FK2 DATA_CUMPARARE TRANZACTIE_ID PK,FK2
DATA_VANZARE DATA_INTOARCERE
FORMAT_MEDIA COST_INCHIRIERE
COST_INTARZIERE_SAU_PIERDERE
DATA_RETURNARE
LIMBA
COD_LIMBA PK CLIENT_CONT CLIENT_TRANZACTIE
NUME_LIMBA CLIENT_CONT_ID PK TRANZACTIE_ID PK
CLIENT_HOLD_IND CLIENT_CONT_ID FK1
DATA_INSCRIS ANGAJAT_PERSOANA_ID
DATA_TERMINAT TRANZACTIE_DATA
CLIENT_COD_PERSOANA CLIENT_DEPOZIT_SUMA VANZARI_TAXA
CLIENT_CONT_ID PK,FK1 CARD_CREDIT_LA_DOSAR_INDIC
PERSOANA_ID PK,FK2 COPIL_INCHIRIERE_PERMIS_INDIC
PERSOANA ANGAJAT
PERSOANA_ID PK PERSOANA_ID PK,FK1
PERSOANA_PRENUME SUPERVISOR_PERSOANA_ID
PERSOANA_NUME_MIJLOCIU ANGAJAT_TAXA_ID
PERSOANA_NUME ANGAJAT_JOB_CATEGORIE
PERSOANA_ADRESA_1 ANGAJAT_RATA_PE_ORA
PERSOANA_ADRESA_2 ANGAJARE_DATA
PERSOANA_ADRESA_ORAS INCHIDERE_DATA
PERSOANA_ADRESA_JUDET_PROV
PERSOANA_ADRESA_COD_POSTAL
PERSOANA_ADRESA_TARA
PERSOANA_TELEFON
NASTERE_DATA
MOARTE_DATA
33
Exemplu de Diagrama conceptuală
Exemplele din lucrare se referă la un model de gestiune a operelor
de artă dintr-un muzeu. Diagrama entitate-relaŃie (E/R) corespunzătoare
modelului analizat este prezentată în figura 2.1.
Schemele relaŃionale corespunzătoare diagramei conceptuale sunt:
• OPERA(cod_opera#, tip, titlu, cod_artist, data_crearii,
data_achizitiei, valoare, cod_galerie, cod_sala,
material, stil,
dim1, dim2, stare);
• GALERIE(cod_galerie#, nume_galerie, nume_cladire, adresa);
• SALA(cod_sala#, cod_galerie#, nume_sala, suprafata);
• SECURITATE(cod_opera#, sistem#, descriere, valoare,
data_inst, firma, executant);
• POLITA_ASIG(cod_opera, cod_polita#, descriere, firma,
valoare, semnat_contract);
• SPECIALIST(cod_specialist#, nume, prenume, experienta,
autorizatie, tip, loc);
• ARTIST(cod_artist#, nume, prenume, an_nastere, an_moarte,
nationalitate, observatii);
• SURSA_BIBLIO(cod_sursa#, tip, titlu);
• ARTICOL(cod_sursa#, nume_revista, nr_revista, data,
pag_revista);
• CARTE(cod_sursa#, an, editura, ISBN);
• AUTOR(cod_autor#, nume, prenume, descriere, job, adresa);
• EXPOZITIE(cod_expo#, nume_expo, tip);
• STUDIAZA(cod_opera#, cod_specialist#, data#, operatia,
metode, rezultat, plata);
• MENTIONATA_IN(cod_opera#, cod_sursa#, pagina#);
• FIGUREAZA_IN(cod_opera#, cod_expo#, cond_speciale,
localitate, data_inceput);
• EDITATA_DE(cod_sursa#, cod_autor#);
• ORGANIZATA_DE(cod_organizator#, cod_expozitie#);
• ORGANIZATOR(cod_organizator#, nume, tip_organizator,
adresa).
Numele atributelor corespunzătoare relaŃiilor menŃionate sunt
sugestive, astfel încât semnificaŃia lor este evidentă. Se pot face
următoarele precizări:
• câmpul tip din tabelul opera este un şir de caractere ce
reprezintă categoria în care se încadrează opera de artă
respectivă (pictură, sculptură, grafică, tapiserie etc.);
34
• material este un câmp de tip şir de caractere, ale cărui valori
specifică materialul din care a fost concepută opera de artă
(hârtie, pânză, bronz, aramă, piatră etc.);
• stil este un atribut de tip şir de caractere ce precizează stilul în
care se încadrează opera de artă respectivă (clasic, renascentist,
contemporan, impresionist etc.);
• dim1, dim2 sunt coloane numerice care specifică dimensiunile
operei;
• stare este un atribut de tip şir de caractere care precizează starea
fizică a operei de artă respective (medie, bună, foarte bună etc.);
• câmpul tip din tabelul specialist precizează calificarea unui
specialist (expert, restaurator, evaluator etc.);
• loc reprezintă Ńara care a emis autorizaŃia corespunzătoare unui
specialist;
• operatia specifică acŃiunea la care este supusă opera de artă
(lipire, retuşare, curăŃare etc.);
• atributul metode specifică procedeele utilizate în realizarea
operaŃiei (infraroşii, ultrasunete, raze X etc.);
• câmpul rezultat reŃine informaŃii asupra expertizei efectuate
(dacă opera de artă este un fals, nivelul restaurării etc.);
• câmpul tip din tabelul sursa_biblio precizează categoria
publicaŃiei în care apar referinŃe la opera de artă respectivă
(articol, studiu, monografie etc.);
• câmpul job specifică profesia autorului unei surse bibliografice
(profesor, expert, artist, jurnalist etc.);
• atributul observatii conŃine informaŃii referitoare la artişti
(pseudonim, curentul artistic de care aparŃin etc.);
• câmpul tip din tabelul expozitie precizează categoria din care
face parte o expoziŃie (fixă, itinerantă, temporară etc.).
35
GALERIE 1 inclusa_in M(1)
cod_galerie SALA
# cod_sala#
cod_galerie#
1
cuprinsa_in SECURITATE
M(1) cod_opera#
1 protejata_prin M(0)
sistem#
1 creata_de M(1)
ARTIST OPERA
1 asigurata_de M(0)
cod_artist# cod_opera# POLITA_ASIG
M(1) cod_polita#
M(1) M(1)
mentionata_in
studiaza
M(0)
M(0)
SURSA_BIBLIO
cod_sursa# SPECIALIST
tip
cod_specialist#
ARTICOL
figureaza_in
CARTE M(0)
M(1)
AUTOR
cod_autor
#
36
Asupra modelului considerat sunt definite anumite restricŃii.
Conform acestora, o operă de artă poate:
• avea fie un singur autor, fie autor necunoscut;
• avea mai multe sisteme de securitate instalate;
• fi asigurată la mai multe firme;
• fi menŃionată în aceeaşi sursă bibliografică, la pagini diferite.
37
[<restricŃie_coloană>]
M(1) M(0)
CITITOR
CARTE
imprumuta codec#
codel# nume
titlu dep
autor
pret
nrex
coded M(0) 1
DOMENIU
apartine coded#
intdom
38
CREATE TABLE carte
(codel CHAR(5) PRIMARY KEY,
titlu VARCHAR2(30),
autor VARCHAR2(30),
pret NUMBER(8,2),
nrex NUMBER(3),
coded CHAR(5) NOT NULL
REFERENCES domeniu(coded));
Constrângerea de cheie primară sau externă ce presupune?
CREATE TABLE carte
(codel CHAR(5) PRIMARY KEY,
titlu VARCHAR2(30),
autor VARCHAR2(30),
pret NUMBER(8,2),
nrex NUMBER(3),
coded CHAR(5) NOT NULL
REFERENCES domeniu(coded)
ON DELETE CASCADE);
CREATE TABLE domeniu
(coded CHAR(5) PRIMARY KEY,
den_dom VARCHAR2(30));
39
REFERENCES domeniu (coded));
Dacă cheia primară are mai mult de o coloană atunci cheile trebuie
indicate la nivel de tabel.
CREATE TABLE imprumuta
(codel CHAR(5),
codec CHAR(5),
dataim DATE DEFAULT SYSDATE,
datares DATE,
dataef DATE,
PRIMARY KEY (codel, codec, dataim),
FOREIGN KEY (codel)
REFERENCES carte(codel),
FOREIGN KEY (codec)
REFERENCES cititor(codec));
40
Constrângeri
RestricŃiile coloanelor
Descriere tabel:
ID_CONT_CLIENT - cheie primara
STARE_CONT_CLIENT de tip DA/NU, care indica daca un cont este sau nu
blocat.(inchirierile nu sunt permise pt conturile blocate)
DATA_INSCRIERE - data la care s-a deschis contul in magazin
DATA_INCHEIERE - daca contul a fost inchis contine data; daca contul e activ
contine NULL
41
SUMA_DEPOZIT_CLIENT – pt clientii care nu au carte de credit, suma lasata ca
depozit in magazin
IND_CREDIT_CARD - de tip DA/NU, care indica daca un client a lasat inf despre
carte de credit, pt a garanta platile asociate contului.
INDIC_PERMIS_INCHIRIERE_COPIL - de tip DA/NU, care indica daca
uncopil<18 ani are voie sa inchirieze filme folosind acest cont
Exemplu:
IND_CREDIT_CARD CHAR(1) NOT NULL,
CHECK (CREDIT_CARD_INDIC IN (‘Y’, ‘N’))
42
CONSTRAINT CK_CUST _ACCT _ID
CHECK (ID_CONT IS NOT NULL)
43
RestricŃiile tabelelor
Exemplu:
CONSTRAINT CK_SUMA_DEPOZITE_CLIENT
CHECK (SUMA_DEPOZITE_CLIENT >= 0 OR
SUMA_DEPOZITE_CLIENT IS NULL)
• RestricŃia UNIQUE
[CONSTRAINT nume_restricŃie]
PRIMARY KEY (nume_coloană [,nume_coloană...])
RestricŃia de mai jos este chiar definiŃia cheii primare din tabelul
CONT_CLIENT [
Exemplu:
CONSTRAINT PK_CONT_CLIENT
PRIMARY KEY (ID_CONT_CLIENT)
44
Spre deosebire de forma pentru restricŃia referenŃială a coloanei,
aceasta poate referi mai multe coloane.
[CONSTRAINT nume_restricŃie]
FOREIGN KEY (nume_coloană [,nume coloană...])
REFERENCES nume_tabel (nume_coloană [,nume_coloană...
[ON DELETE CASCADE |ON DELETE SET NULL]
Exemple:
1. Să se definească o constrângere la nivel de coloană prin care să
se specifice cheia primară şi cheia externă.
CREATE TABLE carte
(codel CHAR(5)
CONSTRAINT cp_carte PRIMARY KEY,
Titlu VARCHAR2(30),
autor VARCHAR2(30),
pret NUMBER(8,2),
nrex NUMBER(3),
coded CHAR(5)
CONSTRAINT nn_coded NOT NULL
CONSTRAINT ce_coded
REFERENCES domeniu(coded));
45
ObservaŃii:
• Liniile ce nu respectă constângerea sunt depuse automat într-un
tabel special.
• Constrângerile previn ştergerea unui tabel dacă există
dependenŃe.
• Constrângerile pot fi create o dată cu tabelul sau după ce acesta
a fost creat.
• Constrângerile pot fi activate sau dezactivate în funcŃie de
necesităŃi.
46
Constrângerea de integritate referenŃială asigură coerenŃa între
cheile primare şi cheile externe corespunzătoare. Când este definită o
cheie externă sistemul Oracle verifică:
• dacă a fost definită o cheie primară pentru tabelul referit de
cheia externă;
• dacă numărul coloanelor ce compun cheia externă corespunde
numărului de coloane a cheii primare;
• dacă tipul şi lungimea fiecărei coloane a cheii externe
corespunde cu tipul şi lungimea fiecărei coloane a cheii
primare.
47
Comanda care realizează modificarea structurii tabelului (la nivel de
coloană sau la nivel de tabel), dar nu modificarea conŃinutului acestuia,
este ALTER TABLE.
48
• Adăugarea unei coloane la un tabel. Definirea coloanei se face cu
aceeaşi sintaxă ca şi în cazul instrucŃiunii CREATE TABLE. Nu se
poate specifica unde să apară coloana, ea devenind ultima coloană a
tabelului.
49
• Adăugarea unei restricŃii. DefiniŃia restricŃiei este identică cu
definiŃia unei restricŃii care ar putea apărea într-o instrucŃiune
CREATE TABLE.
ALTER TABLE nume_tabel
ADD CONSTRAINT <definiŃie_restricŃie>;
Exemplu:
ALTER TABLE CONT_CLIENT
ADD CONSTRAINT CK_SUMA_DEPOZIT_CLIENT
CHECK (SUMA_DEPOZIT_CLIENT >= 0
OR SUMA_DEPOZIT_CLIENT IS NULL);
50
în comanda ALTER apare opŃiunea CASCADE, care determină şi
ştergerea cheilor externe ce referă cheia primară.
ALTER TABLE domeniu
DROP PRIMARY KEY CASCADE;
• Ştergerea cheii externe se face ca si pentru ştergerea cheii
primare
ALTER TABLE carte
ADD CONSTRAINT beta
FOREIGN KEY (coded) REFERENCES domeniu;
51
• Pentru a activa (ENABLE) sau dezactiva (DISABLE)
constrângeri:
ALTER TABLE nume_tabel
ENABLE nume_constrangere;
ObservaŃii
• Nu se poate specifica poziŃia unei coloane noi în structura tabelului. O
coloană nouă devine automat ultima în cadrul structurii tabelului.
52
• Modificarea unei coloane presupune schimbarea tipului de date, a
dimensiunii sau a valorii implicite a acesteia. O schimbare a valorii
implicite afectează numai inserările care succed modificării.
• Dimensiunea unei coloane numerice sau de tip caracter poate fi
mărită, dar nu poate fi micşorată decât dacă acea coloană conŃine
numai valori null sau dacă tabelul nu conŃine nici o linie.
• Tipul de date al unei coloane poate fi modificat doar dacă valorile
coloanei respective sunt null.
• Alte opŃiuni ale comenzii ALTER TABLE, care au apărut începând cu
versiunea Oracle8i, sunt SET UNUSED şi DROP UNUSED
COLUMNS:
ALTER TABLE nume_tabel
ET UNUSED [ ( ] nume_coloană [ ) ];
ALTER TABLE nume_tabel
DROP UNUSED COLUMNS;
OpŃiunea SET UNUSED permite marcarea uneia sau mai multor
coloane ca fiind nefolosite, cu scopul de a fi şterse atunci când
necesităŃile sistemului impun acest lucru. Coloanele nefolosite sunt tratate
ca şi cum ar fi fost suprimate, deşi datele acestora rămân în liniile
tabelului. După ce o coloană a fost marcată UNUSED, utilizatorul nu mai
are acces la aceasta prin intermediul cererilor. În plus, numele şi tipurile
de date ale coloanelor nefolosite nu vor fi afişate cu comanda DESCRIBE
din SQL*Plus. Utilizatorul poate să adauge tabelului o nouă coloană
având acelaşi nume cu cel al unei coloane nefolosite.
DROP UNUSED COLUMNS şterge din tabel toate coloanele
marcate ca fiind nefolosite. Acest lucru se poate utiliza pentru eliberarea
spaŃiului de pe disc corespunzător coloanelor nefolosite din tabel. Dacă
tabelul nu conŃine nici o coloană nefolosită, instrucŃiunea nu întoarce o
eroare şi nu are nici un efect.
Atunci când se elimină o coloană dintr-un tabel folosind opŃiunea
DROP a comenzii ALTER TABLE, vor fi suprimate şi coloanele din tabel
care sunt marcate cu opŃiunea SET UNUSED.
53
constrângeri de integritate referenŃială, sistemul returnează o eroare şi nu
suprimă tabelul.
Suprimarea unui tabel presupune:
0suprimarea definiŃiei sale în dicŃionarul datelor;
0suprimarea indecşilor asociaŃi;
0suprimarea privilegiilor conferite în legătură cu tabelul;
0recuperarea spaŃiului ocupat de tabel;
0permanentizarea tranzactiilor in asteptare;
0invalidarea (dar nu suprimarea) funcŃiilor, procedurilor,
vizualizărilor, secventelor, sinonimelor referitoare la tabel.
Odată executată, instrucŃiunea DROP TABLE este ireversibilă. Ca
şi în cazul celorlalte instrucŃiuni ale limbajului de definire a datelor,
această comandă nu poate fi anulată (ROLLBACK).
InstrucŃiunea DROP
54
Tipul de obiect specifică tipul obiectului care urmează să fie şters, cum ar
fi INDEX, TABLE sau VIEW.
OpŃiunile de ştergere sunt specifice fiecărui SGBD. Sintaxa este diferită
de la un producător la altul — PostgreSQL şi MySQL folosesc în acest
scop cuvântul cheie CASCADE, în timp ce în Oracle trebuie să se
folosească CASCADE CONSTRAINTS.
Exemplu:
DROP TABLE COD_CLIENT;
DROP TABLE COD_CLIENT CASCADE CONSTRAINTS; (Oracle)
DROP INDEX IX_TITLU_FILM;
55
DESCRIBE USER_TABLES;
Exemplu:
SELECT 'DROP TABLE ' || OBJECT_NAME || ';'
FROM USER_OBJECTS
WHERE OBJECT_TYPE = 'TABLE';
56
InstrucŃiunea CREATE INDEX
57
un tabel sau cluster;
• unui tabel imbricat.
58
• manual, de către utilizator (CREATE INDEX).
Server-ul Oracle creează automat un index unic atunci când se
defineşte o constrângere PRIMARY KEY sau UNIQUE asupra unei
coloane sau unui grup de coloane. Numele indexului va fi acelaşi cu
numele constrângerii.
Indecşii, fiind obiecte ale schemei bazei, beneficiază de procesul de
definire a unui obiect.
Crearea unui index (care nu este obligatoriu unic) pe una sau mai
multe coloane ale unui tabel se face prin comanda:
59
ON cititor (adresa DESC);
CREATE INDEX cit_den
ON cititor (DEN_CITITOR);
2) Să se afişeze informaŃiile referitoare la indexul cititor_idx.
SELECT TABLE_NAME
FROM USER_INDEXES
WHERE INDEX_NAME=’CITITOR_IDX’;
SELECT TABLE_NAME
FROM USER_INDEXES
WHERE INDEX_NAME=’CIT_DEN’;
60
Crearea unui index nu este recomandată în următoarele cazuri:
• tabelul este mic;
• coloanele nu sunt utilizate des în cadrul unei condiŃii dintr-o
cerere;
• majoritatea interogărilor regăsesc mai mult de 2-4% din liniile
tabelului;
• tabelul este actualizat frecvent;
• coloanele indexate sunt referite în expresii.
In concluzie, ce tabele sau ce coloane trebuie (sau nu) indexate?
• indexaŃi tabelele pentru care interogările selectează un număr redus
de rânduri (sub 5%);
• indexaŃi tabelele care sunt interogate folosind clauze SQL simple;
• nu indexaŃi tabelele ce conŃin puŃine înregistrări (accesul secvenŃial
este mai simplu);
• nu indexaŃi tabelele care sunt frecvent actualizate, deoarece
ştergerile, inserările şi modificările sunt îngreunate de indecşi;
• indexaŃi coloanele folosite frecvent în clauza WHERE sau în clauza
ORDER BY;
• nu indexaŃi coloanele ce conŃin date asemănătoare (puŃine valori
distincte);
Exemplu:
Pentru a asigura că server-ul Oracle utilizează indexul şi nu
efectuează o căutare asupra întregului tabel, valoarea funcŃiei
corespunzătoare expresiei indexate trebuie să nu fie null în interogările
ulterioare creării indexului. Următoarea instrucŃiune garantează utilizarea
indexului dar, în absenŃa clauzei WHERE, server-ul Oracle ar putea
cerceta întreg tabelul.
SELECT * FROM artist
WHERE UPPER(nume) IS NOT NULL
ORDER BY UPPER(nume);
Exemplu:
Să se creeze tabelul artist, specificându-se indexul asociat cheii
primare.
CREATE TABLE artist(
cod_artist NUMBER
PRIMARY KEY USING INDEX
(CREATE INDEX artist_cod_idx
ON artist(cod_artist)),
61
nume VARCHAR2(30) NOT NULL,
prenume VARCHAR2(30));
62
USER_IND_COLUMNS din dicŃionarul datelor. Dintre coloanele
tabelului USER_INDEXES se remarcă, dând
Desc USER_INDEXES;
INDEX_NAME Numele indexului
INDEX_TYPE Tipul indexului (NORMAL, LOB, CLUSTER etc.)
TABLE_OWNER Proprietarul tabelului indexat
TABLE_NAME Numele tabelului indexat
TABEL_TYPE Tipul tabelului indexat (TABLE, CLUSTER etc.)
UNIQUENESS Starea de unicitate (UNIQUE, NONUNIQUE)
TABLESPACE_NAME SpaŃiul tabel în care este stocat indexul
INITIAL_EXTENT SpaŃiul alocat pentru prima extensie
NEXT_EXTENT SpaŃiul alocat pentru următoarea extensie
MIN_EXTENTS Numărul minim de extensii alocate
MAX_EXTENTS Numărul maxim de extensii
PCT_INCREASE Procentul cu care cresc extensiile
BLEVEL Nivelul din B-arbore. Acesta arată adâncimea indexului de
la ramuri la frunze
LEAF_BLOCKS Numărul de blocuri frunză din index
DISTINCT_KEYS Numărul de chei distincte în index
STATUS Starea indexului (VALID, INVALID, DIRECT_LOAD)
NUM_ROWS Numărul de linii utilizate. Acesta nu trebuie să includă
valorile NULL din tabelul de bază
PARTITIONED Determină dacă indexul este partiŃionat (YES sau NO)
GENERATED Determină dacă sistemul a generat numele indexului (Y)
sau utilizatorul (N)
63
Pentru a vedea informatiile din USER_IND_COLUMNS, avem
Desc USER_IND_COLUMNS;
Exemplu:
Să se obŃină informaŃii referitoare la indecşii tabelului carte.
64
SELECT a.index_name, a.column_name,
a.column_position poz, b.uniqueness
FROM user_indexes b, user_ind_columns a
WHERE a.index_name = b.index_name
AND a.table_name = ’CITITOR’;
SecvenŃe
65
Crearea unei secvenŃe se face cu ajutorul comenzii:
66
NEXTVAL şi CURRVAL pot fi folosite în:
• clauza VALUES a unei comenzi INSERT;
• clauza SET a unei comenzi UPDATE;
• lista SELECT a unei subcereri dintr-o comanda INSERT;
• lista unei comenzi SELECT.
NEXTVAL şi CURRVAL nu pot fi folosite în:
• subinterogare in SELECT, DELETE sau UPDATE;
• interogarea unei vizualizări;
• comandă SELECT cu operatorul DISTINCT;
• comandă SELECT cu clauza GROUP BY, HAVING sau ORDER
BY;
• clauza WHERE a unei comenzi SELECT;
• condiŃia unei constrângeri CHECK;
• valoarea DEFAULT a unei coloane într-o comandă CREATE
TABLE sau ALTER TABLE;
• comandă SELECT care este combinată cu altă comandă SELECT
printr-un operator mulŃime (UNION, INTERSECT, MINUS).
Din dicŃionarul datelor pot fi obŃinute informaŃii despre secvenŃe
folosind vizualizarea USER_SEQUENCES.
67
SELECT INCREMENT_BY,MAX_VALUE, MIN_VALUE
FROM USER_SEQUENCES
WHERE SEQUENCE_NAME = 'DOMENIUSEQ’;
Modificarea unei secvenŃe se face prin comanda ALTER
SEQUENCE. Sintaxa comenzii este similară instrucŃiunii CREATE
SEQUENCE , dar:
• noua valoare maximă pentru MAXVALUE nu poate fi mai mică
decât valoarea curentă;
• opŃiunea START WITH nu poate fi modificată de comandă.
Sinonime
68
Oracle oferă posibilitatea de a atribui mai multe nume aceluiaşi
obiect. Aceste nume adiŃionale sunt numite sinonime (synonymes). Ele
sunt utile deoarece permit simplificarea formulării cererii şi referirea la
obiecte, fără a fi nevoie să se specifice proprietarii obiectelor sau
localizarea acestora(schema).
Spre deosebire de alias a cărui durată de viaŃă este limitată la
cererea ce conŃine alias-ul, sinonimele sunt salvate în dicŃionarul datelor
şi pot fi reutilizate.
Sistemul Oracle permite crearea de sinonime pentru obiecte de
tipul: tabel, vizualizare, secvenŃă, funcŃie, procedură, pachet, clişeu,
sinonim.
Crearea unui sinonim se realizează prin comanda:
69
Vizualizări
70
O vizualizare reflectă la orice moment conŃinutul exact al tabelelor
de bază. Orice modificare efectuată asupra tabelelor se repercutează
instantaneu asupra vizualizării. Ştergerea unui tabel implică invalidarea
vizualizărilor asociate tabelului şi nu ştergerea acestora.
Vizualizările sunt definite pentru:
• furnizarea unui nivel mai înalt de securizare a bazei;
• simplificarea formulării unei cereri;
• mascarea complexităŃii datelor;
• afişarea datelor într-o altă reprezentare decât cea a tabelelor de
bază;
• asigurarea independenŃei datelor;
• asigurarea confidenŃialităŃii anumitor informaŃii;
• definirea constrângerilor de integritate;
• restricŃionarea acesului la date.
Vizualizările pot fi simple şi complexe. O vizualizare simplă
extrage date dintr-un singur tabel, nu conŃine funcŃii sau grupări de date şi
asupra ei pot fi efectuate operaŃii LMD.
O vizualizare este considerată complexă dacă extrage date din mai
multe tabele, conŃine funcŃii sau grupări de date şi nu permite întotdeauna
(prin intermediul său) operaŃii LMD asupra tabelelor de bază.
OperaŃiile LMD asupra vizualizărilor complexe sunt restricŃionate
de următoarele reguli:
• nu se poate insera, actualiza sau şterge o linie dintr-o vizualizare
dacă aceasta conŃine funcŃii grup, clauza GROUP BY, cuvântul
cheie DISTINCT sau pseudocoloana ROWNUM;
• nu se poate adăuga sau modifica o linie dintr-o vizualizare, dacă
aceasta conŃine coloane definite prin expresii;
• nu pot fi adăugate linii printr-o vizualizare, dacă tabelul de bază
conŃine coloane care au constrângerea NOT NULL şi nu apar în
lista SELECT a vizualizării.
Pentru a obŃine informaŃii referitoare la vizualizările definite, se pot
interoga vizualizările USER_VIEWS şi ALL_VIEWS din dicŃionarul
datelor. Textul instrucŃiunii SELECT care defineşte o vizualizare este
stocat într-o coloană de tip LONG, numită TEXT.
Atunci când datele sunt accesate prin intermediul unei vizualizări,
server-ul Oracle efectuează următoarele operaŃii:
• recuperează definiŃia acesteia din USER_VIEWS;
• verifică privilegiile de acces la tabelele ei de bază;
• converteşte cererea într-o operaŃie echivalentă asupra tabelelor
de bază.
71
Crearea unei vizualizări se realizează cu ajutorul comenzii:
CREATE [OR REPLACE][FORCE | NOFORCE] VIEW
[<nume_schema>.]<nume_view> [(<alias>[,<alias>]…)]
AS <cerere_SELECT>
[WITH {CHECK OPTION [CONSTRAINT <nume_constrangere>] |
READ ONLY }];
– OR REPLACE recreează vizualizarea dacă aceasta deja există.
– FORCE creează vizualizarea chiar dacă tabelul de bază nu există sau
chiar dacă vizualizarea face referinŃă la obiecte care încă nu sunt
create. Deşi vizualizarea va fi creată, utilizatorul nu poate să o
folosească.
– NO FORCE este implicită şi se referă la faptul că vizualizarea este
creată numai dacă tabelele de bază există.
– Cererea este o comandă SELECT care poate să conŃină alias pentru
coloane.
– WITH CHECK OPTION specifică faptul că reactualizarea datelor din
tabele (inserare sau modificare) se poate face numai asupra datelor
selectate de vizualizare (care apar în clauza WHERE).
– WITH READ ONLY asigură că nici o operaŃie LMD nu poate fi
executată asupra vizualizării.
Exemplu:
Să se genereze o vizualizare care conŃine informaŃii referitoare la
împrumutul cărŃilor şi în care să fie implementată constrîngerea că orice
carte, care există într-un singur exemplar, poate fi împrumutată maximum
15 zile.
CREATE VIEW imprumutare
AS SELECT *
FROM imprumuta
WHERE codel NOT IN
(SELECT codel
FROM carte
WHERE nrex = 1)
OR datares - dataim < 15
WITH CHECK OPTION;
Cererea care defineşte vizualizarea poate fi complexă, incluzând
join-uri, grupări şi subcereri, însă nu poate conŃine clauza ORDER BY.
Dacă este necesar, această clauză poate fi specificată la interogarea
vizualizării. Interogarea unei vizualizări este similară celei unui tabel.
72
Numărul coloanelor specificate în definiŃia vizualizării trebuie să fie egal
cu cel din lista asociată comenzii SELECT.
Asupra cererii care defineşte vizualizarea se impun următoarele
restricŃii:
• nu pot fi selectate pseudocoloanele CURRVAL şi NEXTVAL ale
unei secvenŃe;
• dacă sunt selectate pseudocoloanele ROWID, ROWNUM sau
LEVEL, acestora trebuie să li se specifice alias-uri;
• dacă cererea selectează toate coloanele unui tabel, utilizând
simbolul „*“, iar ulterior se adaugă coloane noi tabelului,
vizualizarea nu va conŃine acele coloane până la recrearea sa
printr-o instrucŃiune CREATE OR REPLACE VIEW;
• pentru vizualizările obiect, numărul şi tipul elementelor
selectate de cerere trebuie să coincidă cu cel al atributelor de pe
primul nivel al tipului obiect.
Aportul versiunii Oracle9i în ceea ce priveşte instrucŃiunea
CREATE VIEW constă în posibilitatea:
• creării de subvizualizări ale vizualizărilor obiect;
• definirii de constrângeri asupra vizualizărilor.
Exemplu:
a) Să se creeze o vizualizare care conŃine numele şi prenumele
artistului, numărul operelor sale şi valoarea medie a acestora.
CREATE VIEW artist_nr_val(nume, numar_opere, val_medie)
AS SELECT a.nume || ' ' || a .prenume "Nume si prenume",
COUNT(o. cod_opera) numar, AVG(o.valoare) medie
FROM opera o, artist a
WHERE o.cod_artist = a.cod_artist
GROUP BY o.cod_artist, a.nume, a.prenume;
b) Să se creeze vizualizarea sculptura ce va conŃine codul operei,
data achiziŃiei, codul artistului şi stilul operelor al căror tip este
„sculptura“.
CREATE OR REPLACE VIEW sculptura
(cod_sculptura, informatii, cod_sculptor, stil)
AS SELECT cod_opera,
'Sculptura ' || titlu ||
' a fost achizitionata la data ' ||
data_achizitiei, cod_artist, stil
FROM opera
WHERE tip = 'sculptura';
73
Modificarea unei vizualizări presupune modificarea definiŃiei
74
există astfel de constrângeri, instrucŃiunea DROP VIEW va eşua.
Recompilarea unei vizualizări permite detectarea eventualelor
erori referitoare la vizualizare, înaintea executării vizualizării. După
fiecare modificare a tabelelor de bază este recomandabil ca vizualizarea
să se recompileze:
ALTER VIEW <nume_view> COMPILE;
Reactualizarea tabelelor implică reactualizarea
corespunzătoare a vizualizărilor!!!
Reactualizarea vizualizărilor implică reactualizarea
tabelelor de bază? NU! Există restricŃii care trebuie
respectate!!!
• Nu pot fi modificate date din vizualizare sau adaugate date prin
vizualizare, daca aceasta contine coloane definite prin expresii.
• Nu pot fi înserate, şterse sau actualizate date din vizualizări ce
conŃin: operatorul DISTINCT; clauzele GROUP BY, HAVING,
START WITH, CONNECT BY; pseudo-coloana ROWNUM; funcŃii
grup; operatori de mulŃimi.
• Nu pot fi inserate sau actualizate date care ar încălca constrângerile
din tabelele de bază.
• Nu pot fi inserate sau actualizate valorile coloanelor care rezultă
prin calcul.
• Nu se pot face operaŃii LMD asupra coloanelor calculate cu
DECODE.
Alături de restricŃiile prezentate anterior, aplicabile tuturor
vizualizărilor, există restricŃii specifice, aplicabile vizualizărilor bazate pe
mai multe tabele.
Regula fundamentală este că orice operaŃie INSERT, UPDATE
sau DELETE pe o vizualizare bazată pe mai multe tabele poate modifica
datele doar din unul din tabelele de bază. In care???
Un tabel de bază al unei vizualizări este protejat prin cheie (key
preserved table) dacă orice cheie selectată a tabelului este de asemenea şi
cheie a vizualizării. Deci, un tabel protejat prin cheie este un tabel ale
cărui chei se păstrează şi la nivel de vizualizare. Pentru ca un tabel să fie
protejat prin cheie nu este necesar ca tabelul să aibă toate cheile selectate
în vizualizare. Este suficient ca, atunci când cheia tabelului este selectată,
aceasta să fie şi cheie a vizualizării.
Asupra unui join view pot fi aplicate instrucŃiunile INSERT,
UPDATE sau DELETE, doar dacă sunt îndeplinite următoarele condiŃii:
• instrucŃiunea LMD afectează numai unul dintre tabelele de bază;
75
• în cazul instrucŃiunii UPDATE, toate coloanele care pot fi
reactualizate trebuie să corespundă coloanelor dintr-un tabel
protejat prin cheie (în caz contrar, Oracle nu va putea identifica
unic înregistrarea care trebuie reactualizată);
• în cazul instrucŃiunii DELETE, rândurile unei vizualizări pot fi
şterse numai dacă există un tabel în join protejat prin cheie şi
numai unul (în caz contrar, Oracle nu ar şti din care tabel să
şteargă);
• în cazul instrucŃiunii INSERT, toate coloanele în care sunt inserate
valori trebuie să provină dintr-un tabel protejat prin cheie.
ALL_UPDATABLE_COLUMNS, DBA_UPDATABLE_COLUMNS
şi USER_UPDATABLE_COLUMNS sunt vizualizări din DD ce conŃin
informaŃii referitoare la coloanele vizualizărilor existente, care pot fi
reactualizate.
Exmplu:
1. Să se creeze un view ce conŃine câmpurile nume, prenume, job din
tabelul salariat.
2. Să se insereze, să se actualizeze şi să se şteargă o înregistrare în
acest view. Ce efect vor avea aceste acŃiuni asupra tabelului de
bază?
CREATE VIEW vederea2
AS SELECT nume, prenume, job
FROM salariat;
Nu se pot face inserari deoarece view-ul nu conŃine cheia primară!
INSERT INTO vederea2
VALUES ('Popescu','Valentin','grafician');
va genera eroarea:
ORA-01400: cannot insert NULL into
("SCOTT"."SALARIAT"."COD_SALARIAT")
Actualizarea job-ului salariatului având numele "Popescu":
UPDATE vederea2
SET job = 'programator'
WHERE nume = 'Popescu';
SELECT nume, prenume, job FROM salariat;
Ştergerea înregistrării referitoare la salariatul având numele
"Popescu":
DELETE vederea2
WHERE nume = 'Popescu';
76
OperaŃiile care se realizează asupra view-ului se realizează şi în
tabelul salariat. Pentru un caz mai general, când view-ul conŃine cheia
externă a tabelului de bază, sunt permise modificări ale view-ului, dacă
acestea nu afectează cheia externă.
Exemplu:
Să se creeze un view care conŃine câmpurile nume, prenume, job
din tabelul salariat. Să se introducă în view doar persoanele care sunt
graficieni.
CREATE VIEW vederea21
AS SELECT nume, prenume, job
FROM salariat
WHERE job = 'grafician'
WITH CHECK OPTION;
Să se creeze o vizualizare care să conŃină cod_salariat, nume,
prenume din tabelul salariat şi coloana tip din tabelul grafician. Apoi să
se insereze, să se actualizeze şi să se şteargă o înregistrare din acest view
(vizualizarea conŃine cheia primară cod_salariat din tabelele salariat şi
grafician).
CREATE VIEW vederea4
AS SELECT s.cod_salariat,nume,prenume,tip
FROM salariat s, grafician g
WHERE s.cod_salariat=g.cod_salariat;
În cazul inserării unei înregistrări pentru care se specifică toate
câmpurile:
INSERT INTO vederea4
VALUES
(30,'Popescu','Valentin','artist plastic');
va apare următoarea eroare:
ORA-01776: cannot modify more than one base TABLE
through a join view
Pot fi inserate date doar într-un tabel de bază (în oricare, dar în
unul singur) prin intermediul view-ului, astfel:
INSERT INTO vederea4 (cod_salariat, nume)
VALUES (30, 'Popescu');
Comanda pentru ştergerea unei înregistrări:
DELETE vederea4
WHERE cod_salariat = 3;
va genera următoarea eroare:
77
ORA-01752: cannot delete from view without
exactly one key-preserved TABLE.
Modificarea unei înregistrări se face prin secvenŃa care urmează.
Toate actualizările care se fac în view se fac şi în tabelele de bază.
UPDATE vederea4
SET tip = 'designer'
WHERE cod_salariat = 3;
Exemplu:
Care dintre coloanele unei vizualizări sunt actualizabile?
SELECT column_name, updatable
FROM user_updatable_columns
WHERE table_name = 'vederea4';
Exemplu:
1. Să se creeze un view (vederea3) care să conŃină, pentru fiecare
categorie de salariat, salariile medii şi numărul de angajaŃi din
tabelul salariat.
2. Să se insereze, să se actualizeze şi să se şteargă o înregistrare în
view.
CREATE VIEW vederea3 (nr, job, salmed)
AS SELECT COUNT(*), job, AVG(salariu)
FROM salariat
GROUP BY job;
Nu se pot face inserări, actualizări sau ştergeri într-un view ce
conŃine funcŃii grup. După oricare din aceste operaŃii apare acelaşi mesaj:
ORA-01732: data manipulation operation not legal
on this view
Exemplu:
Să se creeze o vizualizare care să conŃină coloanele
cod_contractant, adresa, telefon din tabelul contractant şi coloanele
nr_contract, tip_contract, data_incheiere din tabelul contract. Să se
insereze o înregistrare în vizualizare.
CREATE VIEW vederea44
AS SELECT c.cod_contractant, adresa, telefon,
co.nr_contract, tip_contract,
data_incheiere
FROM contractant c, contract co
WHERE
c.cod_contractant=co.cod_contractant;
La inserarea unei înregistrări căreia i se specifică valorile tuturor
câmpurilor din ambele tabele:
78
INSERT INTO vederea44(cod_contractant, adresa,
nr_contract,
data_incheiere)
VALUES (200, 'Str. Marmurei, 14', '6235',
TO_DATE('January 03,2002','Month
dd,yyyy'));
se obŃine eroarea:
ORA-01779: cannot modify a column which maps to a
non key-preserved TABLE
Cele două tabele de bază, contractant şi contract, se află într-o
relaŃie “one-to-many”, iar view-ul creat conŃine cheile primare din ambele
tabele.
Doar tabelul contract este protejat prin cheie şi, prin urmare, doar
el poate fi modificat prin intermediul view-ului. Aceasta, deoarece ar
putea exista mai multe înregistrări în view, cu aceeaşi valoare
corespunzătoare câmpului cod_contractant (CP în contractant).
Exact aceeaşi eroare se obŃine dacă încercăm inserarea unei
înregistrări în vederea44, specificând fie şi numai un câmp provenind din
tabela contractant (indiferent dacă el conŃine sau nu CP).
Singura operaŃie de inserare permisă este aceea efectuată prin
specificarea cheilor provenind doar din tabelul contract. Astfel, prin
executarea comenzii:
INSERT INTO vederea44(nr_contract, tip_contract)
VALUES ('6234', 0);
este creată o înregistrare, dar este modificat şi tabelul contract. Dacă la
inserŃie nu se specifică cheia primară din contract:
INSERT INTO vederea44(tip_contract)
VALUES (1);
ORA-01400: mandatory (NOT NULL) column is missing
or NULL during insert
Cererea din definiŃia vizualizării poate fi restricŃionată prin clauzele
WITH READ ONLY şi WITH CHECK OPTION. OpŃiunea WITH READ
ONLY asigură că nu pot fi efectuate operaŃii LMD asupra vizualizării.
Constrângerea WITH CHECK OPTION garantează faptul că va fi
permisă, prin intermediul vizualizării, numai inserarea sau actualizarea de
linii accesibile acesteia (care sunt selectate de cerere). Prin urmare,
această opŃiune asigură constrângeri de integritate şi verificări asupra
validităŃii datelor inserate sau actualizate.
OpŃiunea WITH CHECK OPTION nu poate funcŃiona dacă:
• există o cerere imbricată în cadrul subcererii vizualizării sau în
vreuna dintre vizualizările de bază;
79
• operaŃiile de inserare, ştergere şi modificare se fac prin
intermediul declanşatorilor INSTEAD OF.
Cuvântul cheie CONSTRAINT permite numirea constrângerii
WITH CHECK OPTION. În absenŃa acestei clauze, constrângerea va avea
un nume implicit de forma SYS_Cn, unde n este un număr întreg unic.
Exemplu:
Să se creeze o vizualizare ce conŃine artiştii de naŃionalitate
română, care au opere expuse în muzeu. DefiniŃia vizualizării nu va
permite modificarea naŃionalităŃii unui artist sau inserarea unui artist
având altă naŃionalitate decât cea română.
CREATE VIEW artist_roman
AS SELECT * FROM artist
WHERE nationalitate = 'romana'
WITH CHECK OPTION CONSTRAINT artist_roman_ck;
UPDATE artist_roman
SET nationalitate = 'engleza'
WHERE cod_artist = 25;
Încercarea de actualizare a unei linii prin instrucŃiunea anterioară
va genera eroarea „ORA-01402: view WITH CHECK OPTION where-
clause violation“.
Exemplu:
Să se creeze o vizualizare asupra tabelului galerie care să nu
permită efectuarea nici unei operaŃii LMD.
CREATE VIEW viz_galerie
AS SELECT cod_galerie, nume_galerie
FROM galerie
WITH READ ONLY;
DELETE FROM viz_galerie
WHERE cod_galerie = 10;
Încercarea de ştergere a unei linii din vizualizarea viz_galerie
determină apariŃia erorii „ORA-01752: cannot delete from view without
exactly one key-preserved table“. Dacă se încearcă modificarea sau
inserarea unei linii prin intermediul unei vizualizări asupra căreia a fost
definită o constrângere WITH READ ONLY, server-ul Oracle generează
eroarea „ORA-01733: virtual column not allowed here“.
Exemplu:
Să se creeze o vizualizare care conŃine codul şi titlul operelor de
artă, codul şi numele artiştilor care le-au creat, precum şi codul galeriilor
unde sunt expuse. Să se afle dacă este posibilă adăugarea unei noi
înregistrări prin intermediul acestei vizualizări.
80
CREATE VIEW opera_artist
AS SELECT o.cod_opera, o.titlu, o.cod_galerie,
a.cod_artist, a.nume
FROM opera o, artist a
WHERE o.cod_artist = a.cod_artist;
InstrucŃiunea următoare afişează numele coloanelor şi valorile
YES/NO, după cum aceste coloane sunt, sau nu, modificabile.
SELECT COLUMN_NAME, UPDATABLE
FROM USER_UPDATABLE_COLUMNS
WHERE TABLE_NAME = 'OPERA_ARTIST';
Se va obŃine că doar primele trei coloane ale vizualizării sunt
modificabile.
Indexul primar al coloanei cod_artist din tabelul artist nu este unic
în vizualizarea opera_artist. Prin urmare, tabelul artist nu este key-
preserved, iar coloanele sale nu sunt modificabile.
InstrucŃiunea următoare va genera eroarea „ORA-01776: cannot
modify more than one base table through a join view“.
INSERT INTO opera_artist
VALUES (200, 'Poeme de l''ame', 20, 147,
'Janmot');
În schimb, instrucŃiunea următoare va fi executată cu succes,
întrucât adaugă o înregistrare în tabelul de bază opera, ale cărui coloane
sunt modificabile.
INSERT INTO opera_artist (cod_opera, titlu,
cod_galerie)
VALUES (200, 'Poeme de l''ame', 20);
Constrângeri asupra vizualizărilor
Începând cu versiunea Oracle9i pot fi specificate constrângeri
pentru vizualizări. Se pot defini constrângeri la nivel de vizualizare,
respectiv la nivel de coloană sau atribut. Constrângerile asupra
vizualizărilor constituie o submulŃime a constrângerilor specifice
tabelelor.
Pot fi specificate explicit numai constrângerile UNIQUE,
PRIMARY KEY şi FOREIGN KEY. Constrângerea de tip CHECK poate fi
realizată prin precizarea clauzei WITH CHECK OPTION în comanda care
defineşte vizualizarea.
Constrângerile asupra vizualizărilor pot fi definite numai în modul
DISABLE NOVALIDATE. Aceste cuvinte cheie trebuie specificate la
declararea constrângerii, nefiind permisă precizarea altor stări.
Exemplu:
81
Să se creeze o vizualizare care conŃine codurile, numele şi adresele
galeriilor. Se va impune unicitatea valorilor coloanei adresa şi
constrângerea de cheie primară pentru coloana corespunzătoare codului
galeriei.
CREATE VIEW viz_galerie(
cod_gal, nume, adresa UNIQUE DISABLE NOVALIDATE,
CONSTRAINT cp_viz PRIMARY KEY (cod_gal) DISABLE
NOVALIDATE)
AS SELECT cod_galerie, nume_galerie, adresa
FROM galerie;
82
Vizualizările din dicŃionarul datelor referitoare la tabele conŃin:
• USER_TAB_COLUMNS|COLS – informaŃii despre coloanele
tabelelor,
• USER_CONS_COLUMNS – informaŃii despre constrângeri la nivel
coloană,
• USER_TAB_COMMENTS – informaŃii despre comentarii la nivel
tabel,
• USER_COL_COMMENTS – informaŃii despre comentarii la nivel
coloană,
• USER_TAB_PARTITIONS – informaŃii despre partiŃiile tabelelor.
83
Limbajul de interogare a datelor(DQL)
Limbajul SQL de interogare a datelor (DQL – Data Query
Language) include o singură comandă SELECT, care este cea mai
folosită pentru a obŃine date din baza de date, astfel încât acestea să fie
prelucrate de o anumită aplicaŃie sau să fie afişate. Rezultatul unei
instrucŃiuni SELECT, numit şi set de rezultate, este returnat sub forma
unui tabel. Deoarece SQL este un limbaj neprocedural, se specifică
rezultatele pe care le doriŃi să le obŃineŃi, nu şi modul lor de obŃinere.
1
Eliminarea duplicatelor se poate realiza folosind clauza
DISTINCT. Dacă nu se specifică parametrul DISTINCT, parametrul ALL
este implicit şi are ca efect afişarea dublurilor.
Simbolul “*” permite selectarea tuturor atributelor din
tabelele asupra cărora se execută cererea. Atributele sau expresiile din
lista clauzei SELECT pot conŃine alias-uri, care vor reprezenta numele
câmpurilor respective în cadrul tabelului furnizat ca rezultat de
instrucŃiunea SELECT.
Clauza WHERE poate fi folosită pentru a impune anumite
condiŃii liniilor din care se vor extrage atributele specificate în clauza
SELECT.
Clauza GROUP BY grupează înregistrările după anumite
câmpuri; în cazul prezenŃei acestei clauze, clauza HAVING poate impune
restricŃii suplimentare asupra rezultatului final.
Ordonarea înregistrărilor se poate face cu ajutorul clauzei
ORDER BY. Cu ajutorul parametrilor ASC şi DESC se poate specifica
ordonarea crescătoare, respectiv descrescătoare a înregistrărilor. Pentru o
secvenŃă crescătoare valorile null sunt afişate ultimele. Dacă nu se face
nici o specificaŃie, atunci ordinea de returnare este la latitudinea server-
ului.
2
Dacă în interiorul alias-ului apare un spaŃiu liber sau caractere
speciale, atunci alias-ul trebuie scris între ghilimele.
SELECT dateres–dataim ”numar zile”
FROM imprumuta;
Sortarea rezultatelor
3
FROM FILM
ORDER BY MPAA_RATING_COD, COD_GEN_FILM ;
ObservaŃie:
Oracle va afişa titlu de coloana la dimensiunea maximă a valorilor
din coloana(de ex. dacă în coloana RATING val cea mai mare este de 5
caractere, interogarea va afişa RATIN). În noua versiune de SQL produs
de Oracle, iSQL*Plus, nu mai prescurtează.
Exemplu :
SELECT titlu, autor, pret, coded as domeniu
from carte
order by coded, autor;
4
Utilizarea clauzei WHERE pentru filtrarea rezultatelor
- Operatori de comparare
- Operatori conjuctivi
- Operatori logici
- Operatori aritmetici
Operatori de comparare
5
Operator Descriere
= Egal cu
< Mai mic decât
<= Mai mic sau egal
> Mai mare decât
>= Mai mare sau egal
!= Diferit de
<> Diferit de (standard ANSI)
6
• Să se obŃină titlurile şi numărul de exemplare ale cărŃilor care au
nrex>100 ;
SELECT titlu, nrex
FROM carte
WHERE nrex>100;
Operatori conjunctivi
7
• Să se afişeze toate filmele pentru care categoria RATING este PG-
13 sau preŃul de vânzare cu amănuntul pentru formatul DVD este
19.99 sau mai mic, în ordinea crescătoare a preŃurilor.
Operatori logici
8
IS NULL
Operatorul IS NULL este folosit pentru a determina dacă o valoare
este nulă.
Exemple:
• Să se găsească toate conturile de clienŃi active, adică toate conturile
pentru care coloana DATA–TERMINATA conŃine o valoare nulă:
SELECT ID_CONT_CLIENT
FROM CONT_CLIENT
WHERE DATA_INCHEIERE IS NULL;
BETWEEN
Operatorul BETWEEN este folosit pentru a determina dacă o
valoare se încadrează într-un interval special. Intervalul este specificat
folosind o valoare minimă şi o valoare maximă, fiind un interval inclusiv,
ceea ce înseamnă că include şi valori specificate.
Exemple:
• Să se afişeze toate filmele cu preŃul de vânzare cu amănuntul pentru
formatul DVD între 14.99 şi 19.99, ordonate crescător după preŃ.
SELECT TITLU_FILM, PRET_VANZARE_DVD
FORM FILM
WHERE PRER_VANZARE_DVD BETWEEN 14.99 AND 19.99
ORDER BY PRER_VANZARE_DVD;
9
Tabelul DUAL se află în schema SYS şi poate fi accesat de către
toŃi utilizatorii. Tabelul este util atunci când se afişează valoarea unei
constante, pseudocoloane sau expresii care nu este construită pe baza
datelor vreunui tabel. În general, tabelul DUAL este utilizat pentru a
completa sintaxa instrucŃiunii SELECT, întrucât clauzele SELECT şi
FROM sunt obligatorii.
Să se afişeze data şi ora curentă.
SELECT TO_CHAR(SYSDATE,’DD/MM/YY HH24:MI:SS’)
FROM DUAL;
LIKE
Operatorul LIKE este folosit pentru a compara o valoare de tip
caracter cu un tipar*(pattern), returnând valoarea logică “adevărat” dacă
valoarea de tip caracter se încadrează în tipar şi “fals" în caz contrar.
Pentru definirea tiparului pot fi folosite două caractere de înlocuire:
• LiniuŃa de subliniere (_) - Caracterul liniuŃă de subliniere poate fi
folosit drept caracter de înlocuire poziŃional, ceea ce înseamnă că se
potriveşte cu orice caracter aflat pe poziŃia respectivă în şirul de
caractere evaluat.
• Procent (%) - Simbolul procent (%) poate fi folosit drept caracter
de înlocuire nepoziŃional, ceea ce înseamnă că se potriveşte cu
orice număr de caractere, indiferent de lungime, reprezentând orice
secvenŃă de zero sau mai multe caractere, şi „_“, reprezentând un
singur caracter.
Exemple de tipare:
Tipar Interpretare
Now% Se potriveşte cu orice şir de caractere care incepe cu
„Now".
N_w Se potriveşte cu orice şir de caractere format din
exact trei caractere, care începe cu „N" şi se termină
cu „w".
10
%N-w% Se potriveşte cu orice şir de caractere care conŃine
litera „N", urmată de orice alt caracter, urmat de litera
„w" (1a începutul, la sfârşitul sau undeva în mijlocul
şirului de caractere)
%Now% Se potriveşte cu orice şir de caractere care conŃine
„Now" (1a inceput, la sfârşit sau în mijloc).
%Now Se potriveşte cu orice şir de caractere care se termină
cu „Now".
Exemple
Să se afişeze cartile ale căror titlu conŃine cuvântul „Baza“, sau
pentru care al treilea caracter din autor este „p“.
SELECT titlu, autor
FROM carte
WHERE titlu LIKE '%Baza%' OR autor LIKE '__p%';
11
IN
Operatorul IN este folosit pentru a determina dacă o valoare face
parte dintr-o listă de valori. Lista poate fi specificată ca valori literale,
folosind o listă de valori separate prin virgule şi încadrate între paranteze,
sau poate fi selectată din baza de date folosind o subselecŃie (o
subinterogare), care este o interogare în cadrul unei alte interogări.
12
EXISTS
13
Operatori aritmetici
Operator Descriere
+ Adunare
- Scădere
* ÎnmulŃire
/ ÎmpărŃire
• Cât v-ar costa aceeaşi achiziŃie dacă aŃi avea un bon valoric de 8 ron?
SELECT (PRET_VANZARE _VHS + PRET_VANZARE _DVD) - 8
AS COST
FROM FILM
WHERE TITLU_FILM = 'The Last Samurai';
• Dacă taxele sunt de 8.25% (0.0825), cât reprezintă taxele de vânzare
din costul achiziŃiei anterioare?
SELECT (PRET_VANZARE _VHS+ PRET_VANZARE _DVD) *
0.0825 AS TAX
FROM FILM
WHERE TITLU_FILM = 'The Last Samurai';
• Care este costul mediu pentru o copie a filmului The Last Samurai?
SELECT (PRET_VANZARE_VHS+PRET_VANZARE _DVD) / 2
AS AVG_COST
FROM FILM
WHERE TITLU_FILM = 'The Last Samurai'
14
FuncŃii SQL elementare
15
Aceeaşi soluŃie, modificată pentru a funcŃiona în Microsoft SQL
Server :
SELECT ‚Client' + NUME_PERSOANA +
' ' + NUME_FAMILIE_PERSOANA AS SALUT_CLIENT
FROM PERSOANA;
<nume angajat> castiga <salariu> lunar, dar doreste <salariu de 3
ori mai mare>
SELECT last_name||'castiga'||salary||'lunar, dar doreste'||salary*3 "salariul
ideal"
FROM employees;
UPPER
FuncŃia UPPER este deseori folosită în condiŃiile WHERE.
FuncŃia UPPER transformă literele dintr-un şir de caractere în litere mari.
Numerele şi caracterele speciale sunt lăsate ca stare.
Exemple:
• Să se afişeze comediile (COD_GEN_FILM = 'Comdy') scriind
titlurile cu majuscule.
SELECT UPPER(TITLU_FILM) AS TITLU_FILM
FROM FILM
WHERE COD_GEN_FILM = 'Comdy';
LOWER
FuncŃia LOWER este inversa funcŃiei UPPER — transformă
literele dintr-un * de caractere în litere mici.
Exemple de utilizare a funcŃiei LOWER:
• Să se afişeze comediile (GEN_COD_FILM = 'Comedy') scriind
titlurile cu minuscule.
SELECT LOWER(TITLU_FILM) AS TITLU_FILM
FROM FILM
WHERE GEN_COD_FILM = 'Comedy';
16
• Se poate folosi funcŃia LOWER într-o clauză WHERE, atunci când
nu ştiti sigur ce tip de litere obŃine textul pe care vreŃi să-1
comparaŃi. AfişaŃi toate filmele care au în titlu cuvântul „of ",
indiferent dacă este scris cu litere mari sau mici.
SELECT TITLU_FILM
FROM FILM
WHERE LOWER(TITLU_FILM) LIKE ' % of %'
OR LOWER(TITLU_FILM) LIKE 'of % '
OR LOWER(TITLU_FILM) LIKE ' % of ';
SUBSTR
17
LENGTH
FunŃii matematice
FuncŃiile matematice manipulează valori numerice, în conformitate
cu regulile matematicii.
ROUND
FuncŃia ROUND rotunjeşte o valoare la un număr specificat
de zecimale. Valoarea numerică este furnizată prin primul parametru, iar
numărul de zecimale prin cel de-al doilea. În continuare este prezentat
formatul general al funcŃiei ROUND.
ROUND (expresie numerică, număr de poziŃii zecimale)
• Care este costul mediu al unei copii a filmului The Last Samurai,
rotunjit la două zecimale?
SELECT ROUND((PRET_VANZARE_VHS +
PRET_VANZARE _DVD) / 2, 2) AS AVG_COST
FROM FILM
WHERE TITLU_FILM = 'The Last Samurai';
18
FuncŃie Descriere
ABS Valoarea absolută a unui număr dat
COS Cosinusul trigonometric al unui unghi specificat în radiani
EXP Valoarea exponenŃială a unui număr dat
POWER Ridică un număr la o putere (numărul şi puterea sunt fumizate
ca parametri)
SIN Sinusul trigonometric al unui unghi specificat în radiani
TAN Tangenta trigonometrică a unui unghi specificat în radiani
FuncŃii de conversie
FuncŃiile de conversie transformă date dintr-un tip de date în altul.
CAST
FuncŃia CAST transformă date dintr-un tip de date în altul. Iată
sintaxa generală a funcŃiei CAST, urmată de un exemplu:
CAST (expresie AS tip de date)
• Afişati preŃul pentru formatul DVD al filmului The Last Samurai,
cu un simbol dolar în faŃa sumei. Valoarea numerică trebuie să fie
convertită într-un şir de caractere pentru a putea fi concatenată cu o
valoare literală conŃinând simbolul dolar.
SELECT '$' || CAST(PRET_VANZARE _DVD AS
VARCHAR(6)) AS PRET
FROM FILM
WHERE TITLU_FILM = 'The Last Samurai';
FuncŃie Descriere
AVG Calculează valoarea medie pentru o coloană sau o
expresie.
COUNT Numără valorile dintr-o coloană.
MAX Găseşte valoarea maxină dintr-o coloană.
MIN Găseşte valoarea minimă dintr-o coloană.
SUM Însumează valorile dintr-o coloană.
Exemple:
• Care este preŃul mediu al unui DVD?
19
SELECT ROUND(AVG(PRET_VANZARE _DVD),2) AS
AVG_PRET
FROM FILM;
• Câte filme există în tabelul FILM?
SELECT COUNT(*) AS NUM_FILM
FROM FILM;
• Câte genuri diferite de filme sunt reprezentate în tabelul FILM?
SELECT COUNT(DISTINCT COD_GEN_FILM) AS NUM_GEN
FROM FILM;
• Care sunt lungimea minimă şi maximă a titlurilor filmelor?
SELECT MIN(LENGTH(TITLU_FILM)) AS MIN_LENGTH,
MAX(LENGTH(TITLU_FILM)) AS MAX_LENGTH
FROM FILM;
Clauza GROUP BY
20
Pentru a returna informatie corespunxatoare fiecarui grup, pot fi
utilizate functiile agregat. Acestea pot aparea in clauzele SELECT,
ORDER BY si HAVING. Se poate utiliza functie grup in clauza WHERE?
Este corect …WHERE AVG(sal) > 200? NU!
Cand se utilizeaza GROUP BY, server-ul sorteaza implicit
multimea rezultata in ordinea crescatoare a valorilor coloanelor dupa care
se realizeaza gruparea.
Grupurile sunt formate si functiile grup sunt calculate, inainte ca
clauza HAVING sa fie aplicata grupurilor.
Exemplu:
Să se obŃină numărul de câte ori a fost împrumutată fiecare carte.
SELECT codel, COUNT(*)
FROM imprumuta
GROUP BY codel;
Exemplu:
Pentru fiecare domeniu de carte să se obŃină numărul cărŃilor din
domeniu, media preŃurilor şi numărul total de exemplare.
SELECT coded,COUNT(*), AVG(pret),SUM(nrex)
FROM carte
GROUP BY coded;
Dacă în comanda SELECT apar atribute coloană (nu funcŃii grup) şi
se utilizează clauza GROUP BY atunci aceste coloane trebuie obligatoriu
să apară în clauza GROUP BY.
Exemplu:
Să se obŃină pentru fiecare autor, media preŃurilor cărŃilor din
bibliotecă.
SELECT autor, AVG(pret)
FROM carte
GROUP BY autor;
Exemplu:
Pentru departamentele în care salariul maxim depăşeşte 5000$ să
se obŃină codul acestor departamente şi salariul maxim pe departament.
SELECT deptno, MAX(sal)
FROM emp
GROUP BY deptno
HAVING MAX(sal)>5000;
21
Exemplu:
SELECT MAX(AVG(pret))
FROM carte
GROUP BY autor;
Exemplu:
Să se afişeze numele şi salariul celor mai prost plătiŃi angajaŃi
din fiecare departament.
SELECT ename, sal
FROM emp
WHERE (deptno, sal) IN
(SELECT deptno, MIN(sal)
FROM emp
GROUP BY deptno);
Exemplu:
Să se obŃină pentru fiecare carte, codul său şi numărul de
exemplare care nu au fost încă restituite.
SELECT codel, COUNT(*)
FROM imprumuta
WHERE dataef IS NULL
GROUP BY codel;
Exemplu:
Să se obŃină numărul cărŃilor împrumutate cel puŃin o dată.
SELECT COUNT(DISTINCT codel)
FROM imprumuta;
Exemplu:
Să se afişeze numărul cărŃilor împrumutate cel puŃin de două ori
(pentru fiecare carte împrumutată mai mult decât o dată să se obŃină
numărul de câte ori a fost împrumutată).
SELECT COUNT(COUNT(codel))
FROM imprumuta
GROUP BY codel
HAVING COUNT(*)>1;
22
În cererea anterioară COUNT(codel), reprezintă numărul care arată
de câte ori a fost împrumutată fiecare carte, iar COUNT(COUNT(codel)),
reprezintă numărul total al cărŃilor împrumutate.
Exemplu:
Pentru fiecare departament codul dep si numarul de angajati.
1 select department_id, count(*)
2 from employees
3* group by department_id
SQL> /
DEPARTMENT_ID COUNT(*)
------------- ----------
10 1
20 2
30 6
40 1
50 45
60 5
70 1
80 34
90 3
100 6
110 2
1
12 rows selected.
Pentru departamentele cu mai mult de un angajat se afiseaza codul dep si
numarul de angajati.
1 select department_id, coun
2 from employees
3 group by department_id
4* having count(*)>1
SQL> /
DEPARTMENT_ID COUNT(*)
------------- ----------
20 2
30 6
50 45
60 5
80 34
90 3
23
100 6
110 2
8 rows selected.
Se afiseaza numarul departamentelor cu mai mult de un angajat.
1 select count(count(*))
2 from employees
3 group by department_id
4* having count(*)>1
SQL> /
COUNT(COUNT(*))
---------------
8
Exemplu:
Sa se afiseze numărul de cărŃi imprumutate din fiecare domeniu.
SELECT d.intdom, COUNT(*)
FROM domeniu d, carte c, imprumuta I
WHERE c.codel = i. codel
AND c.coded = d.coded
GROUP BY intdom;
Exemplu:
Lista codurilor cititorilor care au mai mult de 3 cărŃi nerestituite la
termen.
SELECT codec
FROM imprumuta
WHERE dataef IS NULL AND datares < SYSDATE
GROUP BY codec
HAVING COUNT(*) > 2;
Exemplu:
Pentru fiecare domeniu de carte care conŃine cel puŃin o carte şi
unde preŃul oricărei cărŃi nu depăşeşte o valoare dată, să se obŃină: codul
domeniului, numărul cărŃilor din domeniu şi numărul mediu de
exemplare.
SELECT coded, COUNT(*), AVG(nrex)
FROM carte
GROUP BY coded
24
HAVING COUNT(*) >= 1
AND MAX(pret) < &pret_dat;
Exemplu:
Codurile domeniilor care nu contin carti.
SELECT coded
FROM carte
GROUP BY coded
HAVING COUNT(*) = 0;
Nu este corect, deoarece se iau in considerare NUMAI codurile
domeniilor care apar in tabelul CARTE.
SELECT intdom
FROM domeniu d
WHERE 0 = (SELECT COUNT(*)
FROM carte
WHERE coded = d.coded);
Urmatoarea cerere este corecta?
SELECT intdom
FROM domeniu d,(SELECT coded, COUNT(*) a
FROM carte
GROUP BY coded) b
WHERE b.coded = d.coded)
AND b.a = o;
Exemplu:
În ce interogări este necesară utilizarea cuvântului cheie
HAVING?
A. când este necesar să eliminăm linii duble din rezultat;
B. când este necesar să ordonăm mulŃimea rezultat;
C. când este necesar să efectuăm un calcul pe grup;
D. când este necesar să restricŃionăm grupurile de linii returnate.
UNION
Operatorul UNION adaugă rândurile din setul de înregistrări al
unei interogări la cel al unei alte inregistrări şi, în acelaşi timp, elimină
rândurile duplicate, într-un mod similar cu cel al cuvântului cheie
25
DISTINCT. OperaŃia este permisă numai dacă interogările sunt
compatibile din punctul de vedere al uniunii, ceea ce înseamnă că au
acelaşi număr de coloane şi că tipurile de date ale coloanelor
corespondente sunt compatibile.
Iată un exemplu:
• AfişaŃi pe o singură coloană toate valorile nenule pentru taxa de
inchiriere şi taxa de întârziere din tabelul FILM_ÎNCHIRIAT.
SELECT INCHIRIAT_FEE AS FEE
FROM FILM_INCHIRIAT
WHERE INCHIRIAT _FEE IS NOT NULL
UNION
SELECT LATE_OR_LOSS_FEE AS FEE
FROM FILM_INCHIRIAT
WHERE LATE _OR_ LOSS FEE IS NOT NULL;
UNION ALL
UNION ALL funcŃionează la fel ca şi operatorul UNION,
exceptând faptul că rândurile duplicate nu sunt eliminate.
INTERSECT
Operatorul INTERSECT găseşte valorile selectate dintr-o
interogare, care apar şi într-o altă interogare. În esenŃă, găseşte intersecŃia
valorilor din cele două interogări. Totuşi, doar un număr mic de sisteme
DBMS (cele mai importance fiind Oracle şi DB2) implementează acest
operator. Nu-1 veŃi găsi în Microsoft SQL Server sau MySQL.
Iată un exemplu:
• Există în tabelul FILM filme pentru care preŃul pentru DVD este
egal cu preŃul pentru VHS?
SELECT INCHIRIAT_FEE AS FEE
FROM FILM_ INCHIRIAT
WHERE INCHIRIAT _FEE IS NOT NULL
INTERSECT
SELECT LATE_OR_LOSS_FEE AS FEE
FROM FILM_ INCHIRIAT
WHERE LATE OR_ LOSS FEE IS NOT NULL
EXCEPT
EXCEPT este operatorul standard ANSI/ISO care găseşte
diferenŃele dintre două seturi de rezultate, returnând, în esenŃă, valorile
26
din prima interogare care nu apar în cea de-a doua interogare. Foarte
puŃine sisteme DBMS implementează acest operator. În unele
implementări, precum Oracle, operatorul se numeşte MINUS, nu
EXCEPT.
27
Cereri multi – relaţie
Comanda SELECT oferă posibilitatea de a consulta informaţii care provin
din mai multe tabele. Operatorii care intervin în astfel de cereri pot fi:
operatori pe mulţimi (UNION, UNION ALL, INTERSECT, MINUS);
operatori compunere care implementează diferite tipuri de JOIN.
Există două moduri de realizare a cererilor multi-relaţie:
forma procedurală, în care trebuie indicat drumul de acces la informaţie
prin imbricarea de comenzi SELECT;
forma relaţională, în care drumul de acces la informaţie este în sarcina
sistemului.
Exemplu:
Să se obţină, utilizând aceste două forme, codurile şi titlurile cărţilor
împrumutate.
a) Forma procedurală (imbricare de comenzi SELECT):
SELECT codel, titlu
FROM carte
WHERE codel IN (SELECT DISTINCT codel
FROM imprumuta);
b) Forma relaţională:
SELECT carte.codel, titlu
FROM carte, imprumuta
WHERE carte.codel = imprumuta.codel;
Observaţii:
Comenzile SELECT, care intervin în cereri ce conţin operatori pe mulţimi,
trebuie să satisfacă anumite condiţii:
toate comenzile SELECT trebuie să aibă acelaşi număr de coloane;
opţiunea DISTINCT este implicită (excepţie UNION ALL);
numele coloanelor sunt cele din prima comandă SELECT;
dimensiunea coloanei implicit este cea mai mare dintre cele două coloane;
sunt admise combinaţii de forma:
1. SELECT1 UNION SELECT2 INTERSECT SELECT3 şi ordinea de
execuţie este de la stânga la dreapta;
2. SELECT1 UNION (SELECT2 INTERSECT SELECT3) şi ordinea
este dată de paranteze.
UNION
Exemplu:
Să se afişeze codurile operelor de artă pentru care a fost încheiată o poliţă
de asigurare sau care beneficiază de un sistem de securitate. Fiecare cod va fi
afişat o singură dată.
SELECT cod_opera
FROM polita_asig
UNION
SELECT cod_opera
FROM securitate;
Exemplu:
Să se listeze codul operelor de artă, codul şi numele artiştilor.
SELECT cod_opera, cod_artist, TO_CHAR(null) nume
FROM opera
UNION
SELECT TO_NUMBER(null), cod_artist, nume
FROM artist;
UNION ALL
Operatorul UNION ALL funcţionează la fel ca şi operatorul UNION,
exceptând faptul că rândurile duplicate nu sunt eliminate.
Precizările făcute asupra operatorului UNION sunt valabile şi în cazul
operatorului UNION ALL. În cererile asupra cărora se aplică UNION ALL nu poate
fi utilizat cuvântul cheie DISTINCT.
Exemplu:
Să se determine codul operelor de artă pentru care s-a încheiat o poliţă de
asigurare sau pentru care s-a achiziţionat un sistem de securitate. Să se afişeze
firma şi data contractării, respectiv a instalării sistemului. Întrucât o operă poate
avea mai multe poliţe de asigurare sau mai multe sisteme de securitate
încheiate, respectiv instalate la aceeaşi firmă şi dată, nu se vor suprima liniile
duplicat. Rezultatul va fi ordonat după codul operelor.
SELECT cod_opera, firma, semnat_contract
FROM polita_asig
UNION ALL
SELECT cod_opera, firma, data_inst
FROM polita_asig
ORDER BY cod_opera;
Clauza ORDER BY poate apărea numai o singură dată într-o cerere compusă.
Dacă se utilizează, clauza trebuie plasată la sfârşitul cererii şi acceptă nume de
coloane, alias-uri sau notaţia poziţională. Numele de coloane şi alias-urile din
clauza ORDER BY trebuie să fie din lista SELECT a primei instrucţiuni.
INTERSECT
Operatorul INTERSECT găseşte valorile selectate dintr-o interogare, care
apar şi într-o altă interogare. În esenţă, găseşte intersecţia valorilor din cele două
interogări. Acest operator nu ignoră valorile null.
Totuşi, doar un număr mic de sisteme DBMS (cele mai importance fiind
Oracle şi DB2) implementează acest operator. Nu-1 veţi găsi în Microsoft SQL
Server sau MySQL.
Exemplu:
Există în tabelul FILM filme pentru care preţul pentru DVD este egal cu
preţul pentru VHS?
SELECT taxa_inchiriat AS TAXA
FROM FILM_INCHIRIAT
WHERE taxa_inchiriat IS NOT NULL
INTERSECT
SELECT taxa_intarziere AS TAXA
FROM FILM_INCHIRIAT
WHERE taxa_intarziere IS NOT NULL;
Exemplu:
Exemplu:
MINUS
Exemplu:
Să se determine codul şi valoarea operelor de artă al căror preţ nu este de
20 de ori mai mare decât valoarea poliţei de asigurare.
SELECT cod_opera, valoare
FROM opera
MINUS
SELECT cod_opera, valoare*20
FROM polita_asig;
Exemplu:
Să se afişeze codurile cititorilor care nu au împrumutat cărţi.
SELECT codec
FROM cititor
MINUS
SELECT DISTINCT codec
FROM imprumuta;
Exemplu:
S-au prezentat până acum instrucţiuni SQL care selectează date dintr-un
singur tabel. Deseori, este util să se combine date din tabele multiple într-o singură
interogare. De exemplu, în listingul celor trei coloane ale tabelului FILM din
figura următoare, observaţi valorile afişate pentru coloană FILM_GEN_COD.
Atunci când s-a proiectat baza de date pentru magazinul de produse video, s-au
folosit coduri în locul descrierilor complete pentru genurile filmelor în tabelul
FILM. Din discuţia despre procesul de normalizare, s-a evitat anomalia de
actualizare – dacă se schimbă descrierea unui gen, nu este nevoie să actualizăm
acea descriere pentru toate filmele asociate genului respectiv în tabelul FILM. În
timpul normalizării, descrierea genurilor a fost mutată în tabelul, FILM_GEN, iar
coloană FILM_GEN_COD a devenit cheie externă în tabelul FILM, referind
coloană cheie primară (cu acelaşi nume) din tabelul FILM_GEN. S-a optat pentru
folosirea unui cod mnemonic pentru codurile genurilor.
Tabelul FILM_GEN
FILM_GEN_COD FILM_GEN_DESCRIERE
ActAd Actiune
Anime Animatie
ChFam Copii şi Familie
Class Clasic
Comedie Comedie
Doc Documentar
Drama Drama
Forgn Strain
Hor Horror
Indep Independent
Music Muzical
Rmce Romance(Romantic, Idila)
SciFi Stiintifico-Fantastic
Sport Sport
Thriller Groaza
Uniuni (join)
O uniune (join) este o operaţie într-o bază de date relaţionale care combină
coloane din două sau mai multe tabele în rezultatele unei singure interogări. O
uniune apare de fiecare dată când clauza FROM a unei instrucţiuni SELECT
specifică numele mai multor tabele.
De exemplu:
SELECT FILM_ID, FILM_GEN_DESCRIERE AS GEN, FILM_TITLU
FROM FILM, FILM_GEN
ORDER BY FILM_ID;
FILM_ID GEN FILM_TITLU
1 Actiune şi Aventura Mystic River
1 Animatie Mystic River
1 Clasic Mystic River
1 Documentar Mystic River
1 Strain Mystic River
1 Independent Mystic River
1 Groaza Mystic River
1 Sport Mystic River
1 SF Mystic River
1 Musical Mystic River
1 Horror Mystic River
1 Drama Mystic River
1 Comedie Mystic River
1 Copii şi Familie Mystic River
1 Actiune şi Aventura The Last Samurai
2 Thriller The Last Samurai
2 Sport The Last Samurai
2 SF The Last Samurai
2 Romantic The Last Samurai
2 Musical The Last Samurai
2 Independent The Last Samurai
2 Horror The Last Samurai
2 Animatie The Last Samurai
2 Copii şi Familie The Last Samurai
2 Documentar The Last Samurai
2 Strain The Last Samurai
2 Drama The Last Samurai
2 Comedie The Last Samurai
2 Clasic The Last Samurai
.. ... ...............
Setul de rezultate al interogării a fost trunchiat după primele două titluri de
filme. Problema este că am cerut bazei de date să unească tabelele, dar nu i-am
spus care este corespondenţa dintre rândurile celor două tabele. Rezultatul este
cunoscut sub numele de produs cartezian (după numele filozofului şi
matematicianului francez Rene Descartes) şi, în bazele de date relaţionale, este un
set de rezultate obţinut prin uniunea dintre fiecare rând al unui tabel cu fiecare rând
dintr-un alt tabel.
Soluţia este să specificăm cum se va face unirea tabelelor. Trebuie indicat ce
coloane ar trebui să se potrivească pentru a uni două rânduri din cele două tabele,
în mod normal, cele două coloane vor fi cheia primară dintr-un tabel şi cheia
externă din celălalt tabel, dar pot există şi excepţii. Pentru a realiza acest lucru
trebuie să se facă o o uniune standard relaţională, cunoscut şi sub numele de
uniune de egalitate (equijoin).
Join-ul este operaţia de regăsire a datelor din două sau mai multe tabele, pe
baza valorilor comune ale unor coloane. De obicei, aceste coloane reprezintă cheia
primară, respectiv cheia externă a tabelelor.
Condiţia de join se scrie în clauza WHERE a instrucţiunii SELECT. Într-o
instrucţiune SELECT care uneşte tabele prin operaţia de join, se recomandă ca
numele coloanelor să fie precedate de numele tabelelor pentru claritate şi
pentru îmbunătăţirea timpului de acces la baza de date. Dacă acelaşi nume de
coloană apare în mai mult de două tabele, atunci numele coloanei se prefixează
obligatoriu cu numele tabelului corespunzător. Pentru a realiza un join între n
tabele, va fi nevoie de cel puţin n – 1 condiţii de join.
Equijoin-ul corespunde situaţiei în care valorile de pe coloanele ce apar în
condiţia de join trebuie să fie egale. Un astfel de join mai poartă numele de join
simplu sau inner join. Un nonequijoin este o condiţie de join care conţine alţi
operatori decât operatorul egalitate. Un self join realizează compunerea unui tabel
cu el însuşi.
Exemplu:
Să se obţină codurile şi titlurile cărţilor împrumutate.
SELECT carte.codel, titlu
FROM carte, imprumuta
WHERE carte.codel = imprumuta.codel;
Exemplu:
a) Să se afişeze informaţii despre operele de artă achiziţionate în 2008 şi
galeriile în care au fost expuse.
Exemplu:
Clauza JOIN este scrisă ca o referinţă de tabel din clauza FROM şi combină
lista de tabele din clauza FROM şi condiţia de legătură scrisă anterior în clauza
WHERE într-o singură clauză. Sintaxa generală a clauzei JOIN pentru o uniune
internă, urmată de câteva exemple.
Exemple:
• JOIN cu condiţie ON:
• JOIN folosind cuvântul cheie USING (în locul condiţiei ON) o scurtatură
elegantă atunci când coloanele din cele două tabele au acelaşi nume (nu e
recunoscută de toate SGBD-urile).
Uniuni naturale
Toate uniunile pe care le-am descris până acum sunt uniuni exclusiv (uniuni
interne), ceea ce înseamnă că singurele rânduri care apar în setul de rezultate sunt
cele pentru care a fost găsită o legătură în toate tabelele unite.
Există situaţii în care se doreşte să includeţi în rezultatele interogării şi
rânduri pentru care nu există o legătură. De exemplu, dacă se doreşte o listă cu
toate genurile de filme, împreună cu toate titlurile asociate cu fiecare gen?
O uniune externă (outer join) - pentru care un nume mai potrivit ar fi uniune
inclusivă - include în setul de rezultate şi rândurile pentru care nu există legături
din cel puţin unul dintre tabele. Atunci când există rânduri fără legături, datele
selectate din tabelul în care nu a fost găsită o legătură primesc valoarea nulă.
Un outer join este utilizat pentru a obţine în rezultat şi înregistrările care nu
satisfac condiţia de join. Operatorul pentru outer join este semnul plus inclus între
paranteze (+), care se plasează în acea parte a condiţiei de join care este deficientă
în informaţie. Efectul acestui operator este de a uni liniile tabelului care nu este
deficient în informaţie şi cărora nu le corespunde nici o linie în celălalt tabel cu o
linie cu valori null. Operatorul (+) poate fi plasat în orice parte a condiţiei de join,
dar nu în ambele părţi.
O condiţie care presupune un outer join nu poate utiliza operatorul IN şi nu
poate fi legată de altă condiţie prin operatorul OR.
Exemplu:
Se presupune că tabelul artist conţine şi artişti care nu au opere expuse în
muzeu. Să se afişeze artiştii şi operele acestora, inclusiv cei care nu au opere
corespunzătoare în tabelul opera.
SELECT a.cod_artist, nume, prenume, cod_opera, titlu
FROM artist a, opera o
WHERE a.cod_artist = o.cod_artist (+);
GEN FILM_TITLU
Actiune şi Aventura Kill Bill: Vol. 1
Actiune şi Aventura Man on Fire
Actiune şi Aventura Pirates of the
Caribbean
Actiune şi Aventura Master and
Commander
Actiune şi Aventura The Day After
Tomorrow
Actiune şi Aventura The Last Samurai
Actiune şi Aventura The Italian Job
Anime and
Animation
Copii şi Familie
Clasic
Comedie 50 First Dates
Comedie Matchstick Men
Comedie The School of Rock
Comedie Something's Gotta
Give
Documentar
Drama Big Fiah
Drama Road to Perdition
Drama Mystic River
Drama Monster
Drama Cold Mountain
Drama Lost în Translation
Strain Das Boot
Horror
Independent
Musical
Romantic 13 Going on 30
Romantic Two Weeks Notice
GEN
Animatie
Copii şi Familie
Clasic
Documentar
Horror
Independent
Musical
SF
Special Interes
Sport
Thriller
Exemplu:
Subinterogări
Subinterogări necorelate
LIMBA_COD LIMBA_NUME
Ja Japoneza
Ko Coreana
ni Olandeza
ru Rusa
zh Chineza
TRANZACTIE_ID TRANZ_DATA
9 03/01/2005
10 03/01/2005
Subinterogări corelate
CLIENT_CONT_ID
2
7
9
Observaţi pseudonimele asociate numelor de tabele din interogările
internă şi externă, precurn şi folosirea acestora în clauza WHERE a interogării
interne. Acesta este elementul de identificare al unei subinterogări corelate.
Interogarea externă selectează o lista de valori CLIENT_CONT_ID distincte din
tabelul CLIENT_TRANZACTIE. Fiecare valoare găsită este transmisă interogării
interne, care este rulată pentru a calcula suma taxelor de închiriere din tranzacţia
respectivă. Dacă suma taxelor de închiriere este mai mare sau egală cu 15, atunci
clauza WHERE din interogarea externă ia valoarea logică „adevărat", iar rândul
CLIENT_ID este adăugat în setul de rezultate.
Vizualizări în linie
Exemplu:
Să se obţină numele şi salariul angajaţilor, având salariul minim.
SELECT name, sal
FROM angajati
WHERE sal=(SELECT MIN(sal)
FROM angajati);
Exemplu:
Să se obţină job-ul pentru care salariul mediu este minim. Sa se afiseze si
salariul mediu.
SELECT job, AVG(sal)
FROM angajati
GROUP BY job
HAVING AVG(sal)=(SELECT MIN(AVG(sal))
FROM angajati
GROUP BY job);
Dacă una din valorile returnate de subcerere este valoarea null atunci cererea
nu întoarce nici o linie. Prin urmare, dacă valoarea null poate să facă parte din
rezultatul subcererii nu trebuie utilizat operatorul NOT IN. Problema nu mai apare
dacă se utilizează operatorul IN.
Exemplu:
Să se obţină salariaţii care nu au subordonaţi.
SELECT a.nume
FROM angajati a
WHERE a.cod_ang NOT IN (SELECT m.mgr
FROM angajati m);
În acest caz, instrucţiunea SQL nu întoarce nici o linie deoarece una din
valorile furnizate de subcerere este valoarea null.
Subinterogări necorelate (nesincronizate)
Exemple:
LIMBA_COD LIMBA_NUME
Ja Japoneza
Ko Coreana
ni Olandeza
ru Rusa
zh Chineza
TRANZACTIE_ID TRANZ_DATA
9 03/01/2005
10 03/01/2005
Exemplu:
CLIENT_CONT_ID
2
7
9
Observaţi pseudonimele asociate numelor de tabele din interogările internă
şi externă, precurn şi folosirea acestora în clauza WHERE a interogării interne.
Acesta este elementul de identificare al unei subinterogări corelate. Interogarea
externă selectează o lista de valori CLIENT_CONT_ID distincte din tabelul
CLIENT_TRANZACTIE. Fiecare valoare găsită este transmisă interogării interne,
care este rulată pentru a calcula suma taxelor de închiriere din tranzacţia
respectivă. Dacă suma taxelor de închiriere este mai mare sau egală cu 15, atunci
clauza WHERE din interogarea externă ia valoarea logică „adevărat", iar rândul
CLIENT_ID este adăugat în setul de rezultate.
Vizualizări în linie
Foarte puţine implementări, printre care Oracle, permit folosirea unei subinterogări
în clauza FROM a unei interogări, într-o construcţie numită vizualizare în linie
(inline view). Această construcţie permite care setul de rezultate al unei interogări
să fie tratat ca şi cum ar fi un tabel sau o vizualizare predefinită. Iată un exemplu:
• Această interogare află numărul maxim de închirieri ale unui singur film;
MAX_INCH_NUMAR
5
Operatorul EXISTS
În instrucţiunile SELECT imbricate, este permisă utilizarea oricărui operator
logic. Pentru a testa dacă valoarea recuperată de cererea externă există în mulţimea
valorilor regăsite de cererea internă corelată, se poate utiliza operatorul EXISTS.
Dacă subcererea returnează cel puţin o linie, operatorul returnează valoarea TRUE.
În caz contrar, va fi returnată valoarea FALSE.
Operatorul EXISTS asigură că nu mai este continuată căutarea în cererea
internă după ce aceasta regăseşte o linie.
Exemplu:
a) Să se determine codul, numele şi prenumele artiştilor care au cel puţin o
operă de artă expusă în muzeu.
SELECT cod_artist, nume, prenume
FROM artist a
WHERE EXISTS (SELECT 'x'
FROM opera
WHERE cod_artist = a.cod_artist);
Întrucât nu este necesar ca instrucţiunea SELECT interioară să returneze o
anumită valoare, se poate selecta o constantă. De altfel, din punct de vedere al
performanţei, selectarea unei constante asigură mai multă rapiditate decât
selectarea unei coloane.
Ca alternativă a lui EXISTS, poate fi utilizat operatorul IN. Exemplul
precedent poate fi rezolvat prin instrucţiunea următoare:
SELECT cod_artist, nume, prenume
FROM artist
WHERE cod_artist IN (SELECT cod_artist
FROM opera);
b) Să se determine operele care nu sunt menţionate în nici o sursă
bibliografică.
SELECT cod_opera, titlu
FROM opera o
WHERE NOT EXISTS (SELECT 'x'
FROM mentionata_in
WHERE cod_opera = o.cod_opera);
Acest exemplu poate fi rezolvat şi printr-o subcerere necorelată, utilizând
operatorul NOT IN:
SELECT cod_opera, titlu
FROM opera
WHERE cod_opera NOT IN (SELECT cod_opera
FROM ment_in
WHERE cod_opera IS NOT NULL);
Clauza WITH
Cu ajutorul clauzei WITH se poate defini un bloc de cerere înainte ca acesta
să fie utilizat într-o interogare. Clauza permite reutilizarea aceluiaşi bloc de cerere
într-o instrucţiune SELECT complexă. Acest lucru este util atunci când o cerere
face referinţă de mai multe ori la acelaşi bloc de cerere, care conţine operaţii join şi
funcţii agregat. Folosind clauza WITH, server-ul Oracle regăseşte rezultatele unui
bloc de cerere şi le stochează în spaţiul tabel temporar al utilizatorului, ceea ce
poate determina îmbunătăţirea performanţelor.
Exemplu:
Utilizând clauza WITH, să se scrie o cerere care afişează numele artiştilor şi
valoarea totală a operelor acestora. Se vor considera artiştii a căror valoare totală a
operelor este mai mare decât media valorilor totale ale operelor tuturor artiştilor.
WITH
val_artist AS (SELECT nume, SUM(valoare) AS total
FROM opera o, artist a
WHERE o.cod_artist = a.cod_artist
GROUP BY nume),
val_medie AS (SELECT SUM(total)/COUNT(*) AS medie
FROM val_artist)
SELECT *
FROM val_artist
WHERE total > (SELECT medie
FROM val_medie)
ORDER BY nume;
Intern, clauza WITH este tratată ca o vizualizare inline (subcerere în clauza
FROM) sau ca un tabel temporar. Optimizorul alege decizia adecvată pe baza
costului sau beneficiului stocării temporare a rezultatelor clauzei WITH.
Observaţii:
Clauza WITH poate fi folosită numai pentru instrucţiuni SELECT.
Un nume de cerere este vizibil tuturor blocurilor din clauza WITH
definite ulterior (inclusiv subcererilor acestora). De asemenea, un nume
de cerere este vizibil cererii principale şi subcererilor acesteia.
Când un nume de cerere coincide cu numele unui tabel, numele blocului
de cerere are precedenţă asupra numelui tabelului.
Clauza WITH poate conţine mai mult decât o singură cerere. În acest caz,
cererile sunt separate prin virgule.
2.4.4. Funcţii grup şi clauza GROUP BY
Clauza GROUP BY este utilizată pentru a diviza liniile unui tabel în grupuri.
Pentru a returna informaţia corespunzătoare fiecărui astfel de grup, pot fi utilizate
funcţiile agregat. Ele pot apărea în clauzele SELECT, ORDER BY şi HAVING.
Server-ul Oracle aplică aceste funcţii fiecărui grup de linii şi returnează un singur
rezultat pentru fiecare mulţime.
Dintre funcţiile grup definite în sistemul Oracle, se pot enumera: AVG,
SUM, MAX, MIN, COUNT, STDDEV, VARIANCE, DENSE_RANK, RANK, FIRST,
LAST, GROUP_ID, GROUPING, GROUPING_ID etc. Tipurile de date ale
argumentelor funcţiilor grup pot fi CHAR, VARCHAR2, NUMBER sau DATE.
Funcţiile AVG, SUM, STDDEV şi VARIANCE operează numai asupra valorilor
numerice. Funcţiile MAX şi MIN pot opera asupra valorilor numerice, caracter sau
de tip dată calendaristică.
Toate funcţiile grup, cu excepţia lui COUNT(*), ignoră valorile null.
COUNT(expresie) returnează numărul de linii pentru care expresia dată nu are
valoarea null. Funcţia COUNT returnează un număr mai mare sau egal cu zero şi
nu întoarce niciodată valoarea null.
Când este utilizată clauza GROUP BY, server-ul sortează implicit mulţimea
rezultată în ordinea crescătoare a valorilor coloanelor după care se realizează
gruparea.
În clauza GROUP BY a unei cereri se pot utiliza operatorii ROLLUP şi
CUBE. Aceştia sunt disponibili începând cu versiunea Oracle8i.
Operatorul ROLLUP
Operatorul ROLLUP produce o mulţime care conţine liniile obţinute în urma
grupării obişnuite şi linii pentru subtotaluri. Acest operator furnizează valori
agregat şi superagregat corespunzătoare expresiilor din clauza GROUP BY.
Operatorul ROLLUP poate fi folosit pentru extragerea de statistici şi informaţii
totalizatoare din mulţimile rezultate. Acest operator poate fi util la generarea de
rapoarte, diagrame şi grafice.
Operatorul ROLLUP creează grupări prin deplasarea într-o singură direcţie,
de la dreapta la stânga, de-a lungul listei de coloane specificate în clauza GROUP
BY. Apoi, se aplică funcţia agregat acestor grupări. Dacă sunt specificate n expresii
în operatorul ROLLUP, numărul de grupări generate va fi n + 1. Liniile care se
bazează pe valoarea primelor n expresii se numesc linii obişnuite, iar celelalte se
numesc linii superagregat.
Dacă în clauza GROUP BY sunt specificate n coloane, pentru a produce
subtotaluri fără operatorul ROLLUP ar fi necesare n + 1 instrucţiuni SELECT
conectate prin UNION ALL. Aceasta ar face execuţia cererii ineficientă deoarece
fiecare instrucţiune SELECT determină accesarea tabelului. Operatorul ROLLUP
determină rezultatele efectuând un singur acces la tabel şi este util atunci când sunt
implicate multe coloane în producerea subtotalurilor.
Exemplu:
Să se afişeze codurile de galerii mai mici decât 50, iar pentru fiecare dintre
acestea şi pentru fiecare autor care are opere expuse în galerie, să se listeze
valoarea totală a lucrărilor sale. De asemenea, se cere valoarea totală a operelor
expuse în fiecare galerie. Rezultatul va conţine şi valoarea totală a operelor din
galeriile având codul mai mic decât 50, indiferent de codul autorului.
SELECT cod_galerie, cod_artist, SUM(valoare)
FROM opera
WHERE cod_galerie < 50
GROUP BY ROLLUP(cod_galerie, cod_artist);
Instrucţiunea precedentă va avea un rezultat de forma:
COD_GALERIE COD_ARTIST SUM(VALOARE)
10 50 14000
10 60 10000
10 24000
40 50 8080
40 8080
32080
Exemplu:
Funcţia GROUPING
Funcţia GROUPING poate fi utilizată alături de operatorii CUBE şi
ROLLUP pentru a arăta modul cum a fost obţinută o valoare totalizatoare. Această
funcţie acceptă un singur argument, care trebuie să fie una dintre expresiile
specificate în clauza GROUP BY.
O valoare null a expresiei din argumentul funcţiei GROUPING poate
proveni din tabelul de bază (valoare null stocată) sau poate fi o valoare null creată
de operaţia ROLLUP sau CUBE ca rezultat al unei funcţii grup asupra expresiei
respective.
Funcţia GROUPING returnează valoarea 0 sau 1. Valoarea 0 returnată de
funcţia GROUPING pe baza unei expresii poate indica fie că expresia a fost
utilizată pentru calculul valorii agregat, fie că valoarea null a expresiei este o
valoare null stocată. Valoarea 1 returnată de funcţia GROUPING pe baza unei
expresii poate indica fie că expresia nu a fost utilizată pentru calculul valorii
agregat, fie că valoarea null a expresiei este creată de ROLLUP sau CUBE ca
rezultat al grupării.
Valorile returnate de funcţia GROUPING sunt utile pentru:
determinarea nivelului de agregare al unui subtotal dat, adică a grupului
sau grupurilor pe care se bazează subtotalul respectiv;
identificarea provenienţei unei valori null a unei expresii calculate, dintr-
una din liniile mulţimii rezultat.
Exemplu:
SELECT cod_galerie, cod_artist, SUM(valoare),
GROUPING(cod_galerie), GROUPING(cod_artist)
FROM opera
WHERE cod_galerie < 50
GROUP BY ROLLUP(cod_galerie, cod_artist);
SUM GROUPING GROUPING
COD_GALERIE COD_ARTIST
(VALOARE) (COD_GALERIE) (COD_ARTIST)
10 50 14000 0 0
10 60 10000 0 0
10 24000 0 1
40 50 8080 0 0
40 8080 0 1
32080 1 1
Pe prima linie din acest rezultat, valoarea totalizatoare reprezintă suma
valorilor operelor artistului având codul 50, în cadrul galeriei 10. Pentru a calcula
această valoare au fost luate în considerare coloanele cod_galerie şi cod_artist.
Prin urmare, expresiile GROUPING(cod_galerie) şi GROUPING(cod_artist) au
valoarea 0 pentru prima linie din rezultat.
Pe linia a treia se află valoarea totală a operelor din galeria având codul 10.
Această valoare a fost calculată luând în considerare doar coloana cod_galerie,
astfel încât GROUPING (cod_galerie) şi GROUPING(cod_artist) au valorile 0,
respectiv 1.
Pe ultima linie din rezultat se află valoarea totală a operelor din galeriile
având codul mai mic decât 50. Nici una dintre coloanele cod_galerie şi cod_artist
nu au intervenit în calculul acestui total, prin urmare valorile corespunzătoare
expresiilor GROUPING(cod_galerie) şi GROUPING(cod_artist) sunt 0.
Clauza GROUPING SETS
GROUPING SETS reprezintă o extensie a clauzei GROUP BY care permite
specificarea unor grupări multiple de date. Utilizarea acestei opţiuni facilitează
analiza datelor în mai multe dimensiuni.
Această extensie, apărută în sistemul Oracle9i, permite scrierea unei singure
instrucţiuni SELECT pentru a specifica grupări diferite (care pot conţine operatorii
ROLLUP şi CUBE), în loc de mai multe instrucţiuni SELECT combinate prin
operatorul UNION ALL. De altfel, reuniunea rezultatelor mai multor cereri este
ineficientă întrucât necesită mai multe parcurgeri ale aceloraşi date.
Operatorii ROLLUP şi CUBE pot fi consideraţi cazuri particulare de mulţimi
de grupări. Au loc următoarele echivalenţe:
CUBE(a, b, c) GROUPING SETS
((a, b, c), (a, b), (a, c), (b, c), (a),
(b), (c), ())
ROLLUP(a, b, GROUPING SETS
c) ((a, b, c), (a, b), (a), ())
Exemplu:
Considerând galeriile al căror cod este mai mic decât 50, să se calculeze
media valorilor operelor:
pentru fiecare galerie şi, în cadrul acesteia, pentru fiecare artist;
pentru fiecare artist şi, în cadrul acestuia, pentru anii de achiziţie
corespunzători.
SELECT cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy') "an achizitie",
AVG(valoare) "Valoare medie"
FROM opera
WHERE cod_galerie < 50
GROUP BY GROUPING SETS
((cod_galerie, cod_artist),
(cod_artist, TO_CHAR(data_achizitiei, 'yyyy')));
Mulţimea rezultat este constituită din valorile medii pentru fiecare dintre
cele două grupuri şi are forma următoare:
COD_GALERIE COD_ARTIST an a Valoare medie
10 50 3500
10 60 2500
40 50 2020
50 2000 2380
50 2002 2300
60 2001 2000
60 2003 3000
Concatenarea grupărilor
Concatenarea grupărilor reprezintă o modalitate concisă de a genera
combinaţii de grupări. Acestea se specifică prin enumerarea mulţimilor de grupări
(grouping sets) şi a operaţiilor ROLLUP, CUBE separate prin virgulă. De exemplu,
expresia GROUP BY GROUPING SETS(a, b), GROUPING SETS(c, d) defineşte
grupările (a, c), (a, d), (b, c), (b, d).
Concatenarea mulţimilor de grupări este utilă atât pentru uşurinţa dezvoltării
cererilor, cât şi pentru aplicaţii. Codul SQL generat de aplicaţiile OLAP implică
deseori concatenarea mulţimilor de grupări, în care fiecare astfel de mulţime
defineşte grupările necesare pentru o dimensiune.
Exemplu:
Să se determine media valorilor operelor luând în considerare următoarele
grupări: (cod_galerie, cod_artist, an_achizitie), (cod_galerie, cod_artist),
(cod_galerie, an_achizitie), (cod_galerie).
SELECT cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy') an_achizitie,
AVG(valoare)
FROM opera
GROUP BY cod_galerie, ROLLUP(cod_artist),
CUBE(TO_CHAR(data_achizitiei, 'yyyy'));
PROBLEME-SUBCERERI
Exemplu:
Să se obţină lista celor mai scumpe cărţi.
SELECT titlu
FROM carte
WHERE pret = (SELECT MAX(pret)
FROM carte);
Exemplu:
Să se obţină lista scriitorilor care au în bibliotecă un număr de exemplare
mai mare decât numărul mediu al cărţilor din bibliotecă.
SELECT DISTINCT autor
FROM carte
WHERE nrex > (SELECT AVG(nrex)
FROM carte);
Exemplu:
Să se obţină informaţii despre cărţile al căror preţ depăşeşte media preţurilor
cărţilor ce aparţin aceluiaşi domeniu
SELECT *
FROM carte c
WHERE pret > (SELECT AVG(pret)
FROM carte
WHERE coded = c.coded);
Exemplu:
Să se obţină lista cititorilor care au împrumutat cel puţin o carte.
SELECT nume
FROM cititor
WHERE codec IN (SELECT DISTINCT codec
FROM imprumuta);
Exemplu:
Să se obţină codurile cititorilor care nu au împrumutat niciodată cărţi.
SELECT codec
FROM cititor
WHERE codec NOT IN
(SELECT DISTINCT codec
FROM imprumuta);
Exemplu:
Să se obţină lista cititorilor care sunt în întârziere cu predarea cărţilor.
SELECT nume
FROM cititor
WHERE codec IN (SELECT DISTINCT codec
FROM imprumuta
WHERE dataef IS NULL
AND dares<SYSDATE);
Exemplu:
Să se obţină numele cititorilor care au împrumutat cel puţin o carte scrisă de
ZOLA.
SELECT nume
FROM cititor
WHERE codec IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE autor=‟ZOLA‟));
Exemplu:
Să se obţină numele cititorilor care nu au împrumutat nici o carte scrisă de
ZOLA.
SELECT nume
FROM cititor
WHERE codec NOT IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE autor=‟ZOLA‟));
Operatorul IN poate fi înlocuit cu = ANY (un element este în listă dacă şi
numai dacă este egal cu un element al listei), iar operatorul NOT IN poate fi
înlocuit prin !=ALL.
Exemplu:
Să se obţină codurile cititorilor care au împrumutat o carte de algebră.
SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE coded=
(SELECT coded
FROM domeniu
WHERE intdom=‟ALGEBRA‟));
Exemplu:
Să se obţină cititorii care au împrumutat numai cărţi scrise de „ZOLA‟.
SELECT nume
FROM cititor
WHERE codec NOT IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel NOT IN
(SELECT codel
FROM carte
WHERE autor=‟ZOLA‟));
Exemplu:
Să se obţină numele cititorilor care au împrumutat cel puţin o carte de
informatică (procedural).
SELECT nume
FROM cititor
WHERE codec IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE coded=
(SELECT coded
FROM domeniu
WHERE intdom= ‟INFORMATICA‟)));
Exemplu:
Să se obţină numele cititorilor şi titlurile cărţilor de informatică împrumutate
de aceşti cititori (relational).
SELECT nume, titlu
FROM cititor, carte, imprumuta, domeniu
WHERE imprumuta.codel = carte.codel
AND carte.coded = domeniu.coded
AND imprumuta.codec = cititor.codec
AND intdom = ‟INFORMATICA‟;
Subinterogări necorelate
Exemple:
LIMBA_COD LIMBA_NUME
Ja Japoneza
Ko Coreana
ni Olandeza
ru Rusa
zh Chineza
TRANZACTIE_ID TRANZ_DATA
9 03/01/2005
10 03/01/2005
Exemplu(din galeria de arta):
a) Să se afişeze titlul, codul artistului şi valoarea operelor create de artistul
căruia îi aparţine opera având codul 180 şi care se află expuse în aceeaşi galerie cu
operele al căror cod este 100 sau 110. Se presupune că o operă are un singur autor.
SELECT titlu, cod_artist, valoare
FROM opera
WHERE cod_artist = (SELECT cod_artist
FROM opera
WHERE cod_opera = 180)
AND cod_galerie IN (SELECT cod_galerie
FROM opera
WHERE cod_opera IN (100, 110));
b) Să se determine artistul pentru care valoare medie a operelor sale de artă
este minimă.
SELECT cod_artist, AVG(valoare)
FROM opera
GROUP BY cod_artist
HAVING AVG(valoare) = (SELECT MIN(AVG(valoare))
FROM opera
GROUP BY cod_artist);
c) Pentru fiecare artist, să se afişeze titlul şi valoarea celei mai ieftine opere
de artă expuse în muzeu.
SELECT titlu, cod_artist, valoare
FROM opera
WHERE valoare IN (SELECT MIN(valoare)
FROM opera
GROUP BY cod_artist);
d) Să se afişeze operele de artă care nu sunt expuse în galeria având codul
30 şi a căror valoare este mai mică decât a unei opere din galeria 30.
SELECT cod_opera, titlu, valoare
FROM opera
WHERE valoare < ANY (SELECT valoare
FROM opera
WHERE cod_galerie = 30)
AND cod_galerie <> 30;
Dacă operatorul ANY se înlocuieşte cu operatorul ALL, cererea precedentă
returnează operele care nu sunt expuse în galeria 30 şi a căror valoare este mai
mică decât a oricărei opere din galeria respectivă.
e) Să se afişeze cele mai scumpe 3 opere de artă din muzeu.
SELECT ROWNUM "Nr.Crt", titlu, valoare
FROM (SELECT titlu, valoare
FROM opera
ORDER BY valoare DESC)
WHERE ROWNUM <= 3;
Exemplu:
Din punct de vedere logic, următoarea cerere ar trebui să returneze titlurile
operelor care nu sunt expuse în galeriile unde se află lucrările artistului având
codul 60.
SELECT titlu
FROM opera
WHERE cod_galerie NOT IN (SELECT cod_galerie
FROM opera
WHERE cod_artist = 60);
Dacă pentru una dintre opere valoarea coloanei cod_galerie este null,
întreaga cerere nu va returna nici o linie, chiar dacă există înregistrări cu
proprietatea cerută. Justificarea este că toate condiţiile în care unul dintre operanzi
este null au, de asemenea, valoarea null. Prin urmare, ori de câte ori este posibil ca
în rezultatul subcererii să apară valori null, nu trebuie folosit operatorul NOT IN.
Operatorul NOT IN este echivalent cu <>ALL. Dacă se utilizează operatorul IN, nu
mai este nici o problemă dacă în rezultatul subcererii apar valori null. Operatorul
IN este echivalent cu =ANY.
Exemplu:
Să se afişeze titlurile operelor care sunt expuse în galeriile unde se află
lucrările artistului având codul 60.
SELECT titlu
FROM opera
WHERE cod_galerie IN (SELECT cod_galerie
FROM opera
WHERE cod_artist = 60);
Pentru a evita obţinerea de valori null în rezultatul unei cereri care utilizează
operatorul NOT IN, se poate specifica o condiţie în subcerere:
SELECT titlu
FROM opera
WHERE cod_galerie NOT IN (SELECT cod_galerie
FROM opera
WHERE cod_artist = 60
AND cod_galerie IS NOT NULL);
Exemplu:
CLIENT_CONT_ID
2
7
9
Observaţi pseudonimele asociate numelor de tabele din interogările internă
şi externă, precurn şi folosirea acestora în clauza WHERE a interogării interne.
Acesta este elementul de identificare al unei subinterogări corelate. Interogarea
externă selectează o lista de valori CLIENT_CONT_ID distincte din tabelul
CLIENT_TRANZACTIE. Fiecare valoare găsită este transmisă interogării interne,
care este rulată pentru a calcula suma taxelor de închiriere din tranzacţia
respectivă. Dacă suma taxelor de închiriere este mai mare sau egală cu 15, atunci
clauza WHERE din interogarea externă ia valoarea logică „adevărat", iar rândul
CLIENT_ID este adăugat în setul de rezultate.
Vizualizări în linie
Foarte puţine implementări, printre care Oracle, permit folosirea unei subinterogări
în clauza FROM a unei interogări, într-o construcţie numită vizualizare în linie
(inline view). Această construcţie permite care setul de rezultate al unei interogări
să fie tratat ca şi cum ar fi un tabel sau o vizualizare predefinită. Iată un exemplu:
• Această interogare află numărul maxim de închirieri ale unui singur film;
MAX_INCH_NUMAR
5
Clauza WITH
Cu ajutorul clauzei WITH se poate defini un bloc de cerere înainte ca acesta
să fie utilizat într-o interogare. Clauza permite reutilizarea aceluiaşi bloc de cerere
într-o instrucţiune SELECT complexă. Acest lucru este util atunci când o cerere
face referinţă de mai multe ori la acelaşi bloc de cerere, care conţine operaţii join şi
funcţii agregat. Folosind clauza WITH, server-ul Oracle regăseşte rezultatele unui
bloc de cerere şi le stochează în spaţiul tabel temporar al utilizatorului, ceea ce
poate determina îmbunătăţirea performanţelor.
Exemplu:
Utilizând clauza WITH, să se scrie o cerere care afişează numele artiştilor şi
valoarea totală a operelor acestora. Se vor considera artiştii a căror valoare totală a
operelor este mai mare decât media valorilor totale ale operelor tuturor artiştilor.
WITH
val_artist AS (SELECT nume, SUM(valoare) AS total
FROM opera o, artist a
WHERE o.cod_artist = a.cod_artist
GROUP BY nume),
val_medie AS (SELECT SUM(total)/COUNT(*) AS medie
FROM val_artist)
SELECT *
FROM val_artist
WHERE total > (SELECT medie
FROM val_medie)
ORDER BY nume;
Intern, clauza WITH este tratată ca o vizualizare inline (subcerere în clauza
FROM) sau ca un tabel temporar. Optimizorul alege decizia adecvată pe baza
costului sau beneficiului stocării temporare a rezultatelor clauzei WITH.
Observaţii:
Clauza WITH poate fi folosită numai pentru instrucţiuni SELECT.
Un nume de cerere este vizibil tuturor blocurilor din clauza WITH
definite ulterior (inclusiv subcererilor acestora). De asemenea, un nume
de cerere este vizibil cererii principale şi subcererilor acesteia.
Când un nume de cerere coincide cu numele unui tabel, numele blocului
de cerere are precedenţă asupra numelui tabelului.
Clauza WITH poate conţine mai mult decât o singură cerere. În acest caz,
cererile sunt separate prin virgule.
Subcereri scalare
Subcererile scalare în SQL returnează valoarea unei singure coloane
corespunzătoare unei linii. Dacă subcererea returnează 0 linii, valoarea subcererii
scalare este null. Dacă subcererea returnează mai mult de o linie, server-ul
generează o eroare.
Subcererile scalare erau acceptate în Oracle8i doar în anumite cazuri, cum ar
fi clauzele FROM şi WHERE ale instrucţiunii SELECT sau clauza VALUES a
instrucţiunii INSERT. Utilitatea subcererilor scalare a fost extinsă în Oracle9i.
Astfel, ele pot apărea în:
condiţiile şi expresiile care fac parte din DECODE sau CASE;
toate clauzele instrucţiunii SELECT, cu excepţia lui GROUP BY;
în partea stângă a operatorului, în clauzele SET şi WHERE ale
instrucţiunii UPDATE.
Subcererile scalare nu sunt permise în următoarele situaţii:
ca valori implicite pentru coloane sau ca expresii hash pentru grupări;
în clauza RETURNING a instrucţiunilor LMD;
în clauza GROUP BY, constrângerile de tip CHECK, condiţiile WHEN;
în instrucţiunile care nu sunt legate de cereri, cum ar fi CREATE
PROFILE.
Exemplu:
Să se afişeze codul, titlul operelor şi numele artistului doar dacă acesta este
Brâncuşi. În caz contrar, se va afişa şirul „alt artist“.
SELECT cod_opera, titlu,
(CASE WHEN cod_artist =
(SELECT cod_artist
FROM artist
WHERE nume = 'Brancusi')
THEN 'Brancusi'
ELSE 'Alt artist' END) artist
FROM opera;
Clauza GROUP BY este utilizată pentru a diviza liniile unui tabel în grupuri.
Pentru a returna informaţia corespunzătoare fiecărui astfel de grup, pot fi utilizate
funcţiile agregat. Ele pot apărea în clauzele SELECT, ORDER BY şi HAVING.
Server-ul Oracle aplică aceste funcţii fiecărui grup de linii şi returnează un singur
rezultat pentru fiecare mulţime.
Dintre funcţiile grup definite în sistemul Oracle, se pot enumera: AVG,
SUM, MAX, MIN, COUNT, STDDEV, VARIANCE, DENSE_RANK, RANK, FIRST,
LAST, GROUP_ID, GROUPING, GROUPING_ID etc. Tipurile de date ale
argumentelor funcţiilor grup pot fi CHAR, VARCHAR2, NUMBER sau DATE.
Funcţiile AVG, SUM, STDDEV şi VARIANCE operează numai asupra valorilor
numerice. Funcţiile MAX şi MIN pot opera asupra valorilor numerice, caracter sau
de tip dată calendaristică.
Toate funcţiile grup, cu excepţia lui COUNT(*), ignoră valorile null.
COUNT(expresie) returnează numărul de linii pentru care expresia dată nu are
valoarea null. Funcţia COUNT returnează un număr mai mare sau egal cu zero şi
nu întoarce niciodată valoarea null.
Când este utilizată clauza GROUP BY, server-ul sortează implicit mulţimea
rezultată în ordinea crescătoare a valorilor coloanelor după care se realizează
gruparea.
În clauza GROUP BY a unei cereri se pot utiliza operatorii ROLLUP şi
CUBE. Aceştia sunt disponibili începând cu versiunea Oracle8i.
Operatorul ROLLUP
Operatorul ROLLUP produce o mulţime care conţine liniile obţinute în urma
grupării obişnuite şi linii pentru subtotaluri. Acest operator furnizează valori
agregat şi superagregat corespunzătoare expresiilor din clauza GROUP BY.
Operatorul ROLLUP poate fi folosit pentru extragerea de statistici şi informaţii
totalizatoare din mulţimile rezultate. Acest operator poate fi util la generarea de
rapoarte, diagrame şi grafice.
Operatorul ROLLUP creează grupări prin deplasarea într-o singură direcţie,
de la dreapta la stânga, de-a lungul listei de coloane specificate în clauza GROUP
BY. Apoi, se aplică funcţia agregat acestor grupări. Dacă sunt specificate n expresii
în operatorul ROLLUP, numărul de grupări generate va fi n + 1. Liniile care se
bazează pe valoarea primelor n expresii se numesc linii obişnuite, iar celelalte se
numesc linii superagregat.
Dacă în clauza GROUP BY sunt specificate n coloane, pentru a produce
subtotaluri fără operatorul ROLLUP ar fi necesare n + 1 instrucţiuni SELECT
conectate prin UNION ALL. Aceasta ar face execuţia cererii ineficientă deoarece
fiecare instrucţiune SELECT determină accesarea tabelului. Operatorul ROLLUP
determină rezultatele efectuând un singur acces la tabel şi este util atunci când sunt
implicate multe coloane în producerea subtotalurilor.
Exemplu:
Să se afişeze codurile de galerii mai mici decât 50, iar pentru fiecare dintre
acestea şi pentru fiecare autor care are opere expuse în galerie, să se listeze
valoarea totală a lucrărilor sale. De asemenea, se cere valoarea totală a operelor
expuse în fiecare galerie. Rezultatul va conţine şi valoarea totală a operelor din
galeriile având codul mai mic decât 50, indiferent de codul autorului.
SELECT cod_galerie, cod_artist, SUM(valoare)
FROM opera
WHERE cod_galerie < 50
GROUP BY ROLLUP(cod_galerie, cod_artist);
Instrucţiunea precedentă va avea un rezultat de forma:
COD_GALERIE COD_ARTIST SUM(VALOARE)
10 50 14000
10 60 10000
10 24000
40 50 8080
40 8080
32080
Funcţia GROUPING
Funcţia GROUPING poate fi utilizată alături de operatorii CUBE şi
ROLLUP pentru a arăta modul cum a fost obţinută o valoare totalizatoare. Această
funcţie acceptă un singur argument, care trebuie să fie una dintre expresiile
specificate în clauza GROUP BY.
O valoare null a expresiei din argumentul funcţiei GROUPING poate
proveni din tabelul de bază (valoare null stocată) sau poate fi o valoare null creată
de operaţia ROLLUP sau CUBE ca rezultat al unei funcţii grup asupra expresiei
respective.
Funcţia GROUPING returnează valoarea 0 sau 1. Valoarea 0 returnată de
funcţia GROUPING pe baza unei expresii poate indica fie că expresia a fost
utilizată pentru calculul valorii agregat, fie că valoarea null a expresiei este o
valoare null stocată. Valoarea 1 returnată de funcţia GROUPING pe baza unei
expresii poate indica fie că expresia nu a fost utilizată pentru calculul valorii
agregat, fie că valoarea null a expresiei este creată de ROLLUP sau CUBE ca
rezultat al grupării.
Valorile returnate de funcţia GROUPING sunt utile pentru:
determinarea nivelului de agregare al unui subtotal dat, adică a grupului
sau grupurilor pe care se bazează subtotalul respectiv;
identificarea provenienţei unei valori null a unei expresii calculate, dintr-
una din liniile mulţimii rezultat.
Exemplu:
SELECT cod_galerie, cod_artist, SUM(valoare),
GROUPING(cod_galerie), GROUPING(cod_artist)
FROM opera
WHERE cod_galerie < 50
GROUP BY ROLLUP(cod_galerie, cod_artist);
SUM GROUPING GROUPING
COD_GALERIE COD_ARTIST
(VALOARE) (COD_GALERIE) (COD_ARTIST)
10 50 14000 0 0
10 60 10000 0 0
10 24000 0 1
40 50 8080 0 0
40 8080 0 1
32080 1 1
Pe prima linie din acest rezultat, valoarea totalizatoare reprezintă suma
valorilor operelor artistului având codul 50, în cadrul galeriei 10. Pentru a calcula
această valoare au fost luate în considerare coloanele cod_galerie şi cod_artist.
Prin urmare, expresiile GROUPING(cod_galerie) şi GROUPING(cod_artist) au
valoarea 0 pentru prima linie din rezultat.
Pe linia a treia se află valoarea totală a operelor din galeria având codul 10.
Această valoare a fost calculată luând în considerare doar coloana cod_galerie,
astfel încât GROUPING (cod_galerie) şi GROUPING(cod_artist) au valorile 0,
respectiv 1.
Pe ultima linie din rezultat se află valoarea totală a operelor din galeriile
având codul mai mic decât 50. Nici una dintre coloanele cod_galerie şi cod_artist
nu au intervenit în calculul acestui total, prin urmare valorile corespunzătoare
expresiilor GROUPING(cod_galerie) şi GROUPING(cod_artist) sunt 0.
Clauza GROUPING SETS
GROUPING SETS reprezintă o extensie a clauzei GROUP BY care permite
specificarea unor grupări multiple de date. Utilizarea acestei opţiuni facilitează
analiza datelor în mai multe dimensiuni.
Această extensie, apărută în sistemul Oracle9i, permite scrierea unei singure
instrucţiuni SELECT pentru a specifica grupări diferite (care pot conţine operatorii
ROLLUP şi CUBE), în loc de mai multe instrucţiuni SELECT combinate prin
operatorul UNION ALL. De altfel, reuniunea rezultatelor mai multor cereri este
ineficientă întrucât necesită mai multe parcurgeri ale aceloraşi date.
Operatorii ROLLUP şi CUBE pot fi consideraţi cazuri particulare de mulţimi
de grupări. Au loc următoarele echivalenţe:
CUBE(a, b, c) GROUPING SETS
((a, b, c), (a, b), (a, c), (b, c), (a),
(b), (c), ())
ROLLUP(a, b, GROUPING SETS
c) ((a, b, c), (a, b), (a), ())
Exemplu:
Considerând galeriile al căror cod este mai mic decât 50, să se calculeze
media valorilor operelor:
pentru fiecare galerie şi, în cadrul acesteia, pentru fiecare artist;
pentru fiecare artist şi, în cadrul acestuia, pentru anii de achiziţie
corespunzători.
SELECT cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy') "an achizitie",
AVG(valoare) "Valoare medie"
FROM opera
WHERE cod_galerie < 50
GROUP BY GROUPING SETS
((cod_galerie, cod_artist),
(cod_artist, TO_CHAR(data_achizitiei, 'yyyy')));
Mulţimea rezultat este constituită din valorile medii pentru fiecare dintre
cele două grupuri şi are forma următoare:
COD_GALERIE COD_ARTIST an a Valoare medie
10 50 3500
10 60 2500
40 50 2020
50 2000 2380
50 2002 2300
60 2001 2000
60 2003 3000
Coloane compuse
O coloană compusă este o colecţie de coloane care sunt tratate unitar în
timpul calculelor asupra grupurilor. Pentru a specifica o coloană compusă, aceasta
se include între paranteze. În operaţia ROLLUP(a, (b, c), d), coloanele b şi c
formează o coloană compusă şi sunt tratate unitar.
În general, coloanele compuse sunt utile pentru operaţiile ROLLUP, CUBE
şi GROUPING SETS. De exemplu, în CUBE sau ROLLUP coloanele compuse pot
determina eliminarea agregării de pe anumite niveluri.
Clauza GROUP BY ROLLUP(a, (b, c)) este echivalentă cu următoarea
instrucţiune compusă (în care se precizează doar forma clauzelor GROUP BY):
GROUP BY a, b, c UNION ALL
GROUP BY a UNION ALL
GROUP BY ( )
Astfel, (b, c) sunt tratate unitar şi operaţia ROLLUP nu va fi efectuată asupra
grupurilor în care coloanele b şi c nu apar simultan. Acest lucru este similar
situaţiei în care este definit un alias x pentru (b, c), iar specificaţia clauzei GROUP
BY este GROUP BY ROLLUP(a, x).
În instrucţiunea precedentă, GROUP BY () reprezintă instrucţiunea SELECT
cu valori null pentru coloanele a şi x. Această clauză este folosită pentru generarea
totalurilor generale:
SELECT null, null, coloană_agregat
FROM nume_tabel
GROUP BY ();
Următorul tabel prezintă câteva specificaţii care utilizează operatorii
ROLLUP, CUBE, GROUPING SETS, împreună cu instrucţiunile compuse
echivalente acestora:
GROUP BY ROLLUP(a, b, c) GROUP BY a, b, c UNION
ALL
GROUP BY a, b UNION
ALL
GROUP BY a
GROUP BY CUBE( (a, b), c) GROUP BY a, b, c UNION
ALL
GROUP BY a, b UNION
ALL
GROUP BY c UNION ALL
GROUP BY()
GROUP BY GROUPING SETS(a, b, GROUP BY a UNION ALL
c) GROUP BY b UNION ALL
GROUP BY c
GROUP BY GROUPING SETS GROUP BY a UNION ALL
(a, b, (b, c) ) GROUP BY b UNION ALL
GROUP BY b, c
GROUP BY GROUPING SETS( (a, b, GROUP BY a, b, c
c) )
GROUP BY GROUPING SETS(a, GROUP BY a UNION ALL
(b), ()) GROUP BY b UNION ALL
GROUP BY ()
GROUP BY GROUPING SETS (a, GROUP BY a UNION ALL
ROLLUP(b, c)) GROUP BY ROLLUP(b, c)
Exemplu:
Să se afişeze următoarele informaţii:
valoarea medie a operelor de artă din fiecare galerie;
valoarea medie a operelor de artă pentru fiecare galerie, iar în cadrul
acesteia pentru fiecare artist şi fiecare an de achiziţie;
media generală a tuturor valorilor operelor de artă.
SELECT cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy') "an achizitie",
AVG(valoare) "Valoare medie"
FROM opera
GROUP BY ROLLUP
(cod_galerie,
(cod_artist, TO_CHAR(data_achizitiei, 'yyyy')));
Exemplul precedent poate fi rezolvat utilizând cererea compusă prezentată
mai jos. Folosirea coloanelor compuse este recomandată pentru asigurarea unei
execuţii eficiente.
SELECT cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy'),
AVG(valoare) "Valoare medie"
FROM opera
GROUP BY cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy')
UNION ALL
SELECT cod_galerie, TO_NUMBER(null), TO_CHAR(null),
AVG(valoare) "Valoare medie"
FROM opera
GROUP BY cod_galerie
UNION ALL
SELECT TO_NUMBER(null), TO_NUMBER(null), TO_CHAR(null),
AVG(valoare) "Valoare medie"
FROM opera
GROUP BY ();
Concatenarea grupărilor
Concatenarea grupărilor reprezintă o modalitate concisă de a genera
combinaţii de grupări. Acestea se specifică prin enumerarea mulţimilor de grupări
(grouping sets) şi a operaţiilor ROLLUP, CUBE separate prin virgulă. De exemplu,
expresia GROUP BY GROUPING SETS(a, b), GROUPING SETS(c, d) defineşte
grupările (a, c), (a, d), (b, c), (b, d).
Concatenarea mulţimilor de grupări este utilă atât pentru uşurinţa dezvoltării
cererilor, cât şi pentru aplicaţii. Codul SQL generat de aplicaţiile OLAP implică
deseori concatenarea mulţimilor de grupări, în care fiecare astfel de mulţime
defineşte grupările necesare pentru o dimensiune.
Exemplu:
Să se determine media valorilor operelor luând în considerare următoarele
grupări: (cod_galerie, cod_artist, an_achizitie), (cod_galerie, cod_artist),
(cod_galerie, an_achizitie), (cod_galerie).
SELECT cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy') an_achizitie,
AVG(valoare)
FROM opera
GROUP BY cod_galerie, ROLLUP(cod_artist),
CUBE(TO_CHAR(data_achizitiei, 'yyyy'));
Funcţii analitice
Funcţiile analitice calculează o valoare agregat pe baza unui grup de
înregistrări. Ele diferă de funcţiile agregat prin faptul că, pentru fiecare grup, pot fi
returnate mai multe linii rezultat.
Aceste funcţii reprezintă ultimul set de operaţii efectuat la procesarea unei
interogări, înaintea clauzei ORDER BY. Din acest motiv, o funcţie analitică poate
apărea numai în lista SELECT sau în clauza ORDER BY.
Exemplu:
Pentru fiecare operă de artă, să se afle numărul de creaţii ale căror valori
sunt cu cel mult 1000 mai mici şi cu cel mult 2000 mai mari decât valoarea operei
respective.
SELECT titlu, valoare,
COUNT(*) OVER (ORDER BY valoare
RANGE BETWEEN 1000 PRECEDING
AND 2000 FOLLOWING) AS nr_dom
FROM opera;
Cuvântul cheie OVER indică faptul că funcţia operează pe mulţimea de
rezultate a cererii, adică după evaluarea celorlalte clauze. Opţiunea RANGE
defineşte, pentru fiecare linie, o „fereastră“ (o mulţime de linii). Funcţia analitică
va fi aplicată tuturor liniilor din această mulţime.
Curs6_IP
Subcereri
De cele mai multe ori, pentru a implementa anumite interogări, nu este
suficientă o singură cerere SELECT ci sunt necesare subcereri. Subcererile sunt
comenzi SELECT încapsulate în oricare din clauzele SELECT, WHERE, HAVING,
FROM.
Dacă subcererea urmează clauzei WHERE sau HAVING, ea poate conţine
unul dintre operatorii ALL, ANY, IN (=ANY), EXIST, NOT IN (!=ALL) care sunt
specifici cererilor care întorc mai multe linii (multiple-row subquery) sau unul
dintre operatorii de comparare (=, <, >, >=, <=, <>) care sunt specifici cererilor
care întorc o singură linie (single-row subquery).
Subcererile trebuie incluse între paranteze şi trebuie plasate în partea dreaptă
a operatorului de comparare. Subcererea nu poate conţine clauza ORDER BY.
Exemplu:
Să se obţină numele şi salariul angajaţilor, având salariul minim.
SELECT ename, sal
FROM emp
WHERE sal=(SELECT MIN(sal)
FROM emp);
Exemplu:
Să se obţină job-ul pentru care salariul mediu este minim. Sa se afiseze si
salariul mediu.
SELECT job, AVG(sal)
FROM emp
GROUP BY job
HAVING AVG(sal)=(SELECT MIN(AVG(sal))
FROM emp
GROUP BY job);
Operatorul ANY presupune că este adevărată condiţia dacă comparaţia este
adevărată pentru cel puţin una din valorile returnate. Sunt evidente relaţiile:
< ANY mai mic ca maximul;
> ANY mai mare ca minimul;
= ANY IN.
Pentru operatorul ALL se presupune că este adevărată condiţia, dacă
comparaţia este adevărată pentru toate elementele listei returnate. Pentru operatorul
ALL sunt evidente relaţiile:
< ALL mai mic ca minimul;
> ALL mai mare ca maximul;
! = ALL NOT IN.
Exemplu:
WHERE codec > ALL („C1‟, „C2‟) este superior tuturor elementelor din listă;
WHERE codec > ANY („C1‟, „C2‟) este superior cel puţin unui element din
listă.
Exemplu:
Să se obţină salariaţii al căror salariu este mai mare ca salariile medii din
toate departamentele.
Dacă una din valorile returnate de subcerere este valoarea null atunci cererea
nu întoarce nici o linie. Prin urmare, dacă valoarea null poate să facă parte din
rezultatul subcererii nu trebuie utilizat operatorul NOT IN. Problema nu mai apare
dacă se utilizează operatorul IN.
Exemplu:
Să se obţină salariaţii care nu au subordonaţi.
SELECT e.ename
FROM emp e
WHERE e.empno NOT IN (SELECT m.mgr
FROM emp m);
În acest caz, instrucţiunea SQL nu întoarce nici o linie deoarece una din
valorile furnizate de subcerere este valoarea null.
Exemplu:
Să se obţină numele salariaţilor, salariile, codul departamentului în care
lucrează şi salariul mediu pe departament pentru toţi angajaţii care au salariul mai
mare ca media salariilor din departamentul în care lucrează (folosirea subcererii în
clauza FROM).
SELECT a.ename,a.sal,a.deptno,b.salavg
FROM emp a,(SELECT deptno,avg(sal) salavg
FROM emp
GROUP BY deptno) b
WHERE a.deptno=b.deptno
AND a.sal>b.salavg
Exemplu:
Să se obţină lista celor mai scumpe cărţi.
SELECT titlu
FROM carte
WHERE pret = (SELECT MAX(pret)
FROM carte);
Exemplu:
Să se obţină lista scriitorilor care au în bibliotecă un număr de exemplare
mai mare decât numărul mediu al cărţilor din bibliotecă.
SELECT DISTINCT autor
FROM carte
WHERE nrex > (SELECT AVG(nrex)
FROM carte);
Exemplu:
Să se obţină informaţii despre cărţile al căror preţ depăşeşte media preţurilor
cărţilor ce aparţin aceluiaşi domeniu
SELECT *
FROM carte c
WHERE pret > (SELECT AVG(pret)
FROM carte
WHERE coded = c.coded);
Exemplu:
Să se obţină lista cititorilor care au împrumutat cel puţin o carte.
SELECT nume
FROM cititor
WHERE codec IN (SELECT DISTINCT codec
FROM imprumuta);
Exemplu:
Să se obţină codurile cititorilor care nu au împrumutat niciodată cărţi.
SELECT codec
FROM cititor
WHERE codec NOT IN
(SELECT DISTINCT codec
FROM imprumuta);
Exemplu:
Să se obţină lista cititorilor care sunt în întârziere cu predarea cărţilor.
SELECT nume
FROM cititor
WHERE codec IN (SELECT DISTINCT codec
FROM imprumuta
WHERE dataef IS NULL
AND dares<SYSDATE);
Exemplu:
Să se obţină numele cititorilor care au împrumutat cel puţin o carte scrisă de
ZOLA.
SELECT nume
FROM cititor
WHERE codec IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE autor=‟ZOLA‟));
Exemplu:
Să se obţină numele cititorilor care nu au împrumutat nici o carte scrisă de
ZOLA.
SELECT nume
FROM cititor
WHERE codec NOT IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE autor=‟ZOLA‟));
Operatorul IN poate fi înlocuit cu = ANY (un element este în listă dacă şi
numai dacă este egal cu un element al listei), iar operatorul NOT IN poate fi
înlocuit prin !=ALL.
Exemplu:
Să se obţină codurile cititorilor care au împrumutat o carte de algebră.
SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE coded=
(SELECT coded
FROM domeniu
WHERE intdom=‟ALGEBRA‟));
Exemplu:
Să se obţină cititorii care au împrumutat numai cărţi scrise de „ZOLA‟.
SELECT nume
FROM cititor
WHERE codec NOT IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel NOT IN
(SELECT codel
FROM carte
WHERE autor=‟ZOLA‟));
Exemplu:
Să se obţină numele cititorilor care au împrumutat cel puţin o carte de
informatică (procedural).
SELECT nume
FROM cititor
WHERE codec IN
(SELECT DISTINCT codec
FROM imprumuta
WHERE codel IN
(SELECT codel
FROM carte
WHERE coded=
(SELECT coded
FROM domeniu
WHERE intdom= ‟INFORMATICA‟)));
Exemplu:
Să se obţină numele cititorilor şi titlurile cărţilor de informatică împrumutate
de aceşti cititori (relational).
SELECT nume, titlu
FROM cititor, carte, imprumuta, domeniu
WHERE imprumuta.codel = carte.codel
AND carte.coded = domeniu.coded
AND imprumuta.codec = cititor.codec
AND intdom = ‟INFORMATICA‟;
Subcererile pot fi executate corelat (cu sincronizare) sau încuibărit (fără
sincronizare).
Subcererile fără sincronizare sunt caracterizate de faptul că se execută
cererea cea mai interioară care întoarce un rezultat ce este transmis cererii de nivel
superior, care întoarce un rezultat s.a.m.d.
Subcererile cu sincronizare sunt caracterizate de faptul că evaluarea
subcererii face referinţă la o coloană a cererii principale, iar evaluarea cererii
interioare se face pentru fiecare linie a cererii (principale) care o conţine.
Exemplu:
Să se obţină, utilizând sincronizarea subcererii cu cererea principală, titlurile
cărţilor care au toate exemplarele împrumutate (se selectează un titlu din carte şi
pentru acest titlu se numără câte exemplare sunt împrumutate).
SELECT titlu
FROM carte
WHERE nrex=(SELECT COUNT(*)
FROM imprumuta
WHERE codel = carte.codel
AND dataef IS NULL);
Exemplu:
Să se obţină codurile cititorilor şi codul ultimei cărţi împrumutate.
SELECT codec, codel
FROM imprumuta i
WHERE dataim>=ALL (SELECT dataim
FROM imprumuta
WHERE codec=i.codec);
Pentru această interogare, clauza WHERE putea fi scrisă şi sub forma:
WHERE dataim=(SELECT MAX(dataim)
FROM imprumuta
WHERE codec=i.codec);
Exemplu:
Să se obţină lista codurilor cărţilor împrumutate şi codul primului cititor care
a împrumutat aceste cărti.
SELECT codel,codec
FROM imprumuta i
WHERE dataim<=ALL (SELECT dataim
FROM imprumuta
WHERE i.codel=codel);
Exemplu:
Să se obţină codurile cărţilor din care cel puţin un exemplar este împrumutat.
SELECT codel
FROM carte
WHERE EXISTS
(SELECT codel
FROM imprumuta
WHERE codel = carte.codel
AND dataef IS NULL);
Operatorul WHERE EXISTS (subcerere) presupune că predicatul este
adevărat dacă subcererea întoarce cel puţin un tuplu, iar WHERE NOT EXISTS
(subcerere) presupune că predicatul este adevărat dacă subcererea nu întoarce nici
un tuplu. EXISTS şi NOT EXISTS cer sincronizarea subcererii.
Exemplu:
Să se obţină titlurile cărţilor care sunt momentan împrumutate.
Soluţia 1 (cu sincronizare):
SELECT titlu
FROM carte
WHERE EXISTS
(SELECT *
FROM imprumuta
WHERE codel = carte.codel
AND dataef IS NULL);
Soluţia 2 (fără sincronizare):
SELECT titlu
FROM carte
WHERE codel IN
(SELECT DISTINCT codel
FROM imprumuta
WHERE dataef IS NULL);
Exemplu:
Să se obţină codurile cărţilor care nu au fost împrumutate niciodată.
Soluţia 1 (cu sincronizare)
SELECT codel
FROM carte
WHERE NOT EXISTS
(SELECT codel
FROM imprumuta
WHERE codel = carte.codel);
Soluţia 2 (fără sincronizare)
SELECT codel
FROM carte
WHERE codel NOT IN
(SELECT DISTINCT codel
FROM imprumuta);
Exemplu:
Să se obţină lista salariaţilor având salariul minim în departamentul în care
lucrează.
SELECT ename,sal
FROM emp e
WHERE sal=(SELECT MIN(sal)
FROM emp
WHERE deptno=e.deptno);
Exemplu:
Să se obţină numele primilor trei salariaţi având retribuţia maximă (ideea
rezolvării este de a verifica dacă numărul salariaţilor care au leafa mai mare decât
leafa salariatului considerat, este mai mic decât 3).
SELECT ename
FROM emp a
WHERE 3>(SELECT COUNT(*)
FROM emp
WHERE sal > a.sal);
Exemplu:
Să se obţină numele cititorilor care au împrumutat cel puţin aceleaşi cărţi ca
şi cititorul având codul C19 (ideea problemei este de a selecta cititorii pentru care
este vidă lista cărţilor împrumutatede C19 mai puţin lista cărţilor împrumutate de
acei cititori).
SELECT nume
FROM cititor
WHERE NOT EXISTS
(SELECT codel
FROM imprumuta
WHERE codec=‟C19‟
MINUS
SELECT codel
FROM imprumuta
WHERE codec= cititor.codec);
Dacă problema era modificată în sensul că „cel puţin”este înlocuit prin „cel
mult” atunci trebuiau inversate interogările legate prin MINUS.
Exemplu:
Să se obţină codurile cititorilor care au împrumutat aceleaşi cărţi ca şi
cititorul având un cod specificat.
Rezolvarea problemei se bazează pe ideea: A = B A B şi B A (A-
B) = şi (B-A) = A-B şi B-A nu furnizează nici un tuplu rezultat.
SELECT codec
FROM imprumuta i
WHERE NOT EXISTS
(SELECT codel
FROM imprumuta
WHERE codec=i.codec
MINUS
SELECT codel
FROM imprumuta
WHERE codec=‟&ccc‟)
AND NOT EXISTS
(SELECT codel
FROM imprumuta
WHERE codec=‟&ccc‟
MINUS
SELECT codel
FROM imprumuta
WHERE codec=i.codec)
AND codec!=‟&ccc‟);
Ultimul operator (AND), asigură să nu apară în rezultat cititorul specificat.
În cazul formei relaţionale de rezolvare a cererii, drumul de acces la
informaţie este în sarcina SGBD-lui şi prin urmare nu mai apar cereri imbricate.
Exemplu:
Să se obţină numele cititorilor care au împrumutat cel puţin o carte.
Soluţia 1 (forma relaţională):
SELECT DISTINCT nume
FROM cititor,imprumuta
WHERE cititor.codec=imprumuta.codec;
Soluţia 2 (forma procedurală):
SELECT nume
FROM cititor
WHERE codec IN
(SELECT DISTINCT codec
FROM imprumuta);
Exemplu:
Să se obţină numele cititorilor care au împrumutat cel puţin două cărţi.
Soluţia 1 (forma relaţională):
SELECT nume
FROM cititor, imprumuta
WHERE cititor.codec=imprumuta.codec
GROUP BY nume
HAVING COUNT(*)>1;
Soluţia 2 (forma procedurală):
SELECT nume
FROM cititor
WHERE codec IN
(SELECT codec
FROM imprumuta
GROUP BY codec
HAVING COUNT(*)>1);
Exemplu:
Să se afişeze numele, prenumele, salariul lucrătorilor, codurile publicaţiilor
la care lucrează şi salariul mediu pe publicaţie pentru toţi angajaţii care au salariul
mai mare decât media salariului pe publicaţia respectivă.
SELECT s.nume, s.prenume, s.salariu,
p.nr_publicatie, a.salariu_mediu
FROM salariat s, publicatie p,
(SELECT p1.nr_publicatie,AVG(salariu) salariu_mediu
FROM publicatie p1, salariat s1
WHERE p1.cod_salariat = s1.cod_salariat
GROUP BY p1.nr_publicatie) a
WHERE p.nr_publicatie = a.nr_publicatie
AND s.cod_salariat = p.cod_salariat
AND s.salariu > a.salariu_mediu;
Exemplu:
Să se obţină numele salariaţilor care nu cunosc nici o limbă străină.
SELECT nume, prenume
FROM salariat
WHERE NOT EXISTS
(SELECT *
FROM limba
WHERE limba.cod_salariat = salariat.cod_salariat
AND limba_cun IS NOT NULL);
Exemplu:
Să se afişeze graficienii care au întârziat să predea frame-urile.
a) cu sincronizare:
SELECT nume, prenume
FROM salariat
WHERE EXISTS
(SELECT *
FROM realizeaza r
WHERE salariat.cod_salariat=r.cod_salariat
AND data_lim < SYSDATE);
b) fără sincronizare:
SELECT nume, prenume
FROM salariat
WHERE cod_salariat IN
(SELECT DISTINCT cod_salariat
FROM realizeaza
WHERE data_lim < SYSDATE);
Exemplu:
Să se determine revistele coordonate de redactori şefi care nu cunosc limba
în care sunt scrise. Se ştie că în urma inspectării vizuale a rezultatului interogării se
poate decide schimbarea redactorilor şefi ai revistelor respective, de aceea se
doreşte blocarea înregistrărilor găsite.
SELECT p.nr_publicatie
FROM salariat s, publicatie p
WHERE s.cod_salariat = p.cod_salariat
AND p.limba NOT IN
(SELECT limba_cun
FROM limba
WHERE limba.cod_salariat = s.cod_salariat)
FOR UPDATE OF p.cod_salariat;
Clauza WITH
Cu ajutorul clauzei WITH se poate defini un bloc de cerere înainte ca acesta
să fie utilizat într-o interogare. Clauza permite reutilizarea aceluiaşi bloc de cerere
într-o instrucţiune SELECT complexă.
Utilizând clauza WITH, să se scrie o cerere care afişează numele artiştilor şi
valoarea totală a operelor acestora. Se vor considera artiştii a căror valoare totală a
operelor este mai mare decât media valorilor operelor tuturor artiştilor.
WITH
val_artist AS (SELECT nume, SUM(valoare) AS total
FROM opera o, artist a
WHERE o.cod_artist = a.cod_artist
GROUP BY nume),
val_medie AS (SELECT SUM(total)/COUNT(*) AS medie
FROM val_artist)
SELECT *
FROM val_artist
WHERE total > (SELECT medie
FROM val_medie)
ORDER BY nume;
Subcereri scalare
Subcererile scalare în SQL returnează valoarea unei singure coloane
corespunzătoare unei linii. Dacă subcererea returnează 0 linii, valoarea subcererii
scalare este null. Dacă subcererea returnează mai mult de o linie, server-ul
generează o eroare.
Subcererile scalare erau acceptate în Oracle8i doar în anumite cazuri, cum ar
fi clauzele FROM şi WHERE ale instrucţiunii SELECT sau clauza VALUES a
instrucţiunii INSERT. Utilitatea subcererilor scalare a fost extinsă în Oracle9i.
Astfel, ele pot apărea în:
condiţiile şi expresiile care fac parte din DECODE sau CASE;
toate clauzele instrucţiunii SELECT, cu excepţia lui GROUP BY;
în partea stângă a operatorului, în clauzele SET şi WHERE ale
instrucţiunii UPDATE.
Exemplu:
Să se afişeze codul, titlul operelor şi numele artistului doar dacă acesta este
Brâncuşi. În caz contrar, se va afişa şirul „alt artist“.
SELECT cod_opera, titlu,
(CASE WHEN cod_artist =
(SELECT cod_artist
FROM artist
WHERE nume = 'Brancusi')
THEN 'Brancusi'
ELSE 'Alt artist' END) artist
FROM opera;
Operatorul ROLLUP
Operatorul ROLLUP produce o mulţime care conţine liniile obţinute în urma
grupării obişnuite şi linii pentru subtotaluri. Acest operator furnizează valori
agregat şi superagregat corespunzătoare expresiilor din clauza GROUP BY.
Operatorul ROLLUP creează grupări prin deplasarea într-o singură direcţie,
de la dreapta la stânga, de-a lungul listei de coloane specificate în clauza GROUP
BY. Apoi, se aplică funcţia agregat acestor grupări. Dacă sunt specificate n expresii
în operatorul ROLLUP, numărul de grupări generate va fi n + 1. Liniile care se
bazează pe valoarea primelor n expresii se numesc linii obişnuite, iar celelalte se
numesc linii superagregat.
Daca in clauza GROUP BY sunt specificate n coloane, atunci pentru a
produce subtotaluri in n dimensiuni ar fi necesare n+1 operatii SELECT legate prin
UNION ALL. Aceasta ar fi total ineficient, deoarece fiecare SELECT ar implica o
parcurgere a tabelului. Operatorul ROLLUP are nevoie de o singura parcurgere a
tabelului.
Exemplu:
Să se afişeze codurile de galerii mai mici decât 50, iar pentru fiecare dintre
acestea şi pentru fiecare autor care are opere expuse în galerie, să se listeze
valoarea totală a lucrărilor sale. De asemenea, se cere valoarea totală a operelor
expuse în fiecare galerie. Rezultatul va conţine şi valoarea totală a operelor din
galeriile având codul mai mic decât 50, indiferent de codul autorului.
SELECT cod_galerie, cod_artist, SUM(valoare)
FROM opera
WHERE cod_galerie < 50
GROUP BY ROLLUP(cod_galerie, cod_artist);
Instrucţiunea precedentă va avea un rezultat de forma:
COD_GALERIE COD_ARTIST SUM(VALOARE)
10 50 14000
10 60 10000
10 24000
40 50 8080
40 8080
32080
Operatorul CUBE
Operatorul CUBE grupează liniile selectate pe baza valorilor tuturor
combinaţiilor posibile ale expresiilor specificate şi returnează câte o linie
totalizatoare pentru fiecare grup. El produce subtotaluri pentru toate combinaţiile
posibile de grupări specificate în GROUP BY, precum şi un total general.
Daca exista n coloane sau expresii in clauza GROUP BY, vor exista 2 n
combinatii posibile superagregat. Matematic, aceste combinatii formeaza un cub
n-dimensional.
Pentru producerea de subtotaluri fara ajutorul operatorului CUBE ar fi
necesare 2 n instructiuni SELECT legate prin UNION ALL.
Exemplu:
Să se afişeze valoarea totală a operelor de artă ale unui autor, expuse în
cadrul fiecărei galerii având codul mai mic decât 50. De asemenea, să se afişeze
valoarea totală a operelor din fiecare galerie având codul mai mic decât 50,
valoarea totală a operelor fiecărui autor indiferent de galerie şi valoarea totală a
operelor din galeriile având codul mai mic decât 50.
SELECT cod_galerie, cod_artist, SUM(valoare)
FROM opera
WHERE cod_galerie < 50
GROUP BY CUBE(cod_galerie, cod_artist);
Funcţia GROUPING
Aceasta funcţie este utilă pentru:
determinarea nivelului de agregare al unui subtotal dat, adică a grupului
sau grupurilor pe care se bazează subtotalul respectiv;
identificarea provenienţei unei valori null a unei expresii calculate, dintr-
una din liniile mulţimii rezultat.
Functia returnează valoarea 0 sau 1. Valoarea 0 poate indica fie că expresia a
fost utilizată pentru calculul valorii agregat, fie că valoarea null a expresiei este o
valoare null stocată.
Valoarea 1 poate indica fie că expresia nu a fost utilizată pentru calculul
valorii agregat, fie că valoarea null a expresiei este o valoare creată de ROLLUP
sau CUBE ca rezultat al grupării.
Exemplu:
SELECT cod_galerie, cod_artist, SUM(valoare),
GROUPING(cod_galerie), GROUPING(cod_artist)
FROM opera
WHERE cod_galerie < 50
GROUP BY ROLLUP(cod_galerie, cod_artist);
Exemplu:
Considerând galeriile al căror cod este mai mic decât 50, să se calculeze
media valorilor operelor:
pentru fiecare galerie şi, în cadrul acesteia, pentru fiecare artist;
pentru fiecare artist şi, în cadrul acestuia, pentru anii de achiziţie
corespunzători.
SELECT cod_galerie, cod_artist,
TO_CHAR(data_achizitiei, 'yyyy') "an achizitie",
AVG(valoare) "Valoare medie"
FROM opera WHERE cod_galerie < 50
GROUP BY GROUPING SETS
((cod_galerie, cod_artist),
(cod_artist, TO_CHAR(data_achizitiei, 'yyyy')));
Mulţimea rezultat este constituită din valorile medii pentru fiecare dintre
cele două grupuri ((cod_galerie, cod_artist) si (cod_artist, an_achizitie)) şi are
forma următoare:
COD_GALERIE COD_ARTIST An achizitie Valoare medie
10 50 3500
10 60 2500
40 50 2020
50 2000 2380
50 2002 2300
60 2001 2000
60 2003 3000
Atunci când inseraţi un nou rând într-un tabel, cheia primară a noului rând
trebuie sa fie unică în întregul tabel.Cand modificati valoarea unei chei primare
(ceea ce se intampla rareori), noua valoare trebuie să fie unică în întregul tabel.
Restricţii de unicitate
O valoare nula (null) este o modalitate specială prin care sistemul SGBD
tratează valoarea unei coloane pentru a indica faptul ca valoarea coloanei
respective nu este cunoscută. O valoare nula nu este acelasi lucru un un spatiu
liber, un sir vid sau valoarea zero – este o valoare speciala care nu este egala cu
nimic altceva.
În cazul instrucţiunilor INSERT, trebuie specificate valori pentru toate
coloanele cu restricţii NOT NULL.
În cazul instrucţiunilor UPDATE nu se pot inlocui valorile unei coloane cu
valori nule dacă pe coloana respectivă este definită p restricţie NOT NULL.
Dacă instrucţiunea DML referă o vizualizare , nu se poate sa o folosim intr-
o instrucţiune INSERT dacă una dintre coloanele tabelului cu o restricţie NOT
NULL(obligatorii) lipseşte din definirea vizualizării.
Restricţii referenţiale
Nu se poate insera sau actualiza valoarea unei chei externe decât dacă există
deja rândul părinte corespondent care conţine valoarea cheii în coloana cheii
primare. In sens invers, nu se poate şterge un rând părinte dacă există rânduri
subordonate care referă valoarea din rândul părinte, decât dacă restricţia a fost
definită cu opţiunea ON DELETE CASCADE. In general inserările în tabele
trebuie făcute ierarhic (mai intai randurile parinte, apoi randurile copii), iar
ştergerile trebuie făcute în ordine inversă (copiii inaintea părinţilor).
Instrucţiunea INSERT
FILM
FILM_ID <pk>
MPAA_RATING FILM_COD_GEN <fk1> FILM_GEN
MPAA_COD_RATING <pk> MPAA_COD_RATING <fk2> FILM_COD_GEN <pk>
MPAA_DESCRIERE_RATING FILM_NUME FILM_DESCRIERE_GEN
RETAIL_PRET_VHS
RETAIL_PRET_DVD
AN_PRODUS
FILM_COPII
FILM_ID <pk, fk>>
NUMAR_COPIE <pk>
DATA_CUMPARARE
DATA_VANZARE
FORMAT_MEDIA
Se poate remarcă următoarele:
Lista de coloane este opţională, dar dacă este inclusă trebuie să fie
încadrată în paranteze rotunde.
Dacă lista de coloane este omisă, trebuie specificată o valoare pentru
fiecare coloană din tabel, în ordinea în care sunt definite coloanele în tabel. Este
bine ca întotdeauna să includeţi lista de coloane, deoarece omiterea acesteia face ca
instrucţiunea INSERT să fie dependentă de definiţia tabelului. Dacă o coloană este
modificată sau în tabel este adăugată o nouă coloană, chiar şi opţională, probabil
instrucţiunea INSERT va eşua la următoarea rulare.
Dacă lista de coloane este specificată, lista de valori trebuie să conţin
o valoare pentru fiecare coloană din listă, în aceeaşi ordine. Cu alte cuvinte, între
lista de coloane şi lista de valori trbuie să existe o corespondenţa unu-la-unu. Orice
coloana care lipseşte din listă va primi o valoare nulă, presupunund că valorile nule
sunt acceptate în coloana respectivă.
În clauza VALUES, valorile de tip caracter şi dată calendaristică
trebuie incluse între apostrofuri. Nu se recomandă includerea între apostrofuri a
valorilor numerice, întrucât aceasta ar determina conversii implicite la tipul
NUMBER.
Pentru introducerea de valori speciale în tabel, pot fi utilizate funcţii.
Adăugarea unei linii care va conţine valori null se poate realiza în
mod:
- implicit, prin omiterea numelui coloanei din lista de coloane;
- explicit, prin specificarea în lista de valori a cuvântului cheie null
sau a şirului vid în cazul şirurilor de caractere sau datelor
calendaristice.
1
cuprinsa_in SECURITATE
cod_opera#
sistem#
M(1) 1 protejata_prin M(0)
1 creata_de M(1)
ARTIST OPERA
cod_artist# cod_opera# 1 asigurata_de M(0)
POLITA_ASIG
M(1) cod_polita#
M(1) M(1)
mentionata_in
studiaza
M(0)
M(0)
SURSA_BIBLIO
cod_sursa# SPECIALIST
tip cod_specialist#
ARTICOL figureaza_in
CARTE M(0)
M(1)
AUTOR
cod_autor#
Să se adauge prin metoda explicită, respectiv prin metoda implicită, două
înregistrări în tabelul artist, precizând numai codul, numele şi prenumele artistului.
Exemplu:
Următoarea instrucţiune va genera eroarea „ORA-01400: cannot insert
NULL into …“, întrucât se încearcă inserarea unei linii în tabelul opera fără a
preciza valoarea cheii primare.
INSERT INTO opera
(titlu, data_achizitiei)
VALUES ('Flori de camp', SYSDATE);
Exemplu BD CARTE
Să se insereze în tabelul carte toate cărţile din tabelul carte_info,
presupunând că tabelul carte_info a fost deja creat. De asemenea, să se introducă o
nouă carte căreia i se cunoaşte codul (c34), titlul (algebra) şi preţul (500).
Exemplu:
INSERT INTO carte
(codel, nrex)
VALUES ('c25', 25);
inserare prin parametrizare
INSERT INTO domeniu
VALUES ('&cod', '&dendom'); inserare prin parametrizare
Aşa cum se observă, este nevoie de foarte mult cod pentru a insera în tabel
un singur rând de date folosind o instrucţiune INSERT cu clauza VALUES.
O altă soluţie, care poate fi folosită pentru a insera rânduri multiple într-un
tabel , este forma care foloseşte o instrucţiune SELECT internă. Această formă este
utilă şi pentru stabilirea următoarei valori disponibile pentru o cheie primară cu
valori secvenţiale. De asemenea , poate fi folosită atunci când creaţi un tabel
temporar pentru testare şi vreţi să-l populaţi cu toate datele dintr-un alt tabel.
Se remarcă următoarele:
Lista de coloane este opţională, dar dacă este inclusă trebuie să fie încadrată
în paranteze.
Dacă lista de coloane este omisă, instrucţiune SELECT internă trebuie să
furnizeze o valoare pentru fiecare coloană din tabel, în ordinea în care sunt definite
coloanele în tabel. Este bine ca întotdeauna să includeţi lista de coloane, deoarece
omiterea acesteia face ca instrucţiunea INSERT să fie dependentă de definiţia
tabelului. Dacă o coloană este modificată sau în tabel este adăugată o nouă
coloană, chiar şi opţională, probabil instrucţiunea INSERT va eşua la următoarea
rulare.
Dacă lista de coloane este specificată, instrucţiune SELECT internă să
furnizeze o valoare pentru fiecare coloană din lista de valori, în aceeaşi ordine. Cu
alte cuvinte, între lista de coloane şi setul de rezultate al instrucţiunii SELECT
trebuie să existe o corespondenţa unu-la-unu. Orice coloană care lipseşte din listă
va primi o valoare nulă, presupunund că valorile nule sunt acceptate în coloana
respectivă.
Cuvântul cheie NULL poate fi folosit în instrucţiunea SELECT pentru
specificarea unei valori nule pentru o coloană.
Ca exemplu, să presupunem că toate filmele sunt acum disponibile şi în
limba franceză. Tabelul FILM_LIMBA conţine rânduri pentru limba franceză
pentru o parte din filme, aşa că acum vrem să le adăugăm numai pe cele care
lipsesc. Instrucţiunea INSERT care va rezolva această problemă este:
Exemplu:
Presupunând că tabelul salariat a fost completat cu datele tuturor
salariaţilor editurii, să se completeze tabelele grafician, tehnoredactor şi
redactor_sef, în concordanţă cu datele conţinute în tabelul salariat (nu pot exista
graficieni, tehnoredactori sau redactori şefi care să nu fie salariaţi!).
INSERT INTO grafician (cod_salariat)
SELECT cod_salariat
FROM salariat
WHERE job = ‟grafician‟;
UPDATE nume_tabel_sau_vizualizare
SET nume_coloana = expresie
[,nume_coloana = expresie...]
[ WHERE conditie];
UPDATE nume_tabel_sau_vizualizare
SET (column1[,column2[,…]]) = (subquery) / column = expr /
(query)
[WHERE condition]
Observaţii:
Pentru a se putea executa instrucţiunea UPDATE, utilizatorul
care o lansează în execuţie trebuie să aibă acest privilegiu.
Dacă nu este specificată clauza WHERE se vor modifica toate
liniile.
Cererea trebuie să furnizeze un număr de valori corespunzător
numărului de coloane din paranteza care precede caracterul de egalitate.
Se remarcă următoarele:
Clauza SET conţine o listă cu una sau mai multe coloane,
împreună cu o expresie care specifică noua valoare pentru fiecare coloană.
Aceasta este o listă de perechi nume-valoare, separate prin virgule, cu un
operator de egalitate între fiecare nume şi valoare.
Expresia poate fi o constantă, un alt nume de coloană sau orice
altă expresie pe care SQL o poate transforma într-o singură valoare, care
poate fi apoi atribuită coloanei respective.
Clauza WHERE conţine o expresie care limitează rândurile
actualizate. Dacă această clauză este omisă, motorul SQL va încerca să
actualizeze toate rândurile din tabel sau din vizualizare.
Exemple:
Un film a fost adăugat cu un preţ incorect. Instrucţiunea
următoare va actualiza două coloane din acelaşI rând a tabelului MOVIE.
UPDATE FILM
SET RETAIL_PRET_VHS = 29.95 ,
RETAIL_PRET_DVD = 34.95
WHERE FILM_ID = 21
UPDATE ANGAJAT
SET ANGAJAT_PLATA_ORA = ROUND
(ANGAJAT_PLATA_ORA * 1.1, 2)
WHERE ANGAJAT_JOB_CATEGORY =‟C‟ ;
Exemple BD CARTE
Preţul cărţilor scrise de Lucian Blaga să fie modificat, astfel încât să fie egal
cu preţul celei mai scumpe cărţi de informatică din bibliotecă.
UPDATE carte
SET pret = (SELECT MAX(pret)
FROM carte
WHERE coded = ‟I‟)
WHERE autor = ‟Lucian Blaga‟;
Exemplu:
Să se modifice preţul cărţilor din bibliotecă, care se găsesc într-un număr de
exemplare mai mic decât media numărului de exemplare pe bibliotecă. Noua
valoare a preţului să fie egală cu suma preţurilor cărţilor scrise de Zola.
UPDATE carte
SET pret = (SELECT SUM(pret)
FROM carte
WHERE autor = ‟Zola‟)
WHERE nrex < (SELECT AVG(nrex)
FROM carte);
Exemplu:
Să se reducă cu 10% salariile redactorilor şefi care nu sunt asociaţi
nici unei publicaţii.
UPDATE salariat
SET salariu = 0,9*salariu
WHERE cod_salariat IN
(SELECT cod_salariat
FROM redactor_sef
WHERE cod_salariat NOT IN
(SELECT cod_salariat
FROM publicatie));
Exemplu:
Să se mărească cu 5% salariile redactorilor şefi ce coordoneaza publicaţiile
care au cel mai mare număr de frame-uri.
UPDATE salariat
SET salariu = 1,05*salariu
WHERE cod_salariat IN
(SELECT cod_salariat
FROM publicatie
WHERE nr_publicatie IN
(SELECT nr_publicatie
FROM frame
GROUP BY nr_publicatie
HAVING COUNT(*) > ALL
(SELECT COUNT(*)
FROM frame
GROUP BY nr_publicatie)));
** Oracle9i permite utilizarea valorii implicite DEFAULT in comenzile
INSERT si UPDATE. Unei coloane i se atribuie valoarea implicită definită la
crearea sau modificarea structurii tabelului dacă nu se precizează nici o valoare sau
dacă se precizează cuvântul cheie DEFAULT în comenzile INSERT sau UPDATE.
Dacă nu este definită nici o valoare implicită pentru coloana respectivă, sistemul îi
atribuie valoarea null.
Exemplu:
UPDATE carte
SET pret = DEFAULT
WHERE codel = 77;
Exemplu:
Să se transfere în galeria având codul 50 toate operele din galeria 60, care
sunt create de Nicolae Grigorescu. Să se mărească cu 10% valoarea acestor opere.
UPDATE opera o
SET cod_galerie = 50,
valoare = valoare * 1.10
WHERE (SELECT INITCAP(nume) ||' '||INITCAP(prenume)
FROM artist
WHERE cod_artist = o.cod_artist)= 'Nicolae Grigorescu'
AND o.cod_galerie = 60;
Exemplu:
Să se actualizeze operele al căror autor este Ştefan Luchian astfel: codul
galeriei devine codul galeriei în care este expusă cea mai scumpă operă a artistului,
valoarea fiecărei opere va fi mărită cu 10% din media valorilor operelor din
galerie, iar data achiziţiei va fi considerată data celei mai recente achiziţii din
galerie.
UPDATE opera o
SET cod_galerie = (SELECT cod_galerie
FROM artist a, opera b
WHERE a.cod_artist = b.cod_artist
AND INITCAP(nume) = 'Luchian '
AND INITCAP(prenume) = 'Stefan'
AND valoare =
(SELECT MAX(valoare)
FROM opera
WHERE cod_artist =
b.cod_artist)),
(valoare, data_achizitiei) =
(SELECT o.valoare + AVG(o2.valoare)*0.10,
MAX(o2.data_achizitiei)
FROM opera o2
WHERE o.cod_opera = o2.cod_opera)
WHERE cod_artist = (SELECT cod_artist
FROM artist
WHERE INITCAP(nume) = 'Luchian '
AND INITCAP(prenume) = 'Stefan');
Instrucţiunea DELETE şterge unul sau mai multe rânduri dintr-un tabel.
Instrucţiunea poate să folosească şi o vzualizare, dar numai dacă aceasta se bazează
pe un singur tabel (ceea ce înseamnă că instrucţiunile DELETE nu pot fi folosite
pentru vizualizări care conţin uniuni). În instrucţiunile DELETE nu sunt referite
niciodată coloane, doarece instrucţiunea şterge rânduri întregi de date, inclusiv
toate valorile datelor (toate coloanele) din rândurile afectate. Dacă vreţi să ştergeţi
o singură valoare din rândurile existente, folosiţi instrucţiunea UPDATE pentru a
înlocui valorile respective cu valori nule (presupunând că valorile nule sunt
permise în acele coloane).
Se remarcă următoarele:
Comanda DELETE nu şterge structura tabelului.
Exemple BD MOVIE
Exemplu:
Să se elimine cititorii care au numele „Popa‟şi cei care au restituit astăzi cel
puţin o carte.
Exemplu:
Să se elimine redactorii şefi care nu au coordonat nici o publicaţie.
DELETE FROM redactor_sef
WHERE cod_salariat NOT IN
(SELECT DISTINCT cod_salariat
FROM publicatie);
Exemplu:
Să se şteargă salariul angajatului având codul 1279.
UPDATE salariat
SET salariu=null
WHERE cod_salariat = 1279;
Exemplu:
Urmatoarele doua comenzi sunt echivalente.
DELETE FROM opera
WHERE cod_opera = 777;
Exemplu:
Să se şteargă cartea cea mai scumpă
DELETE FROM carte
WHERE pret = (SELECT MAX(pret)
FROM carte);
Exemplu:
Pentru fiecare autor care are mai mult de 10 creaţii expuse în muzeu, să se
şteargă ultima operă creată de acesta.
DELETE FROM opera o1
WHERE cod_artist =
(SELECT cod_artist
FROM opera o2
WHERE cod_artist = o1.cod_artist
AND data_crearii =
(SELECT MAX(data_crearii)
FROM opera
WHERE cod_artist = o2.cod_artist)
AND 10 <
(SELECT COUNT(*)
FROM opera
WHERE cod_artist = o2.cod_artist));
Exemple BD GALERIE de ARTA
c) Să se şteargă toate operele care se află expuse într-o galerie al cărei nume
conţine şirul de caractere „muzeu“.
DELETE FROM opera
WHERE cod_galerie = (SELECT cod_galerie
FROM galerie
WHERE UPPER(nume_galerie)
LIKE '%MUZEU%');
Dacă se încearcă ştergerea unei înregistrări care conţine o valoare implicată
într-o constrângere de integritate, atunci va fi returnată o eroare.
Exemplu:
DELETE FROM galerie
WHERE cod_galerie = 40;
În urma execuţiei acestei instrucţiuni sistemul generează eroarea „ORA-
02292: integrity constraint (STUDENT.SYS_C002773) violated - child record
found“, datorată calităţii de cheie externă a coloanei cod_galerie în tabelul opera.
Există opere în galeria având codul 40 şi de aceea aceasta nu poate fi suprimată.
În cazul în care constrângerea de integritate referenţială a fost definită
utilizând opţiunea ON DELETE CASCADE, atunci instrucţiunea DELETE va
şterge atât liniile indicate, cât şi liniile „copil“ din tabelele corespunzătoare.
LIMBAJUL PENTRU CONTROLUL DATELOR
(b) SAVEPOINT a;
(f) ROLLBACK TO a;
INSERT INTO salariat
VALUES (18,‟Ion‟,‟Mihai‟,5,580,‟redr_sef‟);
COMMIT;
Consistenţa la citire
Într-un sistem multi-user, sistemul Oracle furnizează read consistency la
nivel de instrucţiune SQL, adică o singură comandă SQL nu poate da rezultate care
sunt contradictorii sau inconsistente. Read consistency asigură că fiecare utilizator
“vede” datele aşa cum existau la ultimul commit, înainte să înceapă o operaţie
LMD. Prin urmare, modificările efectuate asupra unei baze de date nu sunt vizibile
decât după ce operaţia de actualizare a fost validată. Numai utilizatorul care a
executat tranzacţia poate vedea modificările făcute de el în cursul acestei tranzacţii.
Modelul multiversiune, furnizat de Oracle, asigură consistenţa la citire:
garantează că setul de date văzut de orice instrucţiune SQL este
consistent şi nu se schimbă în timpul execuţiei unei instrucţiuni (Oracle
asigură o consistenţă la citire la nivel de instrucţiune);
operaţiile de citire (SELECT) nu trebuie să vadă datele care sunt
în proces de schimbare;
operaţiile de scriere (INSERT, DELETE, UPDATE) nu trebuie
să afecteze consistenţa datelor şi să întrerupă sau să intre în conflict cu alte
operaţii de scriere concurente.
Cum se implementează modelul multiversiune? Dacă asupra bazei este
executată o comandă LMD, server-ul Oracle face o copie a datelor dinainte de
modificare şi o depune în segmentul rollback (undo).
Toţi utilizatorii (cu excepţia celor care modifică datele) vor vedea datele
cum sunt înainte de modificare (văd conţinutul segmentului undo). Dacă comanda
LMD este commit, atunci schimbările din baza de date devin vizibile oricărui
utilizator care foloseşte instrucţiunea SELECT. Când se termină tranzacţia, spaţiul
ocupat în segmentul undo de “vechea” dată este liber pentru reutilizare. Server-ul
Oracle asigură astfel o vizualizare consistentă a datelor în orice moment.
Carte
Procesarea tranzacţiilor
Modificările făcute asupra datelor în timpul unei tranzacţii sunt temporare
până când tranzacţia este salvată. Operaţiile de prelucrare a datelor afectează iniţial
un buffer al bazei. Prin urmare, starea precedentă a datelor poate fi recuperată.
În general, o tranzacţie începe atunci când este întâlnită prima instrucţiune
LMD şi se termină la:
apariţia unei instrucţiuni LDD;
emiterea unei instrucţiuni LCD;
părăsirea mediului SQL*Plus;
defectarea staţiei de lucru sau la înregistrarea unei întreruperi
a sistemului.
Execuţia unei instrucţiuni LDD determină salvarea automată a modificărilor
survenite pe perioada tranzacţiei. Server-ul Oracle generează o operaţie COMMIT
implicită înainte şi după orice instrucţiune LDD. Aşadar, chiar dacă instrucţiunea
LDD nu este executată cu succes, instrucţiunea anterioară acesteia nu mai poate fi
anulată întrucât server-ul a efectuat deja operaţia COMMIT.
Instrucţiunile COMMIT şi ROLLBACK încheie tranzacţia curentă prin
definitivarea, respectiv anularea tuturor modificărilor aflate în aşteptare. Aceste
instrucţiuni permit:
asigurarea consistenţei datelor;
previzualizarea modificărilor asupra datelor înainte ca acestea
să devină permanente;
gruparea logică a operaţiilor.
Ieşirea din mediul SQL*Plus fără lansarea unei instrucţiuni COMMIT sau
ROLLBACK are ca efect salvarea automată a tranzacţiei curente.
Atunci când intervine o anomalie (cădere) a sistemului sau închiderea
anormală a sesiunii SQL*Plus, întreaga tranzacţie curentă este anulată automat
(ROLLBACK). Acest fapt împiedică eroarea să cauzeze modificări nedorite ale
datelor şi determină revenirea la starea din momentul ultimei operaţii COMMIT.
O ieşire normală din iSQL*Plus are loc prin apăsarea butonului Exit.
Terminarea normală a unei sesiuni SQL*Plus are loc prin execuţia comenzii EXIT.
În SQL*Plus, închiderea ferestrei este interpretată ca ieşire anormală.
Interfaţa SQL*Plus pune la dispoziţie variabila de mediu AUTOCOMMIT,
care poate avea valorile ON sau OFF. Dacă această variabilă are valoarea ON,
atunci efectul fiecărei instrucţiuni LMD se definitivează imediat ce instrucţiunea a
fost executată. Dacă variabila AUTOCOMMIT are valoarea OFF, definitivarea
unei tranzacţii va avea loc la execuţia comenzii COMMIT sau în cazurile de salvare
automată a tranzacţiilor, prezentate anterior.
După încheierea unei tranzacţii, următoarea instrucţiune SQL executabilă
marchează automat începutul unei noi tranzacţii.
Comanda COMMIT
Instrucţiunea COMMIT determină încheierea tranzacţiei curente şi
permanentizarea modificărilor care au intervenit pe parcursul acesteia.
Instrucţiunea suprimă toate punctele intermediare definite în tranzacţie şi
eliberează blocările tranzacţiei. De asemenea, instrucţiunea poate fi utilizată pentru
terminarea unei tranzacţii read-only începută printr-o comandă SET
TRANSACTION.
Sistemul lansează implicit o comandă COMMIT înainte şi după orice
instrucţiune LDD. Sintaxa corespunzătoare comenzii COMMIT este următoarea:
COMMIT [WORK] [COMMENT 'text' | FORCE 'text' [, nr_întreg] ];
Opţiunea WORK a fost introdusă din motive de conformitate cu standardul
SQL. Instrucţiunile COMMIT şi COMMIT WORK sunt echivalente.
Clauza COMMENT permite specificarea unui comentariu care va fi asociat
tranzacţiei curente. Textul care îi urmează poate ocupa maxim 255 octeţi şi va fi
stocat în vizualizarea DBA_2PC_PENDING din dicţionarul datelor.
Într-un sistem distribuit, clauza FORCE permite salvarea manuală a unei
tranzacţii distribuite in-doubt (în care operaţia COMMIT a fost întreruptă de o
cădere a sistemului sau a reţelei). Textul care îi urmează conţine identificatorul
local sau global al tranzacţiei. Pentru a afla aceşti identificatori se poate consulta,
de asemenea, vizualizarea DBA_2PC_PENDING din dicţionarul datelor.
Pentru a atribui tranzacţiei un system change number (SCN) se specifică un
număr întreg în clauza FORCE. Valoarea unui SCN este mărită ori de câte ori este
salvată o tranzacţie. Acest număr are un rol important în asigurarea consistenţei la
citire. În general, o cerere citeşte un bloc de date care are ca atribut un SCN
corespunzător ultimei modificări a blocului respectiv. Dacă acest SCN este mai
mare decât cel citit la începutul interogării, înseamnă că blocul a fost modificat
ulterior lansării cererii. Prin urmare, trebuie găsită o versiune mai veche a blocului
în segmentele de anulare.
O instrucţiune COMMIT care conţine clauza FORCE permanentizează
numai tranzacţia specificată şi nu o afectează pe cea curentă. Prezenţa acestei
clauze nu este permisă în PL/SQL.
Exemplu:
Să se insereze o înregistrare în tabelul artist şi să se permanentizeze
modificarea.
INSERT INTO artist(cod_artist, nume, prenume)
VALUES (189, 'Pallady', 'Theodor');
COMMIT;
Înainte de operaţia COMMIT, utilizatorul curent poate vizualiza rezultatele
comenzilor LMD prin interogarea tabelelor. Efectele acestor operaţii nu sunt
vizibile celorlalţi utilizatori. Server-ul Oracle asigură consistenţa la citire, astfel
încât fiecare utilizator vizualizează datele în starea corespunzătoare ultimei operaţii
COMMIT efectuate asupra lor. Liniile afectate de tranzacţia curentă sunt blocate,
nefiind posibilă modificarea lor de către ceilalţi utilizatori.
Dacă mai mulţi utilizatori modifică simultan acelaşi tabel, fiecare dintre
aceştia poate consulta numai propriile modificări. Pe măsură ce operaţia COMMIT
este executată de către utilizatori, actualizările efectuate de aceştia devin vizibile.
În urma execuţiei instrucţiunii COMMIT, modificările asupra datelor sunt
scrise în baza de date, iar starea precedentă a datelor este pierdută definitiv. În
acest fel, rezultatele tranzacţiei pot fi vizualizate de către toţi utilizatorii. Blocările
asupra liniilor afectate sunt eliberate, astfel că înregistrările devin disponibile
celorlalţi utilizatori pentru a efectua noi actualizări. După operaţia COMMIT, toate
punctele intermediare (SAVEPOINT) ale tranzacţiei respective sunt şterse.
Comanda ROLLBACK
Atunci când o linie este modificată, valorile anterioare ale coloanelor
actualizate sunt salvate într-un segment de reluare. Dacă tranzacţia este anulată,
server-ul Oracle va rescrie valorile din acest segment în linia tabelului.
Pentru a renunţa la modificările efectuate se utilizează instrucţiunea
ROLLBACK. În urma execuţiei acesteia, se încheie tranzacţia, se anulează
modificările asupra datelor, se restaurează starea lor precedentă şi se eliberează
blocările asupra liniilor.
O parte a tranzacţiei poate fi anulată automat printr-o operaţie ROLLBACK
implicită dacă a fost detectată o eroare în timpul execuţiei unei instrucţiuni. Dacă o
singură instrucţiune LMD eşuează în timpul execuţiei unei tranzacţii, efectul său
este anulat de un ROLLBACK la nivel de instrucţiune, dar schimbările efectuate de
instrucţiunile LMD precedente nu sunt anulate. Acestea din urmă pot fi salvate sau
anulate explicit de către utilizator.
Sintaxa instrucţiunii ROLLBACK este următoarea:
ROLLBACK [WORK]
[TO [SAVEPOINT] pct_intermediar | FORCE 'text'];
Semnificaţiile opţiunilor WORK şi FORCE sunt similare celor prezentate în
cadrul instrucţiunii COMMIT.
În clauza TO SAVEPOINT se poate specifica punctul intermediar până la
care se doreşte anularea tranzacţiei. În absenţa acestei clauze, întreaga tranzacţie
este anulată. O tranzacţie in-doubt nu poate fi anulată manual până la un punct
intermediar.
Dacă a fost definit un punct intermediar prin instrucţiunea SAVEPOINT
nume, instrucţiunea ROLLBACK TO SAVEPOINT nume determină întoarcerea
tranzacţiei curente la punctul intermediar specificat.
În felul acesta se revine într-o stare anterioară a tranzacţiei şi se anulează
modificările care au survenit după definirea punctului intermediar. De asemenea,
sunt şterse punctele intermediare ulterioare acestuia şi sunt eliberate toate blocările
asupra tabelelor sau liniilor, efectuate după punctul intermediar respectiv.
Comanda SAVEPOINT
Instrucţiunea SAVEPOINT marchează un punct intermediar în procesarea
tranzacţiei. În acest mod este posibilă împărţirea tranzacţiei în subtranzacţii.
Această instrucţiune nu face parte din standardul ANSI al limbajului SQL.
Punctele intermediare definite în procesarea unei tranzacţii nu sunt obiecte
ale schemei şi nu pot fi referite în dicţionarul datelor. Nu există nici o modalitate
de a lista punctele intermediare definite. Dacă este creat un al doilea punct
intermediar având acelaşi nume cu un punct intermediar precedent, acesta din urmă
este şters.
Instrucţiunea SAVEPOINT are sintaxa:
SAVEPOINT nume_pct_intermediar;
Exemplu:
Să se mărească prin 20%, respectiv 50% valoarea operelor care au codurile
100, respectiv 150. Să se verifice că valoarea totală a operelor din galerie nu
depăşeşte 7 000 000, iar apoi să se reactualizeze valoarea operei având codul 150,
mărind-o cu 40%.
UPDATE opera
SET valoare = valoare * 1.2
WHERE cod_opera = 100;
SAVEPOINT val_100;
UPDATE opera
SET valoare = valoare * 1.5
WHERE cod_opera = 150;
SAVEPOINT val_150;
SELECT SUM(valoare)
FROM opera;
ROLLBACK TO SAVEPOINT val_100;
UPDATE opera
SET valoare = valoare * 1.4
WHERE cod_opera = 150;
COMMIT;
Consistenţa la citire
Utilizatorii pot accesa baza de date prin operaţii de citire (instrucţiuni
SELECT) sau prin operaţii de scriere (instrucţiuni INSERT, UPDATE, DELETE).
Consistenţa la citire este necesară pentru a asigura că:
datele vizualizate de către utilizatorii care scriu şi citesc din
baza de date sunt consistente;
utilizatorii care citesc din baza de date nu pot vizualiza starea
datelor care sunt în curs de modificare;
utilizatorii care scriu în baza de date sunt asiguraţi că
modificările asupra acesteia se realizează în mod consistent;
modificările pe care le efectuează un utilizator nu intră în
conflict cu cele ale altui utilizator.
Scopul consistenţei la citire este ca fiecare utilizator să vizualizeze datele în
starea existentă la ultima operaţie COMMIT.
Consistenţa la citire se implementează automat, prin păstrarea unei copii
parţiale a bazei de date în segmentele de reluare. Atunci când se efectuează o
operaţie INSERT, UPDATE sau DELETE, server-ul Oracle reţine o copie a datelor
în starea dinaintea modificării şi o scrie într-un segment de reluare.
Pentru toţi utilizatorii care citesc din baza de date, cu excepţia celui care a
iniţiat modificarea, datele sunt vizibile în starea dinaintea începerii reactualizării.
Aceşti utilizatori vizualizează starea datelor din segmentul de reluare. Astfel, se
garantează că utilizatorii citesc date consistente care nu sunt în curs de schimbare.
După ce se efectuează operaţia COMMIT asupra tranzacţiei, modificările
făcute în baza de date devin vizibile tuturor. Server-ul Oracle creează vizualizări
consistente la citire pentru interogările care au început înainte ca tranzacţia să fie
salvată. Apoi, spaţiul ocupat de datele vechi în segmentul de reluare va fi eliberat
pentru a fi refolosit.
Dacă se efectuează operaţia ROLLBACK asupra tranzacţiei, modificările
sunt anulate. Astfel, versiunea datelor salvate în segmentul de reluare este rescrisă
în tabel, iar baza de date revine în starea de dinaintea începerii tranzacţiei.