Podzapytania zagnieżdżone w języku SQL
Podzapytanie zagnieżdżone SELECT znajduje się wewnątrz zewnętrznego zapytania SELECT, np. po klauzuli WHERE, HAVING lub FROM. W przypadku tego rodzaju zapytań w pierwszej kolejności wykonywane są wewnętrzne zapytania SELECT, a ich wynik jest wykorzystywany do zewnętrznego zapytania SELECT.
W przypadku zapytań zagnieżdżonych w wewnętrznym zapytaniu SELECT nie może wystąpić klauzula ORDER BY ani operatory UNION, INTERSECT, MINUS (EXCEPT).
UWAGA: należy zwrócić uwagę, czy zapytania wewnętrzne zwracają jeden wynik czy wiele wartości. Jeśli zwracają wiele wartości, to w warunku WHERE zapytania wewnętrznego należy użyć operatora IN lub operatorów ALL lub ANY. Na końcu zapytania wewnętrznego nie ma średnika, zapisuje się je w nawiasach okrągłych.
Operatory ALL i ANY można stosować w podzapytaniach zwracających więcej niż jeden wiersz. Podaje się je w klauzuli WHERE i HAVING razem z operatorami porównywania.
Operator ANY: wiersz/krotka/rekord zostanie wybrany, jeśli wyrażenie jest zgodne z co najmniej jedną wartością wybraną w podzapytaniu wewnętrznym.
Operator ALL: warunek musi być spełniony przez wszystkie wartości wybrane w podzapytaniu.
Przykład: z bazy firma z tabeli emp wybrać imiona i nazwiska pracowników, którzy zarabiają więcej niż średnia płaca w firmie.
SELECT first_name AS imie, last_name AS nazwisko, salary AS placa FROM emp WHERE salary>(SELECT AVG(salary) FROM emp) ORDER BY nazwisko, imie;
Klauzula ORDER BY w zapytaniu SELECT może wystąpić tylko raz na końcu zewnętrznego zapytania SELECT.
Przykład: z bazy danych firma z tabeli emp wybrać imiona, nazwiska, wynagrodzenie, ID departamentu pracowników, którzy mają najwyższe wynagrodzenia w tym departamencie.
SELECT first_name, last_name, salary, dept_id FROM emp WHERE (salary, dept_id) IN (SELECT MAX(salary), dept_id FROM emp GROUP BY dept_id) ORDER BY salary DESC;
Podzapytania skorelowane
Podzapytania skorelowane są szczególnym przypadkiem zapytań zagnieżdżonych. W przypadku zapytań zagnieżdżonych w pierwszej kolejności wykonywane jest zapytanie wewnętrzne, a jego wynik wykorzystywany jest do zapytania zewnętrznego, natomiast w przypadku podzapytań skorelowanych dla każdego wystąpienia zapytania zewnętrznego wykonywane jest zapytanie wewnętrzne, jeśli spełniony jest warunek zawarty w klauzuli WHERE lub HAVING. Wynik zapytania zewnętrznego brany jest pod uwagę.
Przykład: z bazy danych pobierz: nazwę departamentu, nazwisko, imię i wynagrodzenie pracowników, którzy mają najwyższe wynagrodzenie w tym departamencie.
SELECT Ez.dept_id, Ez.last_name, Ez.first_name, Ez.salary FROM emp Ez WHERE Ez.salary=(SELECT MAX(Ew.salary) FROM emp Ew WHERE Ez.dept_id = Ew.dept_id) ORDER BY Ez.dept_id;
W przypadku tego zapytania skorelowanego tworzone jest zapytanie zewnętrzne (nadrzędne) odczytujące wszystkie rekordy z tabeli emp. Następnie dla pierwszego odczytanego rekordu wykonywane jest zapytanie podrzędne (podzapytanie) znajdujące wartość płacy maksymalnej dla tego departamentu, którego ID został odczytany w rozpatrywanym rekordzie zapytania głównego, co jest możliwe dzięki wykonywaniu porównania w wewnętrznej klauzuli WHERE łączącego wyniki zapytań zewnętrznego i podzapytania. Jesli wartość wynagrodzenia odczytana w zapytaniu zewnętrznym jest równa maksymalnej płacy obliczonej w zapytaniu wewnętrznym, to rekord z zapytania głównego brany jest pod uwagę. W przeciwnym wypadku jest odrzucany. Powyższa procedura powtarzana jest dla każdego rekordu wybranego w zapytaniu wewnętrznym.
Operator EXISTS
W przypadku zapytań skorelowanych czasami interesuje nas tylko, czy wiersz spełniający podane warunki istnieje. Wtedy używamy operatora EXISTS.
Przykład: z bazy danych firma wypisać nazwy departamentów, w których aktualnie nikt nie pracuje.
SELECT D.name FROM dept D WHERE NOT EXISTS(SELECT 1 FROM emp Ew WHERE Ew.dept=id = D.id LIMIT 1) ORDER BY D.name;
UWAGA: w przypadku operatora EXISTS chcemy sprawdzić, czy rekord spełniający podany warunek istnieje, dlatego po klauzuli SELECT można napisać * lub dowolną stałą.
Przykład: z bazy danych pobrać imiona i nazwiska pracowników, których wynagrodzenie jest wyższe niż większe niż najwyższe wynagrodzenie pracownika w departamencie 10. Użyć operatora ALL.
SELECT Ez.first_name, Ez.last_name, Ez.salary FROM emp Ez WHERE Ez.salary > ALL(SELECT Ew.salary FROM emp Ew WHERE Ew.dept_id = 10) ORDER BY Ez.last_name, Ez.first_name;
Przykład: z bazy danych firma pobrać imiona i nazwiska pracowników, którzy pracują na tym samym stanowisku, co Adam Krzywy i mają wyższe wynagrodzenia niż Agata Prosta.
SELECT Ez.first_name, Ez.last_name FROM emp Ez WHERE Ez.title = (SELECT Ew.title FROM emp Ew WHERE Ew.first_name = 'Adam' AND Ew.last_name = 'Krzywy') AND Ez.salary > (SELECT Ew.salary FROM emp Ew WHERE Ew.first_name = 'Agata' AND Ew.last_name = 'Prosta') ORDER BY Ez.last_name, Ez.first_name;
Przykład: z bazy danych pobrać nazwy departamentów, w których pracują pracownicy na stanowisku Clerk [z j. ang. urzędnik].
SELECT D.name FROM dept D WHERE D.id IN (SELECT Ew.dept_id FROM emp Ew WHERE Ew.title = 'Clerk')