Sunteți pe pagina 1din 21

SQL

1.1. Prezentare generală


Un limbaj particular care provine din dezvoltarea unui model relaţional este Limbajul Interogării Structurate, sau
SQL. În ultimii ani SQL a devenit limbajul standard al bazelor de date relaţionale, în 1986 un standard pentru
SQL fiind definit de American National Standards Institute (ANSI), care a fost preluat ca standard internaţional
de către International Standards Organization (ISO) în 1987. Mai mult de 100 de sisteme de administrare a
bazelor de date suportă SQL, rulate atât pe calculatoare personale cât şi pe reţele de calculatoare.
În continuare, prezentarea SQL va fi făcută folosind în special terminologia ISO, în acest capitol fiind prezentate
noţiunile de bază ale limbajului SQL.

1.1.1. Obiectivele şi terminologia SQL


În mod ideal un limbaj de baze de date ar trebui să permită utilizatorului să creeze baze de date şi structuri
relaţionale; ar trebui să permită realizarea unor operaţii de bază asupra datelor ca: inserarea, modificarea şi
ştergerea datelor din relaţii; şi ar trebui să permită realizarea atât a interogărilor simple cât şi complexe pentru a
transforma datele iniţiale în informaţii. Ca o completare un limbaj de baze de date trebuie să gestioneze aceste
evenimente cu un efort minim, şi structura şi sintaxa comenzilor trebuie să fie relativ uşor de învăţat. În final
acesta trebuie să fie portabil, adică să fie conform unor standarde recunoscute în domeniu astfel încât să putem
folosi aceleaşi structuri şi sintaxe de comenzi când trecem de la un SGBD la altul. SQL intenţionează să satisfacă
aceste cerinţe.
SQL este un exemplu de limbaj construit pentru folosi relaţiile pentru a transforma intrările în ieşirile cerute. Ca
limbaj, SQL are două mari componente:
• un limbaj de definire a datelor (LDD) pentru definirea structurii bazelor de date ;
• un limbaj de manipulare a datelor (LMD) pentru recuperarea şi actualizarea datelor ;
SQL conţine numai aceste comenzi definitorii şi manipulative; nu conţine comezi de control ca:
IF…THEN…ELSE, GO TO, DO…WHILE ori alte astfel de comenzi. Acestea trebuiesc implementate folosind
un limbaj de programare sau în mod interactiv prin deciziile utilizatorului.
SQL poate fi utilizat în două moduri. Primul mod este de a folosi SQL interactiv, introducînd comenzile de la un
terminal. Al doilea mod este de a ataşa comenzile SQL unui limbaj de programare procedural.
SQL este un limbaj relativ uşor de învăţat:
• Este un limbaj non-procedural: utilizatorul mai degrabă specifică ce informaţii îl interesează, decât
cum se obţin acestea. Cu alte cuvinte nu cere să se specifice metodele de acces la date.
• Ca majoritatea limbajelor moderne, SQL are în mod esenţial un format liber, ceea ce înseamnă că
blocurile de declaraţii nu trebuie tipărite în zone particulare ale ecranului.
• Structura comenzilor conţine cuvinte din engleza standard: CREATE TABLE, INSERT, SELECT.
• SQL poate fi folosit de o mulţime de utilizatori incluzînd administratorii bazelor de date,
programatorii de aplicaţii şi multe alte tipuri de utilizatori.
Există acum un standard internaţional pentru limbajul SQL (ISO, 1992), care este considerat limbajul standard
de definire şi manipulare a bazelor de date relaţionale.
Standardul ISO SQL nu foloseşte termeni formali pentru relaţii, atribute şi tupluri, în schimb foloseşte termenii
de tabele, coloane şi linii. De asemenea trebuie să punctăm faptul că SQL nu aderă strict la o definiţie a
modelului relaţional. De exemplu, SQL permite tabelul produs ca rezultat a operaţiei SELECT ce conţine linii
duble, impunînd o ordine a coloanelor, şi de asemenea permite utilizatorului de a ordona liniile unui tabel.

1.1.2. Scrierea comenzilor SQL


În acestă secţiune vom descrie structura unei declaraţii SQL şi notaţiile pe care le folosim în definirea
formatelor pentru diferite construcţii SQL. O declaraţie SQL conţine cuvinte rezervate şi cuvinte definite de
către utilizator. Cuvintele rezervate sunt o parte fixată a limbajului SQL şi au un înţeles fix. Ele trebuie scrise
exact şi nu pot fi despărţite cu cratimă. Cuvintele definite de către utilizator sunt create ţinîndu-se seama de
reguli specifice de sintaxă şi reprezintă numele diferitelor obiecte de bază de date cum ar fi relaţii, coloane,
indexuri etc. Cuvintele dintr-o declaraţie sunt de asemenea construite ţinîndu-se seama de un set de reguli de
sintaxă. Deşi standardul nu cere acest lucru, multe dialecte SQL pretind folosirea unui declaraţii finale pentru a
marca sfârşitul fiecărei declaraţii SQL (de obicei se foloseşte “;”).

1
Majoritatea componentelor unei declaraţii SQL sunt insensibile la litere mari sau mici. O excepţie importantă de
la această regulă este faptul că datele caractere literale trebuiesc scrise exact cum apar în baza de date. De
exemplu dacă scriem numele unei persoane ‘MARIN’ şi apoi căutăm numele folosind ‘Marin’ articolul nu va fi
găsit.
Deşi SQL este de liber format, o declaraţie SQL sau un set de declaraţii este mult mai interesantă dacă este
folosită alinierea. De exemplu:
• Fiecare clauză din declaraţie ar trebui scrisă pe linie nouă.
• Începutul fiecărei clauze ar trebui scrisă sub începutul altei clauze.
• Dacă o clauză are mai multe părţi ele ar trebui să apară pe linii separate şi să fie inclusă la începutul
clauzei pentru a arăta relaţia dintre ele.
În acest capitol vom folosi următoarea modalitate pentru a defini declaraţiile SQL:
• Literele mari sunt folosite în reprezentarea cuvintelor rezervate şi trebuiesc scrise exact.
• Literele mici sunt folosite în reprezentarea cuvintelor definite de către utilizator.
• “|” indică o alegere între alternative; de exemplu a | b | c.
• Parantezele acolade indică un element dorit: de exemplu, {a}.
• Parantezele pătrate indică un element opţional: de exemplu, [a].
• Punctele (…) sunt folosite pentru a indica repetiţia opţională a unui element nul sau a unui alt
element de mai multe ori. De exemplu: {a|b}[,c…], ceea ce înseamnă a sau b urmat de zero sau mai
multe repetiţii al lui c separate prin virgulă.
În practică declaraţiile LDD sunt folosite pentru crearea de structuri de baze de date (tabele), iar declaraţiile
LMD pentru a popula şi interoga tabelele. În acest capitol vom prezenta declaraţiile LMD înaintea celor LDD
pentru a arăta importanţa relativă a declaraţiilor LMD.

1.2. SQL - limbaj de manipulare a datelor (LMD)


Această secţiune se referă la declaraţii SQL LMD disponibile, numite:
• SELECT interogarea datelor din baza de date,
• INSERT inserarea datelor într-un tabel,
• UPDATE actualizarea datelor într-un tabel,
• DELETE ştergerea datelor dintr-un tabel.
Datorită complexităţii declaraţiilor SELECT şi a simplităţii relative a altor declaraţii LMD o mare parte din
această secţiune va fi dedicată declaraţiei SELECT şi diferitelor formate ale acesteia. La început vor fi prezentate
interogări simple şi succesiv acestea se vor complica pentru a arăta că pot fi generate interogări care folosesc la
sortarea, gruparea, combinarea datelor şi de asemenea interogări pe mai multe tabele. În finalul aceastei secţiuni
vor fi prezentate declaraţiile INSERT, UPDATE şi DELETE.
Pe parcursul acestei secţiuni vor fi date exemple pentru toate comenzile SQL care vor fi prezentate, iar aceste
exemple vor folosi, pentru a ilustra felul în care operează aceste comenzi, următoarele relaţii:
cadre_didactice (nr_mat, nume, prenume, funcţia, vechime, salariu)
secţii (cod_sec, denumire, iniţiale)
grupe (cod_sec, cod_gr, nr_semigrupe, nr_studenţi)
oraşe (cod, denumire);
municipii(cod_m, denum_m);
Pentru aceste relaţii vom considera următoarele înregistrări:

Tabelul cadre_didactice:
nr_mat nume prenume funcţia vechime salariu
1111 Popa Ioan lect. 15 400,000
1112 Liana Carmen conf. 20 700,000
1113 Barbu Traian conf. 25 900,000
1114 Ştefan Elena conf. 25 900,000
1115 Savu Alexandru conf. 28 950,000
1116 Ionescu Mircea lect. 18 400,000

Tabelul secţii:
cod_sec denumire iniţiale
1 Matematică M
2 Informatică I
3 Matematică-fizică MF
4 Fizică-chimie FC

