INTEROGĂRI ÎN SQL SERVER Principala operaţie efectuată într-o bază de date este operaţia de extragere a datelor, care se realizează cu ajutorul unei clauze SELECT. SELECT Clauza SELECT are o sintaxă foarte complexă, dar, pe scurt, se poate folosi următoarea: SELECT [DISTINCT] lista_coloane [ INTO tabel_nou ] FROM lista_tabele [ WHERE predicat ] [ GROUP BY lista_coloane ] [ HAVING predicat ] [ ORDER BY lista_coloane [ ASC DESC ] ] SQL pune la dispoziţie condiţii de joncţiune ce permit obţinerea de date din tabele diferite. Se pot folosi următoarele: CROSS JOIN Este echivalentul din standardul ANSI/ISO SQL: 1999 SQL pentru calcularea unui produs cartezian. Seturile de rezultate reprezintă toate combinaţiile posibile de coloane din ambele tabele. FROM note CROSS JOIN student INNER JOIN Permite folosirea de condiţii la joncţiune. Folosind clauza ON se permite folosirea clauzei WHERE pentru a restrânge numărul de rânduri obţinute. FROM note INNER JOIN student ON note.cods=student.cods; OUTER JOINS În standardul ANSI-99 SQL, o joncţiune a două sau mai multe tabele care returnează doar rândurile corespondente se numeşte joncţiune interioară. Dacă se returnează şi celelalte, se spune că avem o joncţiune exterioară. Se folosesc în sintaxă termenii left, full, şi right, care fac referire la ordinea de apariţie a tabelelor în clauza FROM. Left Outer Join FROM note LEFT OUTER JOIN student ON note.cods =student.cods; Interogarea va întoarce toate rândurile corespondente, cât şi toate notele, chiar dacă acestea nu aparţin nici unui student. Right Outer Join FROM note RIGHT OUTER JOIN student ON note.cods =student.cods; Interogarea va întoarce toţi studenţii, chiar şi cei care nu au note.
Full Outer Join FROM note FULL OUTER JOIN student ON note.cods =student.cods; Extrage atât rândurile corespondente cât şi celelalte din ambele tabele, adică se vor returna toate rândurile din ambele tabele chiar dacă acestea nu au corespondent. UNION Acet operator combină rezultatele obţinute în urma execuţiei a două interogări într-unul singur. Permite combinarea rezultatelor a două interogări într-un singur set de rezultate, respectând condiţiile: a. fiecare interogare trebuie să aibă acelaşi număr de coloane şi să le listeze în aceeaşi ordine; b. coloanele returnate de fiecare instrucţiune SELECT trebuie să fie compatibile la atribuire sau trebuie să fie transformate explicit într-un tip de date compatibil la atribuire cu coloanele lor corespondente din celelalte instrucţiuni SELECT c. combinarea coloanelor compatibile la atribuire, dar de tipuri diferite, produce o coloană de tipul cu cea mai mare precedenţă dintre cele două (de exemplu, combinarea unei coloane de tip smallint cu o coloană de tip float produce o coloană rezultat de tip float; d. numele coloanelor returnate de UNION sunt derivate din cele din prima instrucţiune SELECT e. UNION ALL este mai rapidă decât UNION deoarece ea nu elimină duplicatele înainte de returnate. Operaţii semantice FROM: permite afişarea produsului cartesian al listei_de_tabele WHERE: aplică σ predicat GROUP BY: grupează tuplurile corespunzător cu lista_de_grupare_pe_coloane HAVING: aplică σ predicat grupurilor SELECT: aplică π lista_de_iesire_a_coloanelor (păstrează duplicatele) DISTINCT: elimină duplicatele ORDER BY: ordonează rezultatul pe baza listei_de_ordonare_pe_coloane Predicate Un predicat este o expresie care returnează TRUE sau NOT TRUE. Ele se întâlnesc în clauzele WHERE sau HAVING ale unei interogări. Obs. S-a folosit NOT TRUE şi nu FALSE datorită problemelor de logică trivalentă uneori nu se cunoaşte dacă o expresie este falsă, tot ceea ce se ştie este că, cu siguranţă, nu este adevărată Cele mai folosite predicate sunt: BETWEEN indică dacă o valoare dată se găseşte în intervalul închis definit de alte două valori LIKE testează dacă o valoare se află sau nu într-un şir model. Se pot folosi caracterele de înlocuire: % procent (corespunde oricărui număr de caractere) şi _ underscore (corespunde unui singur caracter). EXISTS funcţie predicat care acceptă ca unic parametru o subinterogare. Dacă interogarea returnează un set de rezultate, EXISTS întoarce valoarea TRUE, altfel întoarce valoarea FALSE IN oferă o metodă prescurtată de comparare a unei valori scalare cu fiecare element dintr-o listă. ANY, ALL lucrează exclusiv cu subinterogări. ANY, operează similar predicatului IN GROUP BY Această clauză se foloseşte pentru a grupa rândurile dintr-un tabel în seturi mai mici, pe baza unei condiţii de grupare. SELECT cods, AVG(nota) FROM note GROUP BY cods; HAVING Să presupunem că se doreşte găsirea celei mai mari note acordate fiecărui student, dar numai a acelora care au mai mult de o notă. Ce este greşit în această soluţie?
SELECT cods, MAX(nota) FROM note WHERE COUNT(*) > 1 GROUP BY cods; Se poate folosi clauza HAVING la fel ca în cazul clauzei WHERE pentru a restrânge numărul de rânduri returnate de interogare. Într-o interogare care foloseşte clauzele GROUP BY şi HAVING, rândurile sunt mai întâi grupate, după care se afişează doar rândurile ce îndeplinesc condiţia din clauza HAVING. Clauza HAVING este folosită pentru a restrânge numărul de grupuri returnate de clauza GROUP BY. SELECT cods, MAX(nota) FROM note GROUP BY cods HAVING COUNT(*) > 1; Funcţii de grup (funcţii agregat) Funcţiile de grup operează pe seturi de înregistrări furnizând un singur rezultat pe grup. Fiecare dintre funcţiile de mai jos furnizează un singur rezultat: MIN: folosită pe coloane ce păstrează orice fel de tipuri de date returnând valoarea minimă. MAX: folosită pe coloane ce păstrează orice fel de tipuri de date returnând valoarea maximă. SUM: folosită pe coloane ce păstrează valori numerice pentru a calcula suma valorilor din coloana respectivă. AVG: folosită pe coloane ce păstrează valori numerice pentru a calcula media aritmetică a valorilor din coloana respectivă. COUNT: întoarce numărul de rânduri. VARIANCE: folosită pe coloane ce păstrează valori numerice pentru a calcula împrăştierea rezultatelor. De exemplu, dacă media aritmetică a notelor dintr-o grupă la ultimul test este 82%, iar studenţii au înregistrat un scor între 40% şi 100%, rezultatul obţinut folosind această funcţie este mai mare decât dacă scorul studenţilor ar fi fost cuprins între 78% şi 88%. STDDEV: asemănător cu funcţia anterioară. Pentru două seturi de date care au aproximativ aceeaşi medie, cu cât împrăştierea este mai mare cu atât deviaţia standard este mai mare. Sintaxa: SELECT coloana, functie_de_grup(coloana),.. FROM tabel WHERE predicat GROUP BY coloana,...; Subinterogări O clauză SELECT care este inserată în cadrul altei clauze SELECT este numită subinterogare. Subinterogarea se execută cu scopul de a obţine informaţii necunoscute. Clauza exterioară foloseşte informaţiile obţinute prin subinterogare pentru a afla ceea ce i se cere. Subinterogarea se execută o singură dată, anterior, în memoria principală a sistemului. Subinterogările se pot introduce în clauzele: WHERE, HAVING şi FROM. Sintaxa: SELECT lista_de_selectie FROM tabel WHERE expresie operator(select lista_de_selectie FROM tabel); Clauza SELECT din paranteză este interogarea interioară sau subinteorgarea. Reguli Subinterogările oferă un mijloc de a baza o interogare pe o alta (imbricarea interogărilor). Trebuie respectate regulile: a. lista de selecţie a unei subinterogări create cu ajutorul unui operator de comparare poate conţine doar o singură expresie sau nume de coloană;
b. dacă clauza WHERE a unei interogări exterioare conţine numele unei coloane, aceasta trebuie să fie compatibilă din punct de vedere al joncţiunii cu coloana din subinterogare; c. tipurile de date ntext, text şi image nu sunt permise într-o subinterogare; d. clauza DISTINCT nu poate fi utilizată în subinterogări ce conţin clauza GROUP BY; e. într-o subinterogare nu poate fi utilizată clauza ORDER BY decât dacă s-a utilizat şi restricţia TOP; f. o vedere creată pe baza unei subinterogări nu poate fi actualizată; Obs. Construcţiile SELECT pot fi imbricate pe max. 16 nivele. O subinterogare diferă de operatorul de cuplare prin aceea că rezultatul final conţine date doar de la ultimul tabel. Există 2 tipuri de subinterogări: Subinterogări ce furnizează ca rezultat un singur rând Folosesc operatorii aritmetici specifici: (>, =, >=, < <>, <=) şi întorc doar un singur rând în urma execuţiei interogării interioare. Acestea: Returnează un singur rând Folosesc doar operatorii de comparare (=, >,>=, <, <=, <>) Întotdeauna: Interogarea interioară se introduce între paranteze rotunde Interogarea interioară se plasează la dreapta operatorului de comparare. De reţinut că: Interogările interioară şi exterioară pot prelua date din tabele diferite. Doar o singură clauză ORDER BY poate fi folosită într-o clauză SELECT şi, dacă apare, trebuie să se afle la sfârşitul clauzei SELECT principale. Subinterogări ce furnizează ca rezultat mai multe rânduri Folosesc predicatele IN, ANY, ALL, SOME, EXISTS şi întorc ca rezultat mai multe rânduri din interogarea interioară. De această dată nu se mai pot folosi operatorii de comparare. Operatorul NOT se poate folosi cu oricare dintre aceste predicate. Dacă una dintre valorile returnate de o astfel de subinterogare este NULL, dar celelalte nu sunt: Dacă se folosesc IN sau ANY, interogarea exterioară va returna doar rândurile corespunzătoare valorilor care nu sunt NULL. Dacă se foloseşte ALL interogarea exterioară nu va returna nimic, deoarece ALL compară un rând provenit din interogarea exterioară cu fiecare valoare returnată de subinterogare, inclusiv cele NULL. Comparând ceva cu NULL va rezulta tot NULL. În continuare este prezentată sintaxa folosită în toate cele trei situaţii. Varianta 1 Predicatele [ANY SOME ALL] Această variantă este folosită atunci când se compară o valoare cu o altă valoare obţinută în urma folosirii unei subinterogări. De exemplu, următoarea construcţie întoarce toţi studenţii (şi notele corespunzătoare acestora) care au cele mai mari note. SELECT Nume, Prenume, Nota FROM Student INNER JOIN Note ON Student.Cods=Note.Cods WHERE Nota = (SELECT Max(Nota) FROM Note); ceea ce conduce la un rezultat diferit de varianta: SELECT Nume, Prenume, MAX(Nota) FROM Student INNER JOIN Note ON Student.Cods=Note.Cods GROUP BY Nume, Prenume; în care răspunsul corespunde întrebării: "Care este cea mai mare notă obţinută de fiecare student?" Se observă faptul că, deoarece subinterogarea întoarce o singură valoare, nu este nevoie să se folosească nici unul dintre predicatele ANY, SOME sau ALL. Următoarea construcţie prezintă toţi studenţii (şi notele corespunzătoare acestora) care au note mai mari decât notele studentului Radu Tiberiu: SELECT Nume, Prenume, Nota
FROM Student INNER JOIN Note ON Student.Cods=Note.Cods WHERE Nota > All( SELECT Nota FROM Note INNER JOIN Student ON Student.Cods =Note.Cods WHERE Nume = 'Radu' And Prenume='Tiberiu'); Se observă faptul că predicatele ANY şi SOME conduc la obţinerea aceluiaşi rezultat, returnând toate variantele ce respectă condiţia de comparare pentru cel puţin una dintre valorile returnate de către subinterogare. De exemplu, dacă se înlocuieşte ALL cu SOME în interogarea anterioară, rezultatul obţinut va conţine toţi studenţii (şi notele corespunzătoare acestora) care sunt mai mari decât cea mai mică notă acordată studentului Radu Tiberiu. Varianta 2 Predicatul [NOT] IN Această variantă se foloseşte la căutarea unei valori a unei coloane dintr-un tabel rezultat în urma alteri interogări. De exemplu, următoarea construcţie returnează toţi studenţii (şi notele corespunzătoare) din tabelul "Student" care nu apar în tabelul "Note" (studenţii care nu au note): SELECT Cods,Nume, Prenume FROM Student WHERE Cods NOT IN (SELECT Cods FROM Note); Varianta 3 Predicatul [NOT] EXISTS Această variantă se foloseşte atunci când se verifică dacă o anumită valoare există (este returnată) în urma executării subinterogării. De exemplu, următoarea construcţie afişează toţi studenţii care nu au note: SELECT Cods, Nume, Prenume FROM Student WHERE NOT EXISTS (SELECT * FROM Note WHERE Note.Cods =Student.Cods); Se observă faptul că tabelul "Note" este referit în subinterogare, ceea ce face ca SQL Server să evalueze subinterogarea câte o dată pentru fiecare valoare a identificatorului "CodS" din tabelul "Note". Observaţii de ansamblu Atunci când se folosesc sintaxele 1 sau 2 subinterogarea trebuie să întoarcă o singură coloană. Altfel va apare un mesaj de eroare. Clauza SELECT interioară are acelaşi format şi reguli ca şi orice altă clauză SELECT, dar trebuie inclusă între paranteze rotunde. Exerciţii 1. Afişaţi notele obţinute de către studentul Radu Tiberiu. 2. Afişaţi taxele mărite cu 10% 3. Afişaţi numele şi prenumele studenţilor într-o singură coloană. 4. Afişaţi toţi studenţii care nu au prenumele Andrei. 5. Afişaţi numele şi prenumele studenţilor care au plătit taxe de peste 20 de lei, dar mai mici de 30 lei. 6. Afişaţi numele şi prenumele studenţilor care au primit note. 7. Afişaţi numele şi prenumele studenţilor din Braşov, Iaşi, Covasna 8. Reuniţi datele obţinute prin executarea a două interogări, astfel:
- Prima extrage numele şi prenumele studenţilor care au luat note mai mari de 7; - A doua extrage numele şi prenumele studenţilor care au luat note de 5. 9. Câţi studenţi plătesc taxă? 10. Care este suma taxelor plătite de către toţi studenţii? 11. Afişaţi suma totală a plăţilor studentului Radu Tiberiu. 12. Care este cea mai mare taxă? 13. Cine are cea mai mare medie a notelor? 14. Câţi ani au trecut de la începutul studiilor fiecărui student? 15. Afişaţi cursurile urmate de către studentul Radu Tiberiu. 16. Afişaţi numele şi prenumele studenţilor, precum şi cursurile promovate cu note mai mari de 8. 17. Afişaţi numele şi prenumele studentelor care sunt la zi şi sunt bursiere. 18. Câte note are fiecare student? Furnizaţi numele şi prenumele lor. 19. Afişaţi media notelor obţinută de către fiecare student. 20. Afişaţi numele şi prenumele studenţilor care au primit note mai mari decât cea mai mică notă acordată.