Sunteți pe pagina 1din 10

Laborator 6

Subcereri

1. Obiective
In aceasta lucrare de laborator vom vedea cum se pot ingloba alte interogari in cererile
SQL. Elementele pe care le prezentam sunt urmatoarele:
Tipuri de subcereri
Subcereri care intorc o singura valoare
Subcereri care intorc o coloana
Subcereri care intorc un tabel
Operatori specifici

Pentru fiecare tip de subcerere vom analiza clauzele unei inteorgari SELECT in care pot
aparea.

2. Introducere
O interogare inglobata in alte cereri SQL se numeste o subcerere. Subcererea poate sa
includa la randul ei o alta subcerere.
Subcererile sunt de doua feluri: necorelate si corelate.
subcererea necorelata este o subcerere independenta de cererea principala. Se
executa o singura data in interiorul cererii principale.
subcererea corelata se bazeaza pe date ce sunt pasate de cererea principala. Se
executa pentru fiecare inregistrare returnata de cererea principala.
Executia unei cereri ce contine subcereri corelate dureaza mai mult, lucru vizibil mai ales
cand numarul inregistrarilor returnate de cererea principala este foarte mare.
Din punct de vedere al rezultatelor returate, subcererile se impart in trei categorii:
Subcereri care intorc o singura valoare
Subcereri care intorc o coloana
Subcereri care intorc un tabel

3. Subcereri care intorc o singura valoare
Subcererile care intorc o singura valoare pot aparea in urmatoarele clauze ale unei
instructiuni SELECT :

Subcereri in clauza SELECT - campuri calculate cu subcereri
O forma simpla de subcerere este aceea care returneaza cate un camp calculat pentru
fiecare inregistrare returnata de cererea principala:

SELECT CampA, (Subcerere) AS CampB FROM Tabel;

Interogarea urmatoare returneaza numarul de proiecte la care lucreaza fiecare angajat al
companiei:

SELECT A.AngajatID, A.Nume + A.Prenume as Nume, (
SELECT COUNT(*) FROM AngajatiProiecte AP
WHERE AP.AngajatID = A.AngajatID)
AS NumarProiecte
FROM Angajati A
In acest caz, subcererea (marcata cu bold) nu ar functiona pe cont propriu deoarece are
nevoie de date (AngajatID) din tabelul Angajati, provenite din cererea principala. Acesta este un
exemplu de subcerere corelata. Subcererea se executa pentru fiecare inregistrare din cererea
principala. In tabelul rezultat in urma executiei interogarii de mai sus, campul returnat de
subcerere se numeste NumarProiecte.

Subcereri in clauza WHERE
In forma cea mai simpla, sintaxa arata astfel:

SELECT CampA FROM TabelA
WHERE CampB operator (Subcerere);
Intrucat subcererea intoarce o singura valoare, operator poate fi: =, <, >, <=, >=, !=.
O situatie in care astfel de interogari sunt extrem de utile este aceea in care folosim date
dintr-o inregistrare drept criteriu de cautare pentru alte inregistrari.
Exemplu: interogarea urmatoare ne va arata persoanele care au fost angajate in companie
inaintea angajatului avand ID-ul 2.

SELECT AngajatID, DataAngajarii FROM Angajati
WHERE DataAngajarii <= (
SELECT DataAngajarii FROM Angajati WHERE AngajatID = 2)
ORDER BY DataAngajarii DESC;
In cazul de fata, subcererea returneaza o singura valoare, si anume DataAngajarii pentru
angajatul avand ID-ul 2.
Obs. Subcererea formeaza intotdeauna membrul drept al unei operatii de comparatie si
este cuprinsa intre paranteze. Membrul drept poate fi un camp sau chiar o constanta.

SELECT DataAngajarii FROM Angajati WHERE AngajatID = 2
Aceasta valoare este folosita de cererea principala, care va gasi toti angajatii care erau
angajati la acel moment. Rezultatul este ordonat descrescator dupa DataAngajarii.
In acest exemplu, ambele interogari lucreaza independent asupra acelorasi date.
Subcererea nu depinde de date provenite din cererea principala pentru a fi executata. Acesta este
un exemplu de cererea necorelata. Subcererea este executata o singura data, iar rezultatul este
utilizat in cererea principala.
In exemplul anterior, atat cererea principala cat si subcererea opereaza asupra unui tabel,
insa in cererea principala si in subcerere se pot invoca tabele diferite.

Subcereri in clauza HAVING
In forma cea mai simpla, sintaxa arata astfel:

SELECT CampA FROM TabelA
WHERE Conditie
GROUP BY CampB
HAVING functie_agregat(CampC) operator (Subcerere)