2
Tabelul grupe:
cod_sec cod_gr nr_semigrupe nr_studenţi
1 1 2 25
1 2 2 24
2 1 1 30
3 1 2 28
3 2 1 24
4 1 2 29

Tabelul oraşe: Tabelul municipii:


cod denumire cod_m denum_m
O1 Braşov M1 Timişoara
O2 Codlea M2 Iaşi
O3 Timişoara M3 Braşov

1.2.1. Comanda SELECT


Scopul declaraţiei SELECT este de a recupera şi a afişa datele din unul sau mai multe tabele de baze de date.
Este o comandă extrem de puternică capabilă de a echivala în algebra relaţională selecţia, proiecţia şi joncţiunea
într-o singură declaraţie. SELECT este comanda cea mai des folosită în SQL. Forma generală a declaraţiei
SELECT este :

SELECT [DISTINCT|ALL]{* | [expresie_coloană [AS nume_nou]][,…]}


FROM nume_tabel [alias][,…]
[WHERE condiţie]
[GROUP BY listă_coloane] [HAVING condiţie]
[ORDER BY listă_coloane]

În această declaraţie expresie_coloană reprezintă o coloană sau o expresie; alias reprezintă o prescurtare a
numelui de tabel. Pentru cuvintele rezervate avem următoarea semnificaţie:
FROM specifică tabelul sau tabelele care vor fi folosite;
WHERE specifică liniile care trebuiesc recuperate cu ajutorul unor condiţii;
GROUP BY grupează toate liniile din tabele care au aceeaşi valoare pe coloana specificată;
HAVING selectează grupurile care respectă condiţia de căutare;
SELECT specifică coloanele care vor apărea în rezultatul interogării;
ORDER BY ordonează valorile returnate după coloanele specificate.
Ordinea clauzelor în declaraţia SELECT nu poate fi schimbată. Singurele două clauze obligatorii sunt primele
două: SELECT şi FROM celelalte fiind opţionale. Operaţia SELECT este închisă, rezultatul interogării unui
tabel fiind tot un tabel. Există mai multe variante ale acestei comenzi, totuşi exemplele care urmează ilustrează
principalele variante ale acestei declaraţii.

Interogări simple

1. Recuperarea tuturor liniilor unui tabel:


SELECT nr_mat, nume, prenume, funcţia, vechime, salariu
FROM cadre_didactice;
SELECT *
FROM cadre_didactice;

Obţinem în urma acestor două interogări acelaşi rezultat:

nr_mat nume prenume funcţia vechime salariu


1111 Popa Ioan lect. 15 400,000
1112 Liana Carmen conf. 20 700,000
1113 Barbu Traian conf. 25 900,000
1114 Ştefan Elena conf. 25 900,000
1115 Savu Alexandru conf. 28 950,000
1116 Ionescu Mircea lect. 18 400,000

2. Recuperarea liniilor unui tabel doar pentru anumite coloane ale:


3
SELECT nr_mat, nume, prenume, salariu
FROM cadre_didactice;

nr_mat nume prenume salariu


1111 Popa Ioan 400,000
1112 Liana Carmen 700,000
1113 Barbu Traian 900,000
1114 Ştefan Elena 900,000
1115 Savu Alexandru 950,000
1116 Ionescu Mircea 400,000

3. Utilizarea cuvântului rezervat DISTINCT care elimină liniile duble:

SELECT salariu SELECT DISTINCT salariu


FROM cadre_didactice; FROM cadre_didactice;

salariu salariu
400,000 400,000
700,000 700,000
900,000 900,000
900,000 950,000
950,000
400,000

4. Folosirea câmpurilor calculate:

SELECT nr_mat, nume, prenume, salariu/5


FROM cadre_didactice;

nr_mat nume prenume col 4


1111 Popa Ioan 80,000
1112 Liana Carmen 140,000
1113 Barbu Traian 180,000
1114 Ştefan Elena 180,000
1115 Savu Alexandru 190,000
1116 Ionescu Mircea 80,000

Clauza WHERE

Exemplele de mai sus arată folosirea declaraţiei SELECT pentru a recupera toate liniile dintr-un tabel. Totuşi,
deseori avem nevoie de a restrânge liniile care au fost recuperate. Acest lucru se poate realiza cu clauza WHERE
care conţine cuvântul cheie urmat de o condiţie de căutare care specifică liniile ce trebuiesc recuperate.
În SQL avem următoarii operatori de comparaţie: =, <, >, <=, >=, < >, != (diferit).
Multe predicate complexe pot fi generate folosind operatori logici AND, OR, şi NOT, cu paranteze sau nu,
pentru a arăta ordinea de valoare. Regulile pentru evaluarea expresiilor condiţionate sunt:
• O expresie evaluată de la stânga la dreapta.
• Subexpresiile în paranteze sunt evaluate prima dată.
• Operatorii NOT sunt evaluaţi înaintea operatorilor AND şi OR.
• Operatorii AND sunt evaluaţi înaintea celor OR.
Folosirea parantezelor este întotdeauna recomandată pentru a elimina posibilile ambiguităţi.
Cele 5 condiţii principale de căutare sunt următoarele:
1. Comparaţia. Compară valorile unei expresii cu valorile altei expresii:

SELECT nr_mat, nume, prenume, salariu


FROM cadre_didactice
WHERE salariu >700.000;

4
nr_mat nume prenume salariu
1113 Barbu Traian 900,000
1114 Ştefan Elena 900,000
1115 Savu Alexandru 950,000
2. Şir. Testează dacă valoarea unei expresii este inclusă într-un şir de valori specificate
(BETWEEN/NOT BETWEEN):

SELECT nr_mat, nume, prenume, salariu


FROM cadre_didactice
WHERE salariu BETWEEN 500.000 AND 900.000;
nr_mat nume prenume salariu
1112 Liana Carmen 700,000
1113 Barbu Traian 900,000
1114 Ştefan Elena 900,000

3. Apartenenţa la o mulţime. Testează dacă valoarea unei expresii aparţine unui set de valori
(IN/NOT IN):

SELECT nr_mat, nume, prenume, vechime


FROM cadre_didactice
WHERE vechime IN (20, 25);

nr_mat nume prenume vechime


1112 Liana Carmen 20
1113 Barbu Traian 25
1114 Ştefan Elena 25

4. Compatibilitatea. Testează dacă un şir se potriveşte cu un model specificat (LIKE/NOT LIKE).


SQL are două caractere speciale:
% reprezintă orice secvenţă de zero sau de mai multe caractere;
_ reprezintă orice caracter singular.
Celelalte caractere se folosesc normal. De exemplu:
• adresa LIKE ‘H%’ înseamnă că primul caracter trebuie să fie H, iar celelalte pot fi orice caracter.
• adresa LIKE ‘H_ _ _’ înseamnă că trebuie să fie exact patru caractere în şir primul fiind H.
• adresa LIKE ‘%e’ înseamnă orice secvenţă de caractere de lungime cel puţin 1 cu ultimul caracter e.
• adresa LIKE ‘%Popescu%’ înseamnă o secvenţă de caractere de orice lungime care conţine
Popescu.
• adresa NOT LIKE ‘H%’ înseamnă că numele nu poate începe cu H.

SELECT nr_mat, nume, prenume


FROM cadre_didactice
WHERE nume LIKE ‘P%’;

nr_mat nume prenume


1111 Popa Ioan
(1 linie)

5. Nul. Testează dacă o coloană are o valoare nulă (necunoscută) (IS NULL/IS NOT NULL).

Clauza ORDER BY (rezultate de sortare)

În general, liniile tabelului rezultat a unei interogari SQL nu sunt aranjate într-o anumită ordine. Totuşi, putem
sorta rezultatele unei interogări folosind clauza ORDER BY din declaraţia SELECT. Clauza ORDER BY
conţine o listă de identificatori de coloană pentru ca rezultatul să fie sortat şi separat prin virgulă. Identificatorul
de coloană poate fi, fie un nume de coloană, fie un număr de coloană care identifică un element din lista
SELECT dîndu-ne poziţia acestuia în listă:1 pentru primul element din listă, 2 pentru al doilea, ş.a.m.d.
Numerele de coloană pot fi folosite dacă coloana ce trebuie sortată este o expresie şi nici o clauză AS nu este
specificată în vederea ataşări unui nume pentru coloană, nume care ar putea fi de referinţă ulterior. Clauza
ORDER BY ne permite ordonarea articolelor recuperate în mod crescător (ASC) sau descrescător (DESC) pe
orice coloană sau combinaţie de coloane indiferent dacă coloana apare sau nu în rezultat.Totuşi, unele dialecte

5
insistă asupra faptului ca elementele ORDER BY să apara în lista SELECT. În ambele cazuri, clauza ORDER
BY trebuie să fie întotdeauna ultima în declaraţia SELECT.

SELECT nr_mat, nume, prenume, salariu


FROM cadre_didactice
ORDER BY salariu DESC;

nr_mat nume prenume salariu


1115 Savu Alexandru 950,000
1113 Barbu Traian 900,000
1114 Ştefan Elena 900,000
1112 Liana Carmen 700,000
1111 Popa Ioan 400,000
1116 Ionescu Mircea 400,000

