Witaj na nowym wykładzie o SQL! Dzisiaj poznamy najbardziej niepozorne, ale mega potężne operatory — EXISTS i NOT EXISTS. Wyobraź sobie szpiega, który nie zostawia śladów, ale od razu mówi: "Tak, obiekt istnieje" albo "Nie, tu pusto". Te operatory nie zwracają danych bezpośrednio, ale pozwalają robić logicznie precyzyjne sprawdzenia w zapytaniach.
Zaczynamy od podstaw. EXISTS — to operator, który sprawdza istnienie rekordów w wyniku podzapytania. Jeśli podzapytanie zwraca chociaż jeden rekord, warunek EXISTS zwróci TRUE, w przeciwnym razie — FALSE.
SELECT 1
WHERE EXISTS (
SELECT *
FROM students
WHERE grade > 3.5
);
Jak widzisz, nie interesują nas same dane z podzapytania, tylko to, czy takie wiersze istnieją. Jeśli choć jeden rekord spełnia warunek, zapytanie zwraca 1.
Składnia EXISTS
Składnia EXISTS jest prosta:
SELECT kolumny
FROM tabela
WHERE EXISTS (
SELECT 1
FROM inna_tabela
WHERE warunek
);
Wyjaśnienie:
- Zagnieżdżone podzapytanie w
EXISTSmoże być dowolnym zapytaniem. - To właśnie wynik podzapytania decyduje, czy zwróci
TRUEczyFALSE.
Przykład: Czy są studenci z oceną powyżej 4?
Wyobraźmy sobie tabelę students:
| id | name | grade |
|---|---|---|
| 1 | Otto | 3.2 |
| 2 | Anna | 4.7 |
| 3 | Dan | 5.0 |
| 4 | Lina | 2.9 |
Załóżmy, że chcemy sprawdzić, czy są studenci z oceną powyżej 4. Użyjemy takiego zapytania:
SELECT 'Są studenci z wysokim wynikiem!'
WHERE EXISTS (
SELECT 1
FROM students
WHERE grade > 4
);
Wynik:
Są studenci z wysokim wynikiem!
Dlaczego EXISTS działa szybciej niż IN?
Główna zaleta EXISTS to to, że zatrzymuje wykonanie podzapytania, jak tylko znajdzie pierwsze dopasowanie. To znaczy, że jeśli mamy warunek sprawdzający istnienie danych, EXISTS może być mega wydajny.
Na przykład, wyobraź sobie, że w tabeli students są miliony rekordów, ale szukamy tylko jednego istniejącego dopasowania (grade > 4). Jak tylko SQL znajdzie pierwszy pasujący wiersz, zapytanie się kończy.
Użycie NOT EXISTS
Teraz pogadajmy o NOT EXISTS. Ten operator działa jak przeciwieństwo EXISTS. Zwraca TRUE, jeśli podzapytanie nie zwraca żadnego rekordu.
Przykład: znajdź studentów bez ocen (NULL)
Załóżmy, że w naszej tabeli są studenci, którzy jeszcze nie mają ocen:
| id | name | grade |
|---|---|---|
| 1 | Otto | NULL |
| 2 | Anna | 4.7 |
| 3 | Dan | 5.0 |
| 4 | Lina | NULL |
Chcemy wybrać wszystkich studentów bez ocen. Używamy NOT EXISTS:
SELECT *
FROM students s
WHERE NOT EXISTS (
SELECT 1
FROM students
WHERE grade IS NOT NULL
AND id = s.id
);
Wynik:
| id | name | grade |
|---|---|---|
| 1 | Otto | NULL |
| 4 | Lina | NULL |
Porównanie EXISTS i IN
Czasem wydaje się, że EXISTS i IN robią to samo. Na pierwszy rzut oka — tak, ale są niuanse. Zwłaszcza jeśli gdzieś pojawi się NULL. Wtedy zachowanie IN może być zaskakujące, a EXISTS — wybawieniem.
Zobaczmy na przykładzie.
Tabela courses (kursy, które można przejść):
| course_id | name |
|---|---|
| 1 | Matematyka |
| 2 | Historia |
A tu studenci:
| student_id | name |
|---|---|
| 1 | Alex Lin |
| 2 | Anna Song |
| 3 | Maria Chi |
| 4 | Dan Seth |
| 5 | Shadow Moon |
Tabela enrollments (zapisy na kursy):
| student_id | course_id |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | NULL |
Chcemy wybrać nazwy kursów, na które ktoś się zapisał. Wydaje się proste.
Z użyciem IN:
SELECT name
FROM courses
WHERE course_id IN (
SELECT course_id
FROM enrollments
);
Na pierwszy rzut oka wszystko powinno działać. Ale jeśli w enrollments jest NULL w courseid, jak u Maria Chi, IN może zwrócić... nic! Bo NULL robi podzapytanie "nieokreślonym", i SQL się gubi: a może NULL to właśnie ten courseid, którego szukamy?
Z użyciem EXISTS:
SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
);
A EXISTS sprawdza: "Czy jest chociaż jeden wiersz, gdzie course_id się zgadza?" — i tyle. Nie przejmuje się, jeśli gdzieś jest NULL, bo szuka konkretnych dopasowań, a nie listy wartości.
Wniosek: jeśli w podzapytaniu może być NULL, lepiej użyć EXISTS, żeby nie mieć niespodzianek.
Przykłady realnych zadań
Tabela students:
| id | name |
|---|---|
| 1 | Alex Lin |
| 2 | Anna Song |
| 3 | Maria Chi |
| 4 | Dan Seth |
| 5 | Shadow Moon |
Tabela enrollments:
| student_id | course_id |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | NULL |
Przykład 1. Studenci zapisani na kursy
Znajdźmy tych, którzy już gdzieś się pojawili — zapisali się na kurs (nawet dziwnie, jak Maria Chi):
SELECT name
FROM students s
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE s.id = e.student_id
);
Wynik:
Alex Lin
Anna Song
Maria Chi
Jeśli student w jakikolwiek sposób pojawia się w enrollments — trafia do wyniku, nawet jeśli jego course_id jest niejasny.
Przykład 2. Studenci bez kursów
Teraz znajdźmy tych, którzy po prostu istnieją w systemie — ale nigdzie się nie zapisali:
SELECT name
FROM students s
WHERE NOT EXISTS (
SELECT 1
FROM enrollments e
WHERE s.id = e.student_id
);
Wynik:
Dan Seth
Shadow Moon
Wygląda na to, że ta dwójka jeszcze nie znalazła kursu dla siebie. Albo po prostu zapomnieli, że trzeba się zapisać :)
Przykład 3. Wybór kursów z więcej niż 5 zapisanymi studentami
Tabela courses:
| course_id | name |
|---|---|
| 1 | Matematyka |
| 2 | Historia |
| 3 | Biologia |
| 4 | Filozofia |
Tabela enrollments:
| student_id | course_id |
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 1 |
| 7 | 2 |
| 8 | 2 |
| 9 | 2 |
| 10 | NULL |
Chcemy znaleźć takie kursy, na które zapisało się więcej niż pięciu studentów. Tu EXISTS jakby pyta: "Czy dla tego kursu jest chociaż jedna grupa rekordów, gdzie studentów jest więcej niż pięciu?"
SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
GROUP BY e.course_id
HAVING COUNT(*) > 5
);
Wynik:
Matematyka
Tylko na kurs "Matematyka" (course_id = 1) zapisało się sześciu studentów. Pozostałe kursy na razie nie są tak popularne.
Typowe błędy przy użyciu EXISTS i NOT EXISTS
- Złe zrozumienie składni podzapytania. Zawsze sprawdzaj, czy podzapytanie poprawnie odnosi się do zewnętrznej tabeli.
- Zapomniana obsługa
NULL. Nawet jeśli używaszEXISTS, czasem musisz jawnie obsłużyćNULL. - Brak indeksu na polach podzapytania. To może mocno spowolnić wykonanie zapytania.
To wszystko na dziś! Teraz już wiesz, jak używać EXISTS i NOT EXISTS do sprawdzania istnienia danych, a także różnice między tymi operatorami i IN. W następnym wykładzie dalej będziemy zgłębiać podzapytania, patrząc jak używać ich w SELECT do pracy z danymi agregowanymi.
GO TO FULL VERSION