Intrucat subcererea intoarce o singura valoare, operator poate fi: =, <, >, <=, >=, !=.
O astfel de interogare este folosita atunci cand functiile statistice calculate pentru anumite
grupuri de date trebuie sa indeplineasca anumite conditii care implica alte inregistrari din
tabelele bazei de date.
Exemplu: cu interogarea urmatoare se vor determina departamentele companiei pentru
care media salariilor angajatilor este mai mare decat media salariului pe intreaga companie.

SELECT D.DepartamentID
FROM Angajati A, Departamente D
WHERE A.DepartamentID = D.DepartamentID
GROUP BY D.DepartamentID
HAVING AVG(A.Salariu) > (SELECT AVG(Salariu) FROM Angajati)
Aici, subcererea necorelata returneaza o singura valoare, si anume media salariilor pe
intreaga companie: SELECT AVG(Salariu) FROM Angajati. Aceasta valoare este folosita de
cererea principala, care va gasi departamentele ale caror angajati au o medie a salariului mai
mare media pe companie.

Subcereri in clauza ORDER BY
In forma cea mai simpla, sintaxa arata astfel:

SELECT CampA FROM TabelA
WHERE Conditie
ORDER BY (Subcerere)[ASC|DESC]

O astfel de interogare este folosita atunci cand se doreste ordonarea rezultatelor din
cererea principala in functie de diverse criterii pe care le indeplinesc datele din baza de date.
Exemplu: interogarea urmatoare va obtine lista angajatilor companiei, ordonata
descrescator dupa media numarului de ore pe care le lucreaza saptamanal la proiecte.

SELECT A.Nume, A.Prenume
FROM Angajati A
ORDER BY (SELECT AVG(AP.NrOreSaptamana)from AngajatiProiecte AP
WHERE A.AngajatID = AP.AngajatID)DESC;
Nota: Subcererile care intorc o singura valoare sunt folosite numai impreuna cu operatori
de comparatie monolinie (<, >, =, >=, <=, !=).
Exemplu: Tiparim numele angajatului care lucreaza 23 ore pe saptamana la un proiect
derulat in cadrul companiei.

SELECT A.Nume FROM Angajati A
WHERE AngajatID =
(SELECT AngajatID from AngajatiProiecte AP
WHERE AP.NrOreSaptamana = 23);

Rezultatul arata astfel:
Nume
----------------
Popescu

Interogarea anterioara functioneaza numai daca stim sigur ca rezultatul subcererii
are o singura inregistrare! In acest caz, trebuie sa stim sigur ca avem un singur
angajat care lucreaza 23 ore/sapt la un proiect.

Daca subcererea returneaza mai multe inregistrari, interogarea de mai sus va esua:

SELECT A.Nume FROM Angajati A
WHERE AngajatID =
(SELECT AngajatID from AngajatiProiecte AP
WHERE AP.NrOreSaptamana = 40);

Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery
follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Cum rezolvam aceasta situatie?

4. Subcereri care intorc mai multe valori
Subcereri care intorc o coloana
Subcereri in clauza WHERE
In cazul in care subcererea returneaza mai multe valori (inregistrari), se folosesc
cuvintele cheie IN sau NOT IN pentru a verifica apartenenta valorii date de campul din membrul
stang la o multime:

SELECT CampA, Camp FROM Tabel1
WHERE CampC IN (SELECT CampD FROM Tabel2);

Deci, putem rescrie interogarea de mai sus astfel:
SELECT A.Nume
FROM Angajati A
WHERE AngajatID IN(
SELECT AngajatID from AngajatiProiecte AP
WHERE AP.NrOreSaptamana = 40
);
In clauza WHERE se verifica daca valoarea campului AngajatID se gaseste in multimea
de inregistrari produse de subcerere. Daca subcererea nu intoarce nici o inregistrare, evaluarea
este FALSE. Interogarea de mai sus poate fi scrisa fara subcerere, si din punct de vedere rezultat
este echivalenta cu:

SELECT A.Nume
FROM Angajati A, AngajatiProiecte AP
WHERE A.AngajatID = AP.AngajatID AND AP.NrOreSaptamana = 40
Exemple:
1. Sa se gaseasca angajatii care lucreaza la proiecte coordonate de departamentul 2.
Varianta 1:

SELECT A.Nume + ' ' + A.Prenume AS Angajat
FROM Angajati A, AngajatiProiecte AP
WHERE A.AngajatID = AP.AngajatID AND AP.ProiectID IN
(SELECT P.ProiectID FROM Proiecte P
WHERE P.DepartamentID = 2)