Este posibil de a include mai multe elemente în clauza ORDER BY. Cheia de sortare majoră determină ordinea
generală a tabelului rezultat. Dacă cheia de sortare majoră este unică, nu mai este nevoie de nici o cheie de
control pentru sortare. Totuşi, dacă cheia de sortare majoră nu este unică, ar putea să existe mai multe linii din
tabelul rezultat cu aceeaşi valoare pentru cheia de sortare majoră. În acest caz, ar fi de dorit să se ordoneze liniile
cu aceeaşi valoare pentru cheia de sortare majoră cu ajutorul unei chei de sortare adiţională. Dacă apare un al
doilea element în clauza ORDER BY, atunci acesta se numeşte cheie de sortare minoră.

SELECT nr_mat, nume, prenume, funcţia, salariu


FROM cadre_didactice
ORDER BY funcţia, salariu DESC;

nr_mat nume prenume funcţia salariu


1111 Popa Ioan lect. 400,000
1116 Ionescu Mircea lect. 400,000
1115 Savu Alexandru conf. 950,000
1113 Barbu Traian conf. 900,000
1114 Ştefan Elena conf. 900,000
1112 Liana Carmen conf. 700,000

Standardul ISO specifică faptul că zerourile dintr-o coloană sau expresie sortată cu ORDER BY trebuiesc tratate
mai puţin sau mai mult decît toate valorile nenule. Acest lucru rămîne la alegerea implementorului SGBD.

Folosirea funcţiilor complexe SQL

Standardul ISO defineşte 5 functii complexe:


• COUNT returnează numarul de valori dintr-o coloană specificată.
• SUM returnează suma valorilor dintr-o coloană specificată.
• AVG returnează media valorilor dintr-o coloană specificată.
• MIN returnează cea mai mică valoare dintr-o coloană specificată.
• MAX returnează cea mai mare valoare dintr-o coloană specificată.
Aceste funcţii operează pe o singură coloană a tabelului şi returnează o singură valoare. COUNT,
MIN, MAX se aplică atât pe câmpuri numerice cât şi pe câmpuri nenumerice, iar SUM şi AVG pot fi folosite
numai pe câmpuri numerice. În afară de COUNT(*), fiecare funcţie elimină prima dată zerourile şi operează pe
cele care au rămas cu valoare nenulă. COUNT(*) este o folosire specială a lui COUNT. Scopul ei este de a
număra liniile unui tabel indiferent dacă conţin sau nu zerouri sau valori duble.
Dacă dorim să eliminăm dublurile înainte de a aplica funcţia, trebuie să folosim cuvântul cheie
DISTINCT înainte de numele de coloană din funcţie. Standardul ISO permite specificarea cuvântului cheie ALL,
dacă nu dorim eliminarea dublurilor, deşi ALL este presupus dacă nu se specifică nimic. DISTINCT nu are nici
un efect asupra funcţiilor MIN şi MAX. Totuşi, poate avea un efect asupra rezultatelor date de SUM sau AVG,
de aceea trebuie pus în vedere dacă dublurile trebuiesc incluse sau nu în operaţie. Ca o adăugare, DISTINCT
poate fi specificat o singură dată în interogare.
Este important faptul că funcţiile complexe pot fi folosite numai în lista SELECT şi în clauza
HAVING. Este incorectă folosirea lor în altă parte. Dacă lista SELECT include o funcţie complexă şi dacă nu a
fost folosită clauza GROUP BY pentru gruparea datelor, atunci nici un element din lista SELECT nu poate
conţine referinţe la o coloană numai dacă acea coloană este un argument a unei funcţii complexe. De exemplu,
interogarea următoare este incorectă:

6
SELECT nr_mat, COUNT(salariu)
FROM cadre_didactice;

deoarece lista SELECT conţine atât un nume de coloană (nr_mat) cât şi o funcţie complexă separată (COUNT),
fără a fi folosită o clauză GROUP BY.
Exemple:
1. Utilizarea funcţiei COUNT(*):

SELECT COUNT(*) AS contor


FROM cadre_didactice
WHERE salariu >400,000;

contor
4

2. Utilizarea funcţiei COUNT(DISTINCT):

SELECT COUNT(DISTINCT salariu) AS contor


FROM cadre_didactice
WHERE salariu BETWEEN 500,000 AND 950,000;

contor
3

3. Utilizarea funcţiilor COUNT şi SUM:

SELECT COUNT(nr_mat) AS contor, SUM(salariu) AS total_sal


FROM cadre_didactice
WHERE funcţia=’conf.’;

contor total_sal
4 4,450,000

4. Utilizarea funcţiilor MIN, MAX, AVG:

SELECT MIN(salariu) AS min, MAX(salariu) AS max, AVG(salariu) AS media


FROM cadre_didactice;

min max media


400,000 950,000 708,333.3

Clauza GROUP BY (rezultate de grupare)

Interogările sumare de mai sus sunt similare cu totalele de la sfârşitul raportului. Se condensează toate datele din
raport într-o singură linie sumară de date. Totuşi, este deseori folositor de a avea subtotale în raport. Pentru
aceasta putem folosi clauza GROUP BY din declaraţia SELECT. O interogare care cuprinde clauza GROUP BY
se numeşte interogare grupată, deoarece grupează datele din tabelele SELECT şi crează o singură linie pentru
fiecare grup. Coloanele numite în clauza GROUP BY se numesc coloane de grupare. Standardul ISO cere ca
clauzele SELECT şi GROUP BY să fie strâns integrate.Când se foloseşte GROUP BY, fiecare element din lista
SELECT trebuie să aibă valoare unică pe grup. În plus, clauza SELECT poate să conţină numai:
• nume de coloană,
• funcţii complexe,
• constante,
• o expresie cuprinzînd combinaţii ale elementelor de mai sus.
Toate numele de coloană din lista SELECT trebuie să apară în clauza GROUP BY, în afară de cazul când
numele este folosit numai într-o funcţie complexă. Reciproca nu este adevărată: pot să existe nume de coloane în
clauza GROUP BY care nu apar în lista SELECT. Când clauza WHERE este folosită cu GROUP BY, prima dată
este apelată clauza WHERE, apoi grupurile sunt formate din liniile rămase ceea ce satisface condiţia de căutare.

7
Standardul ISO consideră două zerouri ca fiind egale pentru scopurile clauzei GROUP BY. Dacă două
linii au zerouri în aceeaşi coloană de grupare şi valori identice în toate coloanele de grupare nenule, ele sunt
combinate în acelaşi grup.
Exemplu:
SELECT funcţia, COUNT(nr_mat) AS contor, SUM(salariu) AS total_sal
FROM cadre_didactice
GROUP BY funcţia
ORDER BY funcţia;

funcţia contor total_sal


lect. 2 800,000
conf. 4 3,450,000

Conceptual, SQL realizează interogarea în felul următor:


1. SQL divide schema în grupuri ţinînd seama de câmpul “funcţia”. În cadrul fiecărui grup, toate
cadrele didactice au aceaşi funcţie.
2. Pentru fiecare grup, SQL calculează numărul membrilor schemei şi adună valorile în coloana “salariu” pentru
a obţine totalul salariilor. SQL generează o singură linie sumară în rezultatul interogării pentru fiecare grup.
3. În final, rezultatul este sortat în ordinea crescătoare după câmpul “funcţia”.

Clauza HAVING (restrângerea grupurilor)

Clauza HAVING este folosită împreună cu clauza GROUP BY pentru a restrânge grupurile care apar la finalul
tabelului rezultat. Deşi sunt similare în sintaxă, HAVING şi WHERE servesc scopuri diferite. Clauza WHERE
strecoară linii individuale în finalul tabelului rezultat, iar HAVING strecoară grupuri în finalul tabelului rezultat.
Standardul ISO cere ca numele coloană folosite în clauza HAVING să apară, deasemenea, în lista GROUP BY
sau să fie conţinute într-o funcţie complexă. În practică, condiţia de căutare din clauza HAVING include
întotdeauna cel puţin o funcţie complexă, altfel condiţia de căutare ar putea fi mutată în clauza WHERE şi
aplicată pe linii individuale.
Clauza HAVING nu este parte importantă a SQL, orice interogare exprimată folosind clauza HAVING poate fi
rescrisă întotdeauna fără clauza HAVING.
Exemplu:
SELECT funcţia, COUNT(nr_mat) AS contor, SUM(salariu) AS total_sal
FROM cadre_didactice
GROUP BY funcţia
HAVING COUNT(nr_mat)>2
ORDER BY funcţia;

funcţia contor total_sal


conf. 4 3,450,000

Subinterogări

Unele declaraţii SQL pot avea o declaraţie SELECT completă încrustată în interiorul lor. Rezultatele acestei
declaraţii SELECT interioare (sau subselect) sunt folosite în declaraţiile exterioare pentru a ajuta la determinarea
conţinutului rezultatului final. Un subselect poate fi folosit în clauzele WHERE şi HAVING a unei declaraţii
SELECT exterioare, unde acesta este numit subinterogare sau interogare intercalată. Subselectele pot apărea, de
asemenea, în declaraţiile INSERT, UPDATE şi DELETE.

