Przykłady złożonych zapytań zagnieżdżonych: łączenie EXISTS, IN, HAVING
Gratulacje, dotarliśmy do momentu, gdzie zaczyna się robić naprawdę ciekawie! Dzisiaj zobaczymy, jak łączyć różne typy podzapytań, żeby rozwiązywać bardziej złożone zadania. EXISTS, IN, HAVING — to właśnie ta trójka sprawi, że poczujesz się jak mag baz danych. Będziemy wyciągać dane z jednej tabeli, filtrować je na podstawie danych z innej, grupować, a potem jeszcze filtrować te grupy. A na dokładkę — ogarniemy, jakie triki pomagają robić to szybciej i sprawniej.
Zacznijmy od postawienia jednego ogólnego zadania, które krok po kroku rozwiążemy w trakcie wykładu.
Opis zadania
Załóżmy, że mamy bazę danych uniwersytetu z trzema tabelami:
Tabela students
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
Tabela courses
| id | name |
|---|---|
| 1 | Matematyka |
| 2 | Programowanie |
| 3 | Filozofia |
Tabela enrollments
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
Musimy wybrać wszystkich studentów, którzy:
- Są zapisani przynajmniej na jeden kurs
EXISTS. - Nie mają oceny z przynajmniej jednego kursu, na który są zapisani
IN. - Należą do grup, gdzie średnia ocen jest wyższa niż 80
HAVING.
Rozwiązanie z użyciem EXISTS i IN
Krok 1: Sprawdzenie zapisanych studentów (EXISTS). Zaczynamy od najprostszego warunku. Musimy sprawdzić, którzy studenci są zapisani na przynajmniej jeden kurs. Do tego użyjemy EXISTS.
SELECT name
FROM students s
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE e.student_id = s.id
);
- Zewnętrzne zapytanie wybiera imiona z tabeli
students. - W podzapytaniu sprawdzamy, czy są wpisy w tabeli
enrollmentsodpowiadające konkretnemu studentowi z zapytania zewnętrznego (WHERE e.student_id = s.id). SELECT 1oznacza, że interesuje nas tylko istnienie wpisów, a nie ich zawartość.
Wynik:
| name |
|---|
| Otto |
| Maria |
| Alex |
Teraz już wiemy, którzy studenci są zapisani na kursy. Ale chcemy więcej. Chcemy przefiltrować ich po braku ocen.
Krok 2: Sprawdzenie braku oceny (IN + NULL). Teraz dodamy filtr: potrzebujemy tylko tych studentów, którzy mają przynajmniej jeden kurs bez oceny. Tu przydadzą się IN i znajomość pracy z NULL.
SELECT name
FROM students s
WHERE id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- W zapytaniu zewnętrznym wybieramy imiona studentów.
- Podzapytanie tworzy listę
student_idz tabelienrollments, gdziegrade IS NULL.
Wynik:
| name |
|---|
| Otto |
No i mamy — Otto to jedyny student, który ma kurs bez oceny. Ale to jeszcze nie koniec: musimy wziąć pod uwagę tylko te grupy, gdzie średnia ocen jest wyższa niż 80.
Rozwiązanie z użyciem HAVING
Krok 3: Grupowanie i filtrowanie z HAVING.
Teraz czas połączyć wszystko w całość. Musimy:
- Policzyć średnią ocenę dla każdej grupy.
- Przefiltrować grupy, gdzie średnia ocen jest wyższa niż 80.
- Wyświetlić studentów z tych grup, biorąc pod uwagę wcześniejsze warunki.
SELECT name
FROM students s
WHERE s.group_id IN (
SELECT group_id
FROM students
JOIN enrollments ON students.id = enrollments.student_id
WHERE grade IS NOT NULL
GROUP BY group_id
HAVING AVG(grade) > 80
)
AND id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- Zewnętrzne zapytanie wybiera imiona studentów, którzy spełniają wszystkie warunki.
- Pierwsze podzapytanie w
WHEREzwraca listęgroup_iddla grup ze średnią oceną powyżej 80.- Łączymy
studentszenrollments, żeby dostać oceny. - Filtrujemy tylko te wpisy, gdzie
grade IS NOT NULL. - Grupujemy dane po
group_id. - Używamy
HAVINGdo filtrowania grup.
- Łączymy
- Drugie podzapytanie w
WHEREsprawdza, czy student ma przynajmniej jeden kurs bez oceny. - Obie części warunku są połączone przez
AND.
Wynik:
| name |
|---|
| Otto |
Wyszło na to, że Otto to nie tylko jedyny student bez oceny, ale też należy do grupy, która może się pochwalić świetnymi wynikami.
Porównanie podejść: EXISTS vs IN
EXISTS najlepiej sprawdza się, gdy chcesz szybko sprawdzić, czy istnieją jakieś wpisy. Jest wydajny, bo zatrzymuje szukanie od razu po znalezieniu pierwszego wpisu. To szczególnie ważne przy dużych tabelach.
Z kolei IN jest przydatny, gdy skupiasz się na zawartości danych. Na przykład, gdy wyciągasz listę identyfikatorów (id) do dalszego filtrowania. Ale pamiętaj, że IN może być wolniejszy, jeśli podzapytanie zwraca dużo wartości.
Kiedy używać HAVING
Dla danych zagregowanych, gdzie musisz filtrować na podstawie wyników, HAVING to najlepszy wybór. Ale jeśli możesz przenieść warunek do WHERE (np. filtrowanie po kolumnie), to uprości zapytanie i przyspieszy jego wykonanie.
Pełny przykład
Dla utrwalenia ogarniemy jeszcze jeden przykład: wybierz grupy, gdzie przynajmniej jeden student ma ocenę poniżej 75, ale jednocześnie nie jest zapisany na kurs "Filozofia".
Przypominamy nasze tabele:
Tabela students
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
Tabela courses
| id | name |
|---|---|
| 1 | Matematyka |
| 2 | Programowanie |
| 3 | Filozofia |
Tabela enrollments
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
SELECT DISTINCT group_id
FROM students s
WHERE group_id IN (
SELECT s.group_id
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.grade < 75
)
AND group_id NOT IN (
SELECT s.group_id -- podzapytanie 1-go poziomu
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.course_id = (
SELECT id FROM courses WHERE name = 'Filozofia' -- podzapytanie 2-go poziomu :P
)
);
- Pierwsze podzapytanie wybiera grupy, gdzie jest student z oceną poniżej 75.
- Drugie podzapytanie wyklucza grupy powiązane z kursem "Filozofia".
- Łączymy warunki przez
INiNOT IN, żeby dostać końcowy wynik.
Wynik:
| group_id |
|---|
| 101 |
Na ile to się przydaje?
W prawdziwym życiu takie podejścia ratują, gdy trzeba analizować złożone powiązania danych. Na przykład:
- W analizie, żeby wyłapać "wyjątkowe" grupy klientów (VIP, problematyczni itd.).
- W budowie systemów rekomendacyjnych, gdzie filtrujemy użytkownika po wielu kryteriach.
- Na rozmowach o pracę, gdy poproszą cię o optymalizację trudnego zapytania SQL.
Ćwicz! To twoja droga do mistrzostwa.
GO TO FULL VERSION