Obs: In Oracle, pentru concatenare se foloseste operatorul ||.

Varianta 2:

SELECT A.Nume, A.Prenume
FROM Angajati A, Proiecte P, AngajatiProiecte AP
WHERE ((A.AngajatID = AP.AngajatID) AND (P.DepartamentID = 2) AND
(AP.ProiectID = P.ProiectID))

Daca dorim eliminarea duplicatelor, adica in cazul existentei unui angajat care lucreaza la
3 proiecte, acesta sa apara o singura data (nu de 3 ori), folosim DISTINCT dupa SELECT.

2. Afisati numele si prenumele angajatilor care nu au supervizor.
Varianta 1:

SELECT A.Nume, A.Prenume
FROM Angajati A
WHERE A.SupervizorID IS NULL

Varianta 2:

SELECT A.AngajatID, A.Nume, A.Prenume
FROM Angajati A
WHERE A.AngajatID NOT IN
(SELECT A.AngajatID
FROM Angajati A, Angajati S
WHERE A.SupervizorID = S.AngajatID
)

Subcereri in clauza FROM
O alta clauza unde pot aparea subcereri care intorc o coloana este clauza FROM.
Exemplu: interogarea urmatoare afiseaza numele si prenumele angajatilor
departamentului cu ID-ul 2:

SELECT A.Nume, A.Prenume
FROM Angajati A, (SELECT A2.AngajatID
FROM Angajati A2
WHERE A2.DepartamentID = 2) AS AD
WHERE A.AngajatID = AD.AngajatID

Subcererea produce un tabel intermediar care contine ID-urile angajatilor
departamentului 2. Acest tabel va fi ulterior identificat prin aliasul AD, definirea unui alias la
orice subcerere in claza FROM fiind obligatorie.
Se obtin angajatii
care au
supervizori
Pentru subcererile care apar in clauza FROM, este de remarcat faptul ca nu pot fi corelate
cu cererea principala.
Nota: In alte sisteme de gestiune a bazelor de date este posibil ca subcererile care intorc o
coloana sa apara si in alte clauze in afara de FROM si WHERE.

Subcereri care intorc un tabel
Subcereri in clauza FROM
In Microsoft SQL Server 2005, subcererile care returneaza un tabel pot aparea doar in
clauza FROM.
Exemplu: Sa se obtina numele primilor 5 angajati care au salariul cel mai mare.
Rezultatul se va ordona descrescator dupa salariu.
SELECT I.Nume, I. Salariu
FROM (SELECT TOP 5 A.Nume as Nume, A.Prenume as Prenume, A.Salariu as
Salariu
FROM Angajati A
ORDER BY A.Salariu DESC ) as I
Nota: In alte sisteme de gestiune a bazelor de date este posibil ca subcererile care intorc
un tabel sa apara si in alte clauze in afara de FROM.

Alti operatori pentru subcereri care intorc mai multe valori
Pana acum am folosit subcereri pentru care am utilizat operatorul IN pentru a gasi
inregistrarile la care o anumita valoare se regaseste intr-o multime de valori. Pe langa acesta, mai
exista si alti operatori.
Operatorul EXISTS
Operatorul EXISTS prefixeaza o subcerere si verifica indeplinirea unei conditii specifice.
EXISTS este intotdeauna urmat de o subcerere si returneaza TRUE daca subcererea intoarce
macar o inregistrare. Operatorul EXISTS este frecvent folosit cu subcererile corelate, dar nu
numai.
Exemplu: urmatoarea interogare returneaza numele si ID-urile angajatilor care efectueaza
mai putin de 20 ore/saptamana macar la unul dintre proiectele la care lucreaza:

SELECT A.AngajatID, A.Nume, A.Prenume
FROM Angajati A
WHERE EXISTS (
SELECT AngajatID FROM AngajatiProiecte AP
WHERE AP.NrOreSaptamana < 20 AND A.AngajatID = AP.AngajatID);
Subcererea returneaza ID-urile angajatilor care lucreaza mai putin de 20 ore/saptamana
cel putin la un proiect:

SELECT AngajatID FROM AngajatiProiecte AP
WHERE AP.NrOreSaptamana < 20
Aceasta este o subcerere corelata, astfel ca trebuie sa fie executata pentru fiecare
inregistrare din cererea principala:
AND A.AngajatID = AP.AngajatID
Aceasta interogare se executa pentru fiecare rand din tabelul Angajati, verificand daca
pentru fiecare angajat exista proiecte la care lucreaza mai putin de 20 ore/saptamana.
Desi intr-o subcerere o operatie NOT IN poate fi la fel de eficienta ca si NOT EXISTS,
NOT EXISTS este mult mai sigur daca subcererea intoarce niste valori NULL, fata de NOT IN
pentru care conditia se evalueaza FALSE cand in lista de comparatii sunt incluse valori NULL.

Operatorul ALL
Pana acum am cautat inregistrari pentru care vaolarea dintr-un camp se regasea printre
valorile dintr-o multime. Dar cum procedam daca vrem sa gasim inregistrarile pentru care
valoarea unui anumit camp este mai mare sau mai mica decat toate valorile dintr-o multime? In
acest caz se poate utiliza operatorul ALL.
Operatorul ALL poate fi folosit pentru subcererile care intorc mai mult de o linie. El
poate aparea in clauzele WHERE sau HAVING, in conjunctie cu operatorii relationali (=, !=, <,
>, >=, <=).
Returneaza TRUE daca valoarea evaluata indeplineste conditia de egalitate/inegaliate
precizata pentru toate valorile returnate de subcerere:

SELECT CampA FROM TabelA
WHERE CampA operator ALL (SELECT CampB FROM TabelB);

Exemplu: Interogarea urmatoare returneaza angajatii care lucreaza la proiecte care au ID-
uri mai mari decat toate ID-urile proiectelor coordonate de departamentul cu ID-ul 1.

SELECT DISTINCT A.AngajatID, A.Nume, A.Prenume, AP.ProiectID
FROM Angajati A, AngajatiProiecte AP
WHERE AP.ProiectID > ALL
(SELECT P.ProiectID FROM Proiecte P WHERE P.DepartamentID =1)


Operatorul ANY
ANY lucreaza similar cu ALL, dar returneaza TRUE daca este indeplinita conditia macar
pentru o inregistrare din subcerere. Operatorul ANY poate fi folosit pentru subcererile care intorc
mai mult de o linie. El poate aparea in clauzele WHERE sau HAVING, in conjunctie cu
operatorii relationali (=, !=, <, >, >=, <=).

SELECT ColoanaA FROM TabelA
WHERE ColoanaA operator ANY (SELECT ColoanaB FROM TabelB);

Exemplu:

SELECT DISTINCT A.AngajatID, A.Nume, A.Prenume, AP.ProiectID
FROM Angajati A, AngajatiProiecte AP
WHERE AP.ProiectID > ANY
(SELECT P.ProiectID FROM Proiecte P WHERE P.DepartamentID =1)

Echivalente intre operatori

o IN este echivalent cu =ANY
o NOT IN este echivalent cu !=ALL
o >ANY este echivalent cu mai mare decat minimul
o < ANY este echivalent cu mai mic decat maximul
o >ALL este echivalent cu mai mare decat maximul
o < ALL este echivalent cu mai mic decat minimul


5. Exercitii
1. Gasiti angajatii care castiga cel mai mare salariu pentru fiecare departamet.Sortati in
ordinea descrescatoare a salariului.
2. Gasiti cei mai recenti angajati din fiecare departament.Ordonati dupa data angajarii.
3. Afisati numele, prenumele, salariul si numele departamentului la care lucreaza pentru
orice angajat care castiga un salariu mai mare decat media pentru departamentul din care face
parte.
4. Listati toate departamentele care nu au angajati (folositi o subcerere).
5. Afisati numele , prenumele si salariul primilor trei angajati in functie de salariul
castigat.
6. In ce an s-au angajat cei mai multi in companie ? Afisati anul si numarul angajatilor.
7. Aflati angajatii care au salariul mai mare decat vreun angajat al departamentului cu ID-
ul 2 si care nu fac parte din acest departament.
8. Gasiti angajatii care au salariul mai mare decat toti angajatii departamentului cu ID-ul
5.
9. Gasiti numele, functia, data angajarii si salariul angajatilor al caror salariu este mai
mare decat cel mai mare salariu al vreunei persoane angajate dupa data de 05-JAN-2000.
10. Gasiti angajatii care nu au subordonati.
11. Gasiti angajatii care au cel putin 2 persoane in intretinere.
12. Afisati numele si departamentul angajatilor care ii sunt subordonati angajatului cu
numele Popa.
13. Sa se afiseze numele, prenumele si salariul angajatului insotit de codul managerului
sau, pentru angajatii al caror salariu este mai mic de 1500 lei.
14. Sa se afiseze numele salariatilor care lucreaza intr-un departament in care exista cel
puin un angajat cu salariul cu salariul mai mare de 2000 lei.
15. Sa se afiseze numele si salariul angajatilor al caror salariu este mai mare decat
salariile medii din toate departamentele.