Exemplu:
SELECT cod_gr, nr_semigrupe, nr_studenţi
FROM grupe
WHERE cod_sec =
(SELECT cod_sec
FROM secţii
WHERE denumire = ‘Matematica’);

cod_gr nr_semigrupe nr_studenţi


1 2 25
8
2 2 24

Subinterogarea poate fi privită ca producătorul unor tabele temporare de rezultate, care pot fi accesate şi folosite
de către declaraţii exterioare. O subinterogare poate fi folosită imediat după un operator relaţional (=,< , >, <=,
>=, < >) într-o clauză WHERE sau HAVING. Subinterogarea însăşi este întodeauna închisă între paranteze.
Asupra subinterogărilor se aplică următoarele reguli:
1) Clauza ORDER BY nu poate fi folosită într-o subinterogare (deşi ea poate fi folosită în
majoritatea declaraţiilor SELECT exterioare.
2) Lista subinterogărilor SELECT trebuie să conţină un singur nume coloană sau o singură expresie,
excepţie făcînd subinerogările care folosesc cuvântul cheie EXISTS.
3) Deoarece lipsesc, numele coloană dintr-o subinterogare se referă la numele tabel din clauza
FROM a subinterogării. Este posibil să se recurgă la un tabel dintr-o clauză FROM a unei interogări exterioare în
modificarea numelui coloană.
4) Când o subinterogare este una din cei doi operanţi implicaţi într-o comparaţie, subinterogarea
trebuie să apară în partea dreaptă a comparaţiei.
5) O subinterogare nu poate fi folosită ca operant într-o expresie.

Folosirea cuvintelor rezervate ANY şi ALL

Cuvintele ANY şi ALL pot fi folosite cu subinterogări care produc o singură coloană a numerelor. Dacă
subinterogarea este precedată de cuvântul cheie ALL, condiţia va fi adevărată numai dacă este satisfăcută de
toate valorile date de subinterogare. Dacă subinterogarea este precedată de cuvântul cheie ANY, condiţia va fi
adevărată dacă este satisfăcută de orice (una sau mai multe) valoare dată de subinterogare. Dacă subinterogarea
este vidă, condiţia ALL returnează valoarea adevărat, iar condiţia ANY returnează valoarea fals. Standardul ISO
permite folosirea calificativului SOME în locul lui ANY.
Exemple:
1. Utilizarea cuvântului rezervat ANY:
SELECT nume, prenume, salariu
FROM cadre_didactice
WHERE salariu > ANY
(SELECT salariu
FROM cadre_didactice
WHERE funcţia =’conf’);

nume prenume salariu


Savu Alexandru 950,000
Barbu Traian 900,000
Ştefan Elena 900,000

2. Utilizarea cuvântului rezervat ALL:


SELECT nume, prenume, salariu
FROM cadre_didactice
WHERE salariu => ALL
(SELECT salariu
FROM cadre_didactice
WHERE funcţia =’conf.’);
nume prenume salariu
Savu Alexandru 950,000

Interogări multi-tabele

Toate exemplele considerate pînă acum au o limitare majoră: toate coloanele ce trebuie să apară în tabelele
rezultat trebuie să provină dintr-un singur tabel. În multe cazuri, acest lucru nu este suficient. Pentru a combina
coloanele din mai multe tabele în obţinerea unui tabel rezultat, avem nevoie de o operaţie de joncţiune. Operaţia
de joncţiune SQL combină informaţia din două tabele formînd perechi de linii înrudite din cele două tabele.
Perechile de linii ce alcătuiesc tabelul asociat sunt toate acolo unde coloanele corespunzătoare din cele două
tabele au aceeaşi valoare.
Dacă avem nevoie să obţinem informaţii din mai multe tabele, putem alege între folosirea unei subinterogări şi a
unei joncţiuni. Dacă tabelul rezultat final trebuie să conţină coloane din diferite tabele, atunci trebuie să folosim
joncţiunea. Pentru a reuşi operaţia de joncţiune, trebuie doar să includem mai multe nume de tabele în clauza
FROM, folosind ca separator virgula şi incuzînd clauza WHERE pentru a specifica coloanele joncţiunii. De
asemenea, este posibil să folosim alias pentru un tabel specificat în clauza FROM. În acest caz, aliasul este
9
separat de numele tabelului printr-un spaţiu. Un alias poate fi folosit pentru a modifica numele coloană ori de
câte ori există ambiguităţi privind sursa numelui coloană. De asemenea, se mai poate folosi ca o notaţie pentru
numele tabelului. Dacă un alias este specificat, trebuie folosit oriunde numele tabelului ar putea fi specificat.
Joncţiunea este o submulţime a unei combinaţii mult mai generale a două tabele cunoscute cum ar fi produsul
cartezian a două tabele. Produsul cartezian a doua tabele este un alt tabel alcătuit din toate perechile posibile de
linii din cele doua tabele. Coloanele tabelului produs sunt toate coloanele primului tabel urmat de toate coloanele
celui de al doilea tabel. Dacă specificăm o interogare doi-tabel fără clauza WHERE, SQL produce produsul
cartezian al celor două tabele ca fiind interogarea rezultat.
Procedura pentru generarea rezultatelor unui SELECT cu o asociere este:
1. Se formează produsul cartezian a tabelelor numite în clauza FROM.
2. Dacă există o clauză WHERE, se aplică condiţia de căutare fiecărei linii a tabelului produs, reţinînd
acele linii care satisfac condiţia. În termenii algebrei relaţionale, această operaţie produce o restricţie
a produsului cartezian.
3. Pentru fiecare linie rămasă, se determină valoarea fiecărui element din lista SELECT în scopul
producerii unei singure linii în tabelul rezultat.
4. Dacă SELECT DISTiNCT a fost specificat, se elimină orice linie dublă din tabelul rezultat. În
algebra relaţională, pasul 3 şi 4 sunt echivalente cu o proiecţie a restricţiei asupra coloanelor
menţionate în lista SELECT.
5. Dacă există o clauza ORDER BY, se sortează în modul cerut tabelul rezultat.
Exemple:

SELECT s.denumire, s.iniţiale, g.cod_gr, g.nr_semigrupe, g.nr_studenţi


FROM secţii s, grupe g
WHERE s.cod_sec = g.cod_sec

denumire iniţiale cod_gr nr_semigrupe nr_studenţi


Matematică M 1 2 25
Matematică M 2 2 24
Informatică I 1 1 30
Matematică-fizică MF 1 2 28
Matematică-fizică MF 2 1 24
Fizică-chimie FC 1 2 29

Cele mai comune interogări muti-tabel implică două tabele care au o relaţie 1:M (sau tată/fiu). Interogarea
precedentă este o astfel de interogare. Fiecare fiu are un tată asociat şi fiecare părinte are asociaţi mai multi fii.
Perechile de linii care generează interogarea rezultat sunt combinaţii de linii tată/fiu. Tabelul care conţine cheia
străină este un tabel fiu, iar tabelul care conţine cheia fundamentală este un tabel tată. Pentru a folosi relaţia
tată/fiu într-o interogare SQL, vom specifica o condiţie de căutare care compară cheia străină cu cheia
fundamentală. În exemplul de mai sus se face o astfel de comparaţie.
Standardul SQL2 furnizează următoarele moduri alternative pentru a specifica această asociere:
FROM secţii s JOIN grupe g ON s.cod_sec=g.cod_sec
FROM secţii JOIN grupe USING cod_sec
FROM secţii NATURAL JOIN grupe
În fiecare caz, clauza FROM înlocuieşte clauzele originale FROM şi WHERE.

Joncţiuni externe

Operaţia de joncţiune combină datele din două tabele formînd perechi de linii specificate, unde coloanele
corespunzătoare din fiecare tabel au aceeaşi valoare. Dacă o linie a tabelului este fără pereche, linia este omisă
din tabelul rezultat. Acesta a fost cazul pentru joncţiunile examinate mai sus. Standardul ISO furnizează un alt
set de operatori de joncţiune numiţi joncţiuni externe. Joncţiunea externă reţine liniile care nu satisfac condiţia de
joncţiune.
Există trei tipuri de joncţiuni externe:
1. joncţiune externă stângă:
SELECT o.*, m.*
FROM oraşe o LEFT JOIN municipii m
ON o.denumire = m.denum_m;

cod denumire cod_m denum_m


O1 Braşov M3 Braşov
O2 Codlea NULL NULL
O3 Timişoara M1 Timişoara

10
2. joncţiune externă dreaptă:
SELECT o.*, m.*
FROM oraşe o RIGHT JOIN municipii m
ON o.denumire = m.denum_m;

cod denumire cod_m denum_m


O3 Timişoara M1 Timişoara
NULL NULL M2 Iaşi
O1 Braşov M3 Braşov

3. joncţiune externă totală:


SELECT o.*, m.*
FROM oraşe o FULL JOIN municipii m
ON o.denumire = m.denum_m;

cod denumire cod_m denum_m


O3 Timişoara M1 Timişoara
NULL NULL M2 Iaşi
O2 Codlea NULL NULL
O1 Braşov M3 Braşov

EXISTS şi NOT EXISTS

Cuvintele cheie EXISTS şi NOT EXISTS se folosesc numai cu subinterogări. Ele produc un rezultat simplu
adevărat/fals. EXISTS este adevărat dacă şi numai dacă există cel puţin o linie în tabelul rezultat returnată de
către interogare şi este falsă dacă subinterogarea returnează un tabel rezultat vid. NOT EXISTS este opusul lui
EXISTS. În timp ce EXISTS şi NOT EXISTS verifică numai existenţa sau inexistenţa liniilor în tabelul rezultat
al subinterogării, subinterogarea poate conţine orice număr de coloane. Pentru simplificare, subinterogarea
urmînd una din aceste cuvinte cheie este de obicei de forma: (SELECT*…).
Exemplu:
SELECT cod_gr, nr_semigrupe, nr_studenţi
FROM grupe g
WHERE EXISTS
(SELECT *
FROM secţii s
WHERE g.cod_sec = s.cod_sec AND iniţiale=’M’ );

cod_gr nr_semigrupe nr_studenţi


1 2 25
2 2 24

Combinarea tabelelor rezultat (UNION, INTERSECT, EXCEPT)

În SQL, putem folosi operaţiile normale cu mulţimi: reuniune, intersecţia şi diferenţa, pentru a combina
rezultatele a două sau mai multe interogări într-un singur tabel rezultat. Reuniunea a două tabele, A şi B, este un
tabel ce conţine toate liniile care sunt atât în tabelul A cât şi în B. Intersecţia a două tabele, A şi B este un tabel
ce conţine toate liniile comune celor două tabele. Diferenţa a două tabele, A şi B, este un tabel ce conţine toate
liniile care sunt în A, dar nu sunt în B.
Există restricţii asupra tabelelor care pot fi combinate folosind operaţiile cu mulţimi. Cea mai importantă
restricţie este că cele două tabele sunt compatibile (prin asociere), adică ele au aceaşi structură. Aceasta implică
faptul că cele două tabele trebuie să conţină acelaşi numar de coloane şi că, coloanele lor corespunzătoare să aibă
aceleaşi tipuri şi lungimi de date. Este responsabilitatea utilizatorului de a se asigura că valorile datelor în
coloanele corespunzătoare provin din acelaşi domeniu. De exemplu, ar fi fără sens să combinăm o coloană care
conţine vârsta personalului cu numărul camerelor dintr-o proprietate, chiar dacă ambele coloane ar putea avea
acelaşi tip de dată: de exemplu, SMALLINT.
Cei trei operatori de mulţimi sunt numiţi, în standardul ISO, UNION, INTERSECT şi EXCEPT. Formatul
clauzei operatorului de mulţimi este, în fiecare caz:
operator [ALL][CORRESPONDING [BY {coloană [,…]}]]
Dacă CORRESPONDING BY este specificat, atunci operaţia cu mulţimi este realizată prin coloana (coloanele)
specificate. Dacă CORRESPONDING este specificat dar fără clauza BY, operaţia cu mulţimi este realizată prin
coloanele care sunt comune ambelor tabele. Dacă ALL este specificat, rezultatul poate include linii duble.
11
Unele dialecte ale SQL nu acceptă INTERSECT şi EXCEPT, altele folosesc MINUS în locul lui EXCEPT.
Exemple:
1. Utilizarea operatorului UNION:
(SELECT denumire AS oraşe
FROM oraşe
WHERE denumire IS NOT NULL)
UNION
(SELECT denum_m AS oraşe
FROM municipii
WHERE denum_m IS NOT NULL)

oraşe
Braşov
Codlea
Timişoara
Iaşi

2. Utilizarea operatorului INTERSECT:

(SELECT denumire AS oraşe


FROM oraşe)
INTERSECT
(SELECT denum_m AS oraşe
FROM municipii)

oraşe
Braşov
Timişoara
3. Utilizarea operatorului EXEPT:

(SELECT denumire AS oraşe


FROM oraşe)
EXEPT
(SELECT denum_m
FROM municipii);

oraşe
Codlea

1.2.2. Actualizările bazelor de date


SQL este un limbaj complet de manipulare a datelor care poate fi folosit pentru modificarea datei în baza de date
precum şi interogarea bazei de date. Comenzile pentru modificarea bazei de date nu sunt aşa complexe ca
declaraţie SELECT. În această secţiune, vom descrie cele 3 declaraţii SQL care sunt disponibile pentru a
modifica conţinuturile tabelelor în baza de date:
• INSERT se agaugă noi linii de date într-un tabel
• UPDATE se modifică data existentă într-un tabel
• DELETE se mută linii de date dintr-un tabel

Adăugarea datei în baza de date (INSERT)

Există două forme a declaraţiei INSERT. Prima formă permite inserarea unei singure linii într-un tabel
specificat. Formatul declaraţiei INSERT este:

INSERT INTO nume_tabel [(listă_coloană)]


VALUES (lista_valoare_dată)

nume_tabel poate fi un tabel de bază sau un view actualizabil şi lista_coloană reprezintă o listă a unuia sau mai
multor nume coloane separate prin virgulă. Lista _coloană este opţională, dacă este omisă, SQL presupune o
listă a tuturor coloanelor în ordinea lor originală din CREATE TABLE. Dacă este specificată, atunci orice
coloană care este omisă din listă trebuie să fie declarată ca şi coloană NULL cănd tabelul a fost creat, în cazul în
12
care opţiunea DEFAULT nu a fost folosită când s-a creat coloana. Lista_valoare_dată trebuie să se potrivească
cu lista coloană după cum urmează :
• Numărul elementelor din ambele liste trebuie să fie acelaşi
• Trebuie să fie o corespondentă directă între poziţia elementelor din ambele liste, astfel încât primul
element din lista_valoare_dată corespunde primului element din lista_coloană, al doilea element din
lista_valoare_dată corespunde celui de-al doilea element din lista_coloană, ş.a.m.d.
• Tipul datei fiecărui element din lista_valoare_dată trebuie să fie compatibil cu tipul datei coloanei
corespunzătoare.
Exemplu:

INSERT INTO oraşe


VALUES (‘O5’, ‘Deva’);

A doua formă a declaraţiei INSERT permite copierea mai multor linii din una sau mai multe tabele în altele.
Formatul este:

INSERT INTO nume_tabel [(lista_coloană)]


SELECT…

Nume_tabel şi lista_coloană sunt definite ca mai sus în cazul încasării unei singure linii. Clauza SELECT poate
fi orice declaraţie valabilă SELECT. Liniile inserate în tabelul specificat sunt identice cu cele din tabelul rezultat
produs de subselect. Aceleaşi restricţii aplicate primei forme a declaraţiei INSERT se aplică şi aici.
Exemplu:
INSERT INTO oraşe
(SELECT cod_m, denum_m
FROM municipii);

Modificarea datei în baza de date (UPDATE)

Declaraţia UPDATE permite schimbarea conţinutului liniilor existente dintr-un tabel specificat. Formatul
comenzii este:

UPDATE nume_tabel
SET nume_coloană 1=valoare_dată 1[, nume_coloană 2= valoare_dată 2…]
[WHERE condiţie_căutare]

Nume_tabel poate fi numele unui tabel de bază sau un view actualizabil. Clauza SET specifică numele unei sau
mai multor coloane care urmează să fie actualizate. Clauza WHERE este opţională. Dacă este omisă, coloana
specificată este actualizată pentru toate liniile din tabel. Dacă clauza WHERE este specificată, numai acele linii
care satisfac condiţia_căutare sunt actualizate. Noile valoare_dată trebuie să fie compatibile cu tipul de dată
pentru coloana corespunzătoare.
Exemplu:
UPDATE cadre_didactice
SET funcţia =’conf.’
WHERE funcţia=’lect.’;
Ştergerea datei din baza de date (DELETE)

Declaraţia DELETE permite ştergerea liniilor dintr-un tabel specificat. Formatul comenzii este:

DELETE FROM nume_tabel


[WHERE condiţia_căutare]
Ca şi în declaraţiile INSERT şi UPDATE, nume_tabel poate fi numele unui tabel de bază şi un view actualizabil.
Condiţia_căutare este opţional, dacă este omisă, toate liniile sunt şterse din tabel. Acesta nu şterge tabelul, dacă
se doreşte ştergerea conţinutul tabelului şi definiţia tabelului, trebuie folosită, în schimb, declaraţia DROP
TABLE. Dacă condiţia_căutare este specificată, numai acele linii care satisfac condiţia sunt şterse.
Exemplu:
DELETE FROM cadre_didactice
WHERE funcţia=’lect.’;

1.3. SQL - limbaj de definire a datelor (LDD)

13
Limbajul de definire a datei SQL (LDD) ne permite crearea şi distrugerea obiectelor bază de dată (scheme,
domenii, tabele, view-uri, rapoarte şi indexuri). În această secţiune, examinăm, pe scurt, cum să creăm şi să
distrugem scheme, tabele şi indexuri.
Principalele declaraţii de definirii datei în SQL sunt:
CREATE SCHEMA DROP SCHEMA
CREATE DOMAIN ALTER DOMAIN DROP DOMAIN
CREATE TABLE ALTER TABLE DROP TABLE
CREATE VIEW DROP VIEW
Deasemenea, următoarele două declaraţii au fost prevăzute de multe SGBD:
CREATE INDEX DROP INDEX
Aceste declaraţii sunt folosite pentru a crea, schimba sau distruge structurile care alcătuiesc schema conceptuală.
Înainte de a considera declaraţiile LDD, discutăm sintaxa identificatorilor SQL şi tipurile de date SQL care pot fi
folosite în definirea coloanelor tabel.

1.3.1. Identificatorii SQL


Identificatorii SQL sunt folosiţi pentru a identifica obiectele în baza de date, cum ar fi numele tabelelor, numele
rapoartelor şi coloanele. Caracterele care pot fi folosite într-un identificator SQL definit de utilizator trebuie să
apara într-un set de caractere. Standardul ISO prevede următorul set de caractere, care conţine literele mari
A…Z, literele mici a…z, cifrele 0…9 şi caracterele subliniate ( _ ). Este, de asemenea, posibilă specificarea unui
set de caractere alternative. Următoarele restricţii sunt impuse pe un identificator:
• Un identificator nu poate fi mai lung decât 128 de caractere (multe dialecte au o limită mai mică
decât aceasta).
• Un identificator trebuie să înceapă cu o literă.
• Un identificator nu poate conţine spaţii.

1.3.2. Tipurile de date ISO SQL


Există 6 tipuri de date scalare SQL definite în standardul ISO, prezentate în tabelul care urmează:

Tipul de dată Declaraţii


caracter CHAR, VARCHAR
bit BIT, BIT VARYING
exact numeric NUMERIC, DECIMAL, INTEGER, SMALLINT
aproximativ FLOAT, REAL, DOUBLE
numeric PRECISION
dată-timp DATE, TIME, TIMESTAMP
interval INTERVAL,

Uneori pentru scopuri de manipulare şi conversie, tipurile de date caracter şi bit sunt atribuite tipurilor de date
string, şi exact numeric şi aproximativ numerice sunt atribuite tipurilor de date numeric, deoarece ele împart
proprietăţisimilare.

Datele caracter

Datele caracter conţin o secvenţă de caractere dintr-un set de caractere definite de implementator, adică este
definită de către distribuitorul dialectului particular SQL. Astfel, caracterele exacte care pot apărea ca valorile
datelor într-o coloană tip caracter vor varia. ASCII este una din seturile utilizate în comun astăzi. Formatul
pentru specificarea unui tip de dată caracter este:

CHARACTER [VARYING][length]
CHARACTER can be abbreviated to CHAR and
CHARACTER VARYING to VARCHAR.

Când o coloană caracter string este definită, o lungime poate fi specificată să indice maximul numărului de
caractere conţinute în coloane (lungimiea implicită este 1). Un caracter string poate fi definită ca avînd o
lungime fixă sau variabilă. Dacă stringul este definit cu lungime fixă, şi dacă introducem un string cu câteva
caractere mai puţin decât această lungime, stringul este completat cu spaţii la dreapta pentru a obţine lungimea
cerută. Dacă stringul este definit cu lungime variabilă şi introducem un string cu câteva caractere mai puţin decât
această lungime, numai acele caractere introduse sunt rezervate, astfel folosindu-se mai puţine spaţii. De
14
exemplu, numărul matricol din coloana nr_mat a tabelului cadre_didactice, care are o lungime fixă de 4
caractere, este declarat ca:
nr_mat CHAR(4)
Coloana nume a tabelului cadre_didactice, care are un număr variabil de caractere până la maximum de 20, este
declarată ca:
nume VARCHAR(20)

Datele bit

Tipul de date bit este folosit pentru definirea stringurile bit, adică o secvenţă de cifre binare, fiecare avînd sau
valoarea 0 sau 1. Formatul pentru specificarea tipului de dată bit este similar cu cel al tipului de dată caracter:

BIT [VARYING][lenght]

De exemplu, pentru a ţine lungimea fixă a stringului binar ‘0011’, declarăm o coloană bit_string, ca:
bit_string BIT(4)

Datele exact numerice

Tipul de dată exact numeric este folosit pentru definirea numerelor cu reprezentare exactă. Numărul conţine
cifre, un punct zecimal opţional şi un semn opţional. Un tip de dată exact numeric conţine o precizie şi o scală.
Precizia dă numărul total a cifrelor zecimale semnificative: adică numărul total al cifrelor, incluzînd zecimalele
dar excluzînd punctul. Scala dă numărul total al zecimalelor. De exemplu, valoarea exact numerică -12.345 are
precizia 5 şi scala 3. Un caz special survine în cazul datelor exact numerice întregi. Există mai multe moduri de a
specifica un tip de dată exact numeric:

NUMERIC [precision [, scale]]


DECIMAL [precision [, scale]]
INTEGER
SMALLINT

INTEGER poate fi prescurtat prin INT şi DECIMAL prin DEC.


NUMERIC şi DECIMAL generează numere în scriere zecimală. Valoarea implicită a scalei este întotdeauna 0,
iar precizia implicită este definită de limbaj. INTEGER este folosit pentru numere mari pozitive sau negative
întregi. SMALLINT este folosit pentru numere mici pozitive sau negative întregi.
Specificînd acest tip de dată, pot fi rezervate mai puţine spaţii de stocare pentru dată. De exemplu, valoarea
absolută maximă care poate fi stocată în acest tip de dată poate fi 32767. Coloana vechime a tabelului
cadre_didactice, care reprezintă numărul anilor , este evident un întreg mic şi poate fi declarată ca:
vechime SMALLINT
Coloana salariu a tabelului cadre_didactice poate fi declarată ca:
salariu DECIMAL(9,2)
care poate manipula o valoare până la 9,999,999.99.

Datele aproximativ numerice

Tipul de dată aproximativ numeric este folosit pentru definirea numerelor care nu au o reprezentare exactă, ca de
pildă numerele reale. Aproximativ numeric, sau punct float, este similar notaţiei stiinţifice în care un număr este
scris ca o putere de zece. De exemplu, 10E3, +5.2E6, -0.2E-4. Există mai multe moduri de a specifica un tip de
dată aproximativ numeric:

FLOAT [precision]
REAL
DOUBLE PRECISION

Datele dată-timp

Acest tip de dată este folosit pentru a defini puncte în timp la un anumit grad de precizie. Ca exemple putem da
date calendaristice, intervale de timp şi intervale ale unei zile. Standardul ISO foloseşte pentru acest tip de dată
cuvintele rezervate: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, TIMEZONE_HOUR şi
TIMEZONE_MINUTE. Ultimele două câmpuri specifică ora şi minutul pentru anumite zone geografice.
Trei tipuri ale acestui tip de date sunt acceptate:
DATE
TIME [time_precision][WITH TIME ZONE]
15
TIMESTAMP [time_precision][ WITH TIME ZONE]

DATE este folosit pentru a reprezenta datele calendaristice folosind cuvintele rezervate YEAR, MONTH şi
DAY.
TIME este folosit pentru a reprezenta timpul folosind cuvintele rezervate HOUR, MINUTE şi SECOND.
TIMESTAMP reprezintă datele calendaristice şi timpul.
Time_precision reprezintă unitatea de măsură a câmpului SECOND. Dacă nu este specificat acesta este implicit
0 în TIME fiind vorba de secunde, iar în TIMESTAMP valoarea implicită este 6 fiind vorba de microsecunde.
Cuvântul cheie WITH TIME ZONE controlează prezenţa câmpurilor TIMEZONE_HOUR şi
TIMEZONE_MINUTE.

Date interval

Tipul de dată interval este folosit pentru a reprezenta intervale de timp. Fiecare tip de dată interval este alcătuit
dintr-un subset învecinat al câmpurilor: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND. Există două clase
de date interval: intervale an-lună şi intervale zi-timp. Clasa an-lună poate conţine numai câmpurile YEAR şi/sau
MONTH, iar clasa zi-timp poate conţine o selecţie invecinată din DAY, HOUR, MINUTE, SECOND.
Formatul pentru specificarea tipului da dată interval este:

INTERVAL {{start_câmp TO sfârşit_câmp} | câmp_dată-timp}


start_câmp=YEAR|MONTH|DAY|HOUR|MINUTE
[(precizia câmpului principal al intervalului)]
sfârşit_câmp=YEAR| MONTH|DAY|HOUR|MINUTE|SECOND
[(precizia zecimalelor câmpului secundar al intervalului)]
câmp_dată-timp=start_câmp|SECOND
[(precizia câmpului principal al intervalului [,precizia zecimalelor
câmpului secundar al intervalului])]

În toate cazurile precizia implicită a câmpului start_câmp este 2.


Exemplu:
INTERVAL YEAR(2) TO MONTH reprezintă un interval de timp cu o valoare între 0 ani 0 luni şi 99 ani 11
luni.
INTERVAL HOUR TO SECOND(4) reprezintă un interval de timp cu o valoare între 0 ore 0 minute 0 secunde
şi 99 ore 59 minute 59.9999 secunde.

1.3.3. Crearea unei baze de date


Procesul de creare a bazei de date diferă semnificativ de la produs la produs. Conform standardului ISO, relaţiile
şi alte obiecte ale bazei de date există într-un mediu. Printre altele, fiecare mediu este alcătuit dintr-unul sau mai
multe cataloage şi fiecare catalog este alcătuit dintr-un set de scheme. O schemă este o colecţie specificată a
obiectelor bazei de date care sunt legate una de alta într-un anumit mod (toate obiectele din baza de date sunt
descrise într-o schemă sau alta). Obiectele dintr-o schemă pot fi tabele, view-uri, domenii, comparaţii, translaţii
şi seturi de caractere. Toate obiectele dintr-o schemă au acelaşi proprietar şi împart un număr de valori implicite.
Standardul ISO defineşte prin implementare mecanismul pentru crearea şi distrugerea cataloagelor şi furnizează
mecanisme pentru crearea şi distrugerea schemelor. Declaraţia de definire a schemei are forma (simplificată)
următoare:

CREATE SCHEMA [nume | AUTHORIZATION creator-identificator]

De aceea, dacă creatorul unei scheme “sql-schemă” este Ionescu, declaraţia SQL este:

CREATE SCHEMA sql-schemă AUTHORIZATION Ionescu;

O schemă poate fi distrusă folosind declaraţia DROP SCHEMA, care are forma următoare:

DROP SCHEMA nume [RESTRICT | CASCADE]

Dacă RESTRICT este specificat, care este şi valoarea implicită dacă nici unul din calificative nu este specificat,
schema trebuie să fie vidă sau operaţia eşuează. Dacă CASCADE este specificat, operaţia aranjează în cascadă
toată obiectele asociate schemei în ordinea definită înainte.

16
În prezent declaraţiile CREATE şi DROP SCHEMA nu sunt încă larg implementate. În unele implementări, în
locul declaraţiei CREATE SCHEMA este folosită declaraţia următoare:

CREATE DATABASE nume_bază-date.

Crearea unui tabel (CREATE TABLE)

Avînd creată structura bazei de dată, putem acum crea structurile tabelelor de bază a relaţiilor pentru a fi
localizate în baza de date. Crearea unui tabel se face folosind comanda CREATE TABLE, care are următoarea
sintaxă principală:
CREATE TABLE nume_tabel
(nume_coloană tip_dată [NULL | NOT NULL][,…])

Această comandă crează un tabel numit nume_tabel alcătuit din una sau mai multe coloane al câmpului tip_dată
specificat. Specificatorul NULL este folosit pentru a indica dacă unei coloane îi este permis sau nu să conţină
null-uri. Un null este diferit de spaţiu sau 0 şi este folosit pentru a reprezenta date dacă acestea nu sunt
disponibile, pierdute sau nu sunt aplicabile. Când NOT NULL este specificat, sistemul respinge orice încercare
de inserare a unui null în coloane. Dacă NULL este specificat, sistemul acceptă null-uri. Valoarea implicită ISO
este NULL. De exemplu, pentru crearea tabelului cadre_didactice avem:

CREATE TABEL cadre_didactice(


nr_mat CHAR(4) NOT NULL,
nume CHAR(15) NOT NULL,
prenume CHAR(15) NOT NULL,
funcţia CHAR(10)
vechime SMALLINT );

Distrugerea unui tabel (DROP TABLE)

Cu timpul structura unei baze de date se va schimba, tabele noi fiind create iar altele vechi nu vor mai fi
necesare. Putem şterge un tabel suplimentar din baza de date folosind declaraţia DROP TABLE, care are
formatul:

DROP TABLE nume_tabel [RESTRICT | CASCADE]

De exemplu pentru a distruge tabelul cadre_didactice folosim comanda:

DROP TABLE cadre_didactice;

Observăm că această comandă şterge nu numai tabelul specificat, ci şi toate liniile din interiorul acesteia. Dacă
se doreşte ştergerea liniilor din tabel dar să se reţină structura tabelului trebuie folosită declaraţia DELETE.
Dacă RESTRICT este specificat în comanda DROP TABLE şi există orice alte obiecte a căror existenţă depinde
de existenţa tabelului, asupra căruia acţionează comanda, SQL nu permite comenzii DROP TABLE să continue.
Dacă CASCADE este specificat SQL automat şterge toate obiectele dependente de tabel (şi obiectele dependente
de aceste obiecte).
O folosire uzuală a comenzii DROP TABLE este de a corecta greşelile făcute la crearea unui tabel. Dacă este
creat un tabel cu o structură incorectă se poate folosi DROP TABLE pentru a şterge tabelul nou creat şi începe
din nou.

Modificarea structurii unui tabel (ALTER TABLE)

Standardul ISO furnizează declaraţia ALTER TABLE pentru modificarea structurii unui tabel odată ce a fost
creat. Definirea declaraţiei ALTER TABLE în standardul ISO este alcătuită din 6 opţiuni pentru:
• a adăuga o nouă coloană la un tabel;
• a şterge o coloană dintr-un tabel ;
• a adăuga o nouă restricţie pentru tabel;
• a şterge o restricţie a tabelului;
• a stabilii o valoare implicită pentru o coloană;
• a şterge o valoare implicită a coloanei.

Formatul principal al declaraţiei este:

17
ALTER TABLE nume_tabel
[ADD [COLUMN] nume_coloană tip_dată [NOT NULL][UNIQUE]
[DEFAULT opţiune_implicită][CHECK(condiţie_căutare)]]
[DROP [COLUMN] nume_coloană [RESTRICT | CASCADE]]
[ADD [CONSTRAINT [nume_restricţie]] definiţie_restricţie_tabel]
[DROP CONSTRAINT nume_restricţie [RESTRICT | CASCADE]]
[ALTER [COLUMN] SET DEFAULT opţiune_implicită]
[ALTER [COLUMN] DROP DEFAULT]

unde parametrii sunt definiţi ca şi în declaraţia CREATE TABLE. Definiţie_restricţie_tabel este una din
clauzele: PRIMARY KEY, UNIQUE, FOREIGN KEY sau CHECK. Clauza ADD COLUMN este similară cu
definirea unei coloane din declaraţia CREATE TABLE. Clauza DROP COLUMN specifică numele coloanei ce
trebuie ştearsă din definiţia tabelului. Această clauză are un calificativ opţional care ne permite să specificăm
dacă acţiunea DROP este în cascadă sau nu. Avem acelaş concept ca şi la calificativul RESTRICT | CASCADE
a declaraţiei DROP TABLE.
De exemplu pentru a adăuga coloana “vechime” la tabelul cadre_didactice folosim comanda:
ALTER TABLE cadre_didactice
ADD vechime INTEGER(2);

Declaraţia ALTER TABLE nu este disponibilă în toate dialectele SQL. În unele dialecte, declaraţia nu poate fi
folosită pentru a muta o coloană existentă dintr-un tabel.

Crearea unui index (CREATE INDEX)

Un index este o structură care furnizează accesul rapid la liniile unui tabel bazat pe valorile uneia sau mai multor
coloane. Prezenţa unui index poate îmbunătăţi simţitor performanţa unei interogări. Indexurile sunt create, de
obicei, pentru a satisface criteriul de căutare particular după ce tabelul a fost folosit un timp şi a crescut în
mărime. Crearea indexurilor nu este standardizată în SQL. Totuşi multe dialecte acceptă cel puţin următoarea
formă a acestei comenzi:

CREATE [UNIQUE] INDEX nume_index


ON nume_tabel (colonă [ASC |DESC] [,…])

Coloanele specificate alcătuiesc cheia index şi ar trebui listate în ordine descrescătoare. Indexurile pot fi create
numai în tabele de bază nu şi în view-uri. Dacă clauza UNIQUE este folosită, unicitatea coloanei sau combinaţiei
de coloane indexate va fi decretată de către sistem. Deşi indexurile pot fi create oricând, putem avea probleme
dacă încercăm să creăm un index unic pe un tabel care conţine articole, deoarece valorile provenite din coloana
(coloanele) indexată poate să conţină deja dubluri. De aceea este mai bine să creăm indexuri unice, cel puţin
pentru coloane cheie principale, atunci când tabelul de bază este creat şi sistemul nu va asigura automat
unicitatea cheii principale.
Pentru tabelul cadre_didactice putem crea următoarele indexuri:

CREATE UNIQUE INDEX matricol_ind ON cadre_didactice(nr_mat);


CREATE UNIQUE INDEX nume_ind ON cadre_didactice(nume);

Pentru fiecare coloană putem specifica dacă ordinea este ascendentă (ASC) sau descendentă (DESC), valoarea
implicită fiind ASC.

Ştergerea unui index (DROP INDEX)

Dacă creăm un index pentru un tabel de bază şi mai târziu decidem că nu mai este necesar, declaraţia DROP
INDEX şterge indexul din baza de date. DROP INDEX are formatul:
DROP INDEX nume_index
Declaraţia următoare va şterge indexul creat în exemplul anterior:
DROP INDEX matricol_ind;

1.4. SQL avansat. View-uri


Un view este rezultatul dinamic a uneia sau mai multor operaţii relaţionale care operează pe relaţii de bază
pentru a produce altă relaţie. Un view este o relaţie virtuală care nu există, de fapt, în baza de date, dar este
produsă pe baza cerinţei unui utilizator particular.

18
1.4.1. Crearea unui view (CREATE VIEW)
Formatul declaraţiei CREATE VIEW este:

CREATE VIEW nume_view [(nume_coloană [,…])]


AS subselect [WITH [CASCADED | LOCAL] CHECK OPTION]

Se poate repartiza, opţional, un nume pentru fiecare coloană din view. Dacă o listă a numelor de coloană este
specificată, atunci trebuie să aibă acelaşi număr de elemente ca şi numărul coloanelor produse de către subselect.
Dacă lista numelor de coloană este omisă, fiecare coloană din view ia numele coloanei corespunzătoare din
subselect. Lista numelor de coloană trebuie să fie specificată în cazul în care există orice ambiguitate în numele
unei coloane. Aceasta se poate întâmpla dacă subselectul include coloane calculate şi subclauza AS nu a fost
folosită pentru a numi astfel de coloane sau produce două coloane cu nume identice ca rezultat a unei joncţiuni.
Subselectul este cunoscut ca interogare definită. Dacă WITH CHECK OPTION este specificat, SQL asigură
faptul că dacă o linie nu ajută la satisfacerea clauzei WHERE a interogării definite a view-ului, aceasta nu este
adăugată la tabelul view-ului.
Deşi toate view-urile sunt create în acelaşi mod, în practică diferite tipuri de view-uri sunt folosite pentru diferite
scopuri:
1. Wiew orizontal, care pemite restricţionarea accesului utilizatorilor la anumite linii ale unui tabel:

CREATE VIEW vechime_cadre


AS SELECT *
FROM cadre_didactice
WHERE vechime >= 25;

nr_mat nume prenume funcţia vechime salariu


1113 Barbu Traian conf. 25 900,000
1114 Ştefan Elena conf. 25 900,000
1115 Savu Alexandru conf. 28 950,000

2. View vertical care pemite restricţionarea accesului utilizatorilor la anumite coloane ale unui tabel ale unui
tabel:

CREATE VIEW cadre_lect


AS SELECT nr_mat, nume, prenume
FROM cadre_didactice
WHERE funcţia = ‘lect.’;

nr_mat nume prenume


1111 Popa Ioan
1116 Ionescu Mircea

3. View grupat şi view joncţionat:

CREATE VIEW secţii_grupe (secţia, grupa)


AS SELECT s.denumire, g.cod_gr
FROM secţii s, grupe g
WHERE s.cod_sec = g.cod_sec;
GROUP BY s.denumire;

secţia grupa
Matematică 1
Matematică 2
Informatică 1
Matematică-fizică 1
Matematică-fizică 2
Fizică-chimie 1

19
1.4.2. Ştergerea unui view (DROP VIEW)
Un view este şters din baza de date cu declaraţia:

DROP VIEW nume_view [RESTRICT | CASCADE];

Dacă CASCADE este specificat, DROP VIEW şterge toate obiectele dependente, cu alte cuvinte, şterge toate
obiectele de referinţă ale view-ului. Dacă RESTRICT este specificat şi există orice alte obiecte a căror existenţă
depinde de existenţa view-ului ce urmează a fi şters, comanda este respinsă. Valoarea imlicită este RESTRICT.
De exemplu, putem şterge view-ul vechime_cadre folosind declaraţia:
DROP VIEW vechime_cadre;

1.4.3. Restricţii asupra view-urilor


Standardul ISO impune câteva restricţii importante în crearea şi folosirea view-urilor, deşi există variaţii
considerabile între dialecte.
• Dacă o coloană din view se bazează pe o funcţie complexă, atunci coloana poate apărea numai în
clauzele SELECT şi ORDER BY ale interogării care accesează view-ul. În particular, o astfel de coloană nu
poate fi folosită într-o clauză WHERE şi nu poate fi folosită ca argument pentru o funcţie complexă în orice
interogare bazată pe view;
• Un view grupat nu poate fi joncţionat cu un tabel sau un view.

1.4.4. Actualizabilitatea view-urilor


Toate actualizările unui tabel sunt imediat reflectate în toate view-urile care cuprind acel tabel. Analog, putem să
ne aşteptăm la faptul că dacă un view este actualizat atunci tabelul (tabelele) va reflecta acea schimbare.
Standardul ISO specifică view-urile care trebuie să fie actualizabile într-un sistem corespunzător standardului.
Definiţia dată în standardul ISO este că un view este actualizabil dacă şi numai dacă:
• DISTINCT nu este specificat, adică liniile duble nu trebuie eliminate din rezultatele interogării.
• Fiecare element din lista SELECT a interogării definitorii este un nume de coloană (mai degrabă
decât o constantă, o expresie sau o funcţie complexă) şi nici o coloană nu apare mai mult decât o dată.
• Clauza FROM specifică numai un tabel, adică view-ul trebuie să aibă un singur tabel sursă asupra
căruia utilizatorul are privilegiile cerute. Dacă tabelul sursă însăşi este un view, atunci acest view trebuie să
satisfacă aceste condiţii. De aceea, aceasta exclude orice view-uri bazate pe o joncţiune, reuniune (UNION),
intersecţie (INTERSECT) şi diferenţă (EXCEPT).
• Clauza WHERE nu include nici un SELECT intercalat care se referă la tabelul din clauza FROM.
• Nu există clauzele GROUP BY şi HAVING în interogarea definitorie.
În plus, fiecare linie adăugată prin view nu trebuie să încalce restricţiile de integritate ale tabelului. Cu alte
cuvinte, SGBD-ul trebuie să poată să copieze orice linie sau coloană înapoi în linia sau coloana sa din tabelul
sursă.

1.4.5. Avantaje şi dezavantaje ale view-urilor

Avantaje.
1. Independenţa datei
Un view poate prezenta o imagine consistentă, nemodificabilă a structurii bazei de date, chiar dacă tabelele sursă
principale sunt schimbate (de exemplu, coloane adăugate sau şterse, relaţii schimbate, tabele dezbinate,
restructurate sau redenumite). Dacă coloanele sunt adăugate sau şterse dintr-un tabel şi aceste coloane nu sunt
cerute de către view, atunci definirea view-ului nu necesită schimbări. Dacă un tabel existent este rearanjat sau
dezbinat, un view poate fi definit astfel încât utilizatorul poate continua să vadă vechiul tabel. În cazul dezbinării
unui tabel, vechiul tabel poate fi recreat prin definirea unui view joncţionînd noile tabele.
2. Universalitatea
Schimbările oricăror tabele în interogarea definită sunt imediat reflectate în view.
3. Securitatea

20
Fiecare utilizator poate avea privilegiul de a accesa baze de date numai printr-un mic set de view-uri, care
conţine date potrivite acelui utilizator, astfel restrângînd şi controlînd accesul la baza de date a fiecărui utilizator.
4. Complexitatea redusă
Un view poate simplifica interogările prezentarea datelor din mai multe tabele într-un singur tabel şi, astfel,
transformînd interogări multi-tabel în interogări 1-tabel.
5. Selectabilitatea
View-urile furnizează posibilitatea ca aceleaşi tabele principale să poată fi văzute de diferiţi utilizatori, în diferite
moduri.
6. Integritatea datei
Dacă WITH CHECK OPTION a declaraţiei CREATE VIEW este folosit, SQL asigură faptul că nici o linie care
nu satisface clauza WHERE a interogării definite nu este niciodată adăugată la orice tabel principal prin view,
prin aceasta asigurînd integritatea view-ului.

Dezavantaje.
Deşi view-urile furnizează multe beneficii semnificative, există de asemenea, câteva dezavantaje ale view-urilor
SQL:
1. Restricţia actualizării
În unele cazuri un view nu poate fi actualizat.
2. Restricţia structurii
Structura unui view este determinată la timpil creării lui. Dacă interogarea definită a fost de forma SELECT *
FROM …, atunci * se referă la coloanele tabelului de bază existent când a fost creat view-ul. Dacă coloanele
sunt ulterior adăugate la tabelul de bază, atunci aceste coloane nu vor apărea în view, în afară de cazul când
view-ul este şters şi recreat.
3. Performanţa
Există un neajuns care apare când folosim un view. În unele cazuri acest lucru va fi neglijabil; în alte cazuri
poate fi mai problematic. De exemplu, un view definit printr-o interogare complexă multi-tabel poate lua un timp
îndelungat pentru a fi prelucrat pe când rezoluţia view-ului trebuie să joncţioneze tabelele de fiecare dată când
view-ul este accesat. Rezoluţia view-ului cere resurse în plus calculatorului.

21

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