Wróć do: Bazy danych
zaawansowany SQLsubquerymatura rozszerzona z informatyki

Podzapytania (subqueries)

Zapytania zagnieżdżone w klauzuli WHERE – podzapytanie zwracające pojedynczą wartość oraz operatory IN i NOT IN dla listy wyników.

01

Opis

Czasem warunek, którego potrzebujesz, sam wymaga odpytania bazy. „Uczniowie ze średnią powyżej średniej całej szkoły" – żeby to sprawdzić, trzeba najpierw tę średnią policzyć. Podzapytanie pozwala zrobić obie rzeczy w jednym poleceniu: zapytanie wewnętrzne liczy potrzebną wartość, a zewnętrzne używa jej jako warunku.

Podzapytanie zapisuje się w nawiasie i wykonuje się ono jako pierwsze. Dopiero jego wynik trafia do warunku zapytania zewnętrznego. Czytając cudze zapytanie z podzapytaniem, zacznij więc od środka – od nawiasu – a nie od początku linii.

Podzapytanie zwracające jedną wartość. Jeśli wewnętrzne zapytanie zwraca dokładnie jedną liczbę (typowo przez AVG, MAX czy COUNT), można je porównać zwykłym operatorem: WHERE srednia > (SELECT AVG(srednia) FROM uczniowie). Baza podstawia policzoną wartość w miejsce nawiasu i dalej działa to jak zwykły warunek WHERE.

Podzapytanie zwracające listę. Częściej wynik wewnętrzny to cała kolumna wartości – wtedy zwykłe = nie zadziała, bo nie da się porównać jednej wartości z listą. Służy do tego operator IN: sprawdza, czy wartość znajduje się gdziekolwiek na tej liście. WHERE id IN (SELECT uczen_id FROM oceny) znaczy „uczniowie, których numer występuje wśród numerów w tabeli ocen".

NOT IN działa odwrotnie – zwraca wiersze, których na liście nie ma. To najprostszy sposób na pytanie „kto nie ma ani jednej oceny", na które złączenie wewnętrzne z definicji nie potrafi odpowiedzieć, bo pomija wiersze bez dopasowania.

Podzapytanie a złączenie. Sporo zadań da się rozwiązać oboma sposobami i oba bywają uznawane. Różnica jest w tym, co dostajesz na wyjściu: złączenie dokłada kolumny z drugiej tabeli do wyniku, a podzapytanie z IN służy tylko do odsiania wierszy – kolumn z tabeli wewnętrznej w wyniku nie zobaczysz. Jeśli zadanie prosi o pokazanie danych z obu tabel, potrzebne jest złączenie. Jeśli tylko o wskazanie wierszy spełniających warunek – podzapytanie jest zwykle czytelniejsze.

Uwaga na NOT IN i puste wartości. Jeżeli lista zwrócona przez podzapytanie zawiera choć jedną wartość pustą (NULL), NOT IN potrafi zwrócić pustą tabelę, mimo że intuicja podpowiada coś innego. To znany zaskakujący przypadek – przy NOT IN warto upewnić się, że kolumna w podzapytaniu nie ma pustych pól.

02

Zapytanie

-- Podzapytanie zwraca jedną liczbę: średnią ze wszystkich uczniów (4.21)
SELECT nazwisko, srednia
FROM uczniowie
WHERE srednia > (SELECT AVG(srednia) FROM uczniowie)
Wynik zapytania
nazwiskosrednia
Kowalska4.75
Wiśniewski5.10
-- Podzapytanie zwraca listę numerów - porównujemy operatorem IN
SELECT nazwisko
FROM uczniowie
WHERE id IN (
  SELECT uczen_id FROM oceny WHERE przedmiot = 'informatyka'
)
Wynik zapytania
nazwisko
Kowalska
Nowak
Wiśniewski
-- NOT IN odpowiada na pytanie, na które INNER JOIN nie potrafi
SELECT nazwisko
FROM uczniowie
WHERE id NOT IN (SELECT uczen_id FROM oceny)
Wynik zapytania
nazwisko
Zając
Dąbrowski

Na maturze

Zacznij od ustalenia, co ma zwrócić podzapytanie: jedną wartość czy listę. To rozstrzyga, jakiego operatora użyć. Porównanie = z podzapytaniem zwracającym wiele wierszy kończy się błędem – i odwrotnie, IN z jedną wartością zadziała, ale jest mylące dla czytającego.

Częsta pułapka: podzapytanie liczące AVG na tej samej tabeli, co zapytanie zewnętrzne. To jest poprawne i działa – wewnętrzne SELECT AVG(srednia) FROM uczniowie liczy średnią z całej tabeli, niezależnie od tego, że zewnętrzne zapytanie zaraz ją zawęzi. Uczniowie często zakładają błędnie, że warunek zewnętrzny wpływa też na podzapytanie.

Sprawdzaj wynik na danych z arkusza. Podzapytania rzadko wywalają się błędem składni – częściej zwracają coś, co wygląda poprawnie, ale odpowiada na nieco inne pytanie niż to z polecenia.

04

Najczęściej zadawane pytania

Czy podzapytanie może być w SELECT albo FROM?

Tak, choć na maturze zdecydowanie najczęściej pojawia się w WHERE. Podzapytanie w FROM tworzy tabelę tymczasową, na której działa zapytanie zewnętrzne, a w SELECT – dokłada do wyniku wartość liczoną osobno dla każdego wiersza.

Ile poziomów zagnieżdżenia jest dozwolone?

Formalnie wiele, praktycznie – im mniej, tym lepiej. Zapytanie z trzema poziomami nawiasów jest bardzo trudne do sprawdzenia wzrokiem. Jeśli zadanie prowadzi w tę stronę, warto rozważyć złączenie zamiast kolejnego zagnieżdżenia.

Czy podzapytanie wykonuje się raz, czy dla każdego wiersza?

Zależy od tego, czy odwołuje się do zapytania zewnętrznego. Podzapytanie niezależne – jak SELECT AVG(srednia) FROM uczniowie – baza liczy raz i podstawia gotowy wynik. Podzapytanie skorelowane, czyli używające kolumny z zapytania zewnętrznego, musi zostać policzone osobno dla każdego sprawdzanego wiersza.

Powiązane zagadnienia