0Pricing
SQL Interview Prep · Lekcja

Skorelowane EXISTS i NOT EXISTS

Solidna alternatywa dla anti-join, która poprawnie obsługuje wartości NULL

Skorelowane EXISTS i NOT EXISTS to bezpłatna lekcja SQL Interview Prep na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej SQL Interview Prep, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs SQL Interview Prep zawiera 4 lekcji w sumie.

EXISTS sprawdza obecność

EXISTS przyjmuje podzapytanie i zwraca TRUE natychmiast, gdy podzapytanie zwróci co najmniej jeden wiersz; w przeciwnym razie zwraca FALSE. Nigdy nie zwraca samych wierszy.

Gdy wewnątrz znajduje się podzapytanie skorelowane, EXISTS staje się testem obecności wykonywanym dla każdego wiersza zewnętrznego: „czy dla tego wiersza zewnętrznego istnieje pasujący wiersz?”

Ponieważ kończy działanie po znalezieniu pierwszego dopasowania, liczba pasujących wierszy nie ma znaczenia. Ten szczegół semantyki jest częstym tematem rozmów rekrutacyjnych.

Podstawowe skorelowane EXISTS

Należy znaleźć klientów, którzy złożyli co najmniej jedno zamówienie. Zapytanie wewnętrzne jest skorelowane przez o.customer_id = c.customer_id.

Dla każdego klienta EXISTS sprawdza: czy istnieje jakiekolwiek zamówienie tego klienta? Jeśli tak, klient zostaje zachowany.

SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id
);

Dlaczego wewnątrz EXISTS używa się SELECT 1

W EXISTS można spotkać SELECT 1, SELECT * lub SELECT NULL. Wszystkie te formy są równoważne.

EXISTS sprawdza wyłącznie czy zwrócono wiersze, a nie ich zawartość, dlatego wybierane kolumny nie mają znaczenia. Optymalizator je ignoruje.

SELECT 1 jest powszechną konwencją sygnalizującą intencję: „liczy się dla mnie tylko istnienie”. Należy wybrać jedną formę i stosować ją konsekwentnie; nie wolno dopuścić, aby rekruter uznał, że lista kolumn ma tutaj znaczenie.

NOT EXISTS znajduje brakujące elementy

NOT EXISTS odwraca test: zachowuje wiersz zewnętrzny tylko wtedy, gdy podzapytanie skorelowane nie zwróci żadnego wiersza.

To kanoniczne złączenie antysemi: klienci bez zamówień, produkty, których nigdy nie sprzedano, oraz studenci bez przesłanych prac.

SELECT c.customer_id, c.name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id
);

Pułapka NULL w NOT IN

To ważny temat rekrutacyjny. NOT IN użyte z podzapytaniem, które może zawierać NULL, działa zdradliwie: jeśli lista zawiera choć jedną wartość NULL, NOT IN nie zwróci żadnego wiersza.

Dzieje się tak, ponieważ porównanie z NULL daje UNKNOWN, a NOT IN wymaga, aby każde porównanie było fałszywe. Jedna wartość UNKNOWN zatruwa cały warunek.

NOT EXISTS nie ma tego problemu; po prostu sprawdza obecność wiersza i bezpiecznie obsługuje wartości NULL.

-- Risky: returns nothing if any o.customer_id is NULL
SELECT c.customer_id FROM customers c
WHERE c.customer_id NOT IN (SELECT o.customer_id FROM orders o);

-- Safe: NULLs do not break it
SELECT c.customer_id FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);

Dlaczego NOT EXISTS jest bezpieczne dla NULL

Powód tkwi w logice dopasowania. NOT EXISTS sprawdza, czy jakikolwiek wiersz wewnętrzny spełnia warunek o.customer_id = c.customer_id.

Wiersz, w którym o.customer_id ma wartość NULL, nigdy nie spełnia tego warunku równości (NULL = cokolwiek daje UNKNOWN, a nie TRUE), więc po prostu nie jest uznawany za dopasowanie. Test obecności pozostaje poprawny.

W przypadku NOT IN ta sama wartość NULL staje się częścią porównania z listą, którego wynik UNKNOWN usuwa cały wynik. Dlatego podczas rozmów na stanowiska seniorskie preferuje się NOT EXISTS.

EXISTS z dodatkowymi warunkami

Skorelowane podzapytanie może zawierać dodatkowe predykaty. Należy znaleźć klientów, którzy złożyli co najmniej jedno zamówienie o wartości przekraczającej 1000.

Dodatkowy warunek znajduje się wewnątrz podzapytania EXISTS i jest stosowany osobno dla każdego klienta.

SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id
      AND o.amount > 1000
);

Wydajność: działanie z wczesnym zakończeniem

EXISTS może przerwać skanowanie relacji wewnętrznej natychmiast po znalezieniu jednego pasującego wiersza. Nie tworzy ani nie zlicza pełnego zbioru wyników.

Dzięki temu EXISTS jest zazwyczaj wydajne, szczególnie gdy skorelowana kolumna jest indeksowana, ponieważ każde wyszukiwanie dla pojedynczego wiersza może szybko znaleźć dopasowanie i zakończyć działanie.

W przeciwieństwie do skorelowanego COUNT(*) > 0, które wymusza zliczenie wszystkich dopasowań. Jeśli potrzebna jest tylko odpowiedź tak/nie, należy preferować EXISTS.

EXISTS a COUNT przy sprawdzaniu istnienia

Kandydaci czasem zapisują skorelowane zliczanie, aby sprawdzić istnienie. Działa ono, ale powoduje niepotrzebny wysiłek.

Wersja z COUNT zlicza każde pasujące zamówienie, natomiast EXISTS kończy działanie po pierwszym. Przy samym sprawdzaniu istnienia EXISTS jasno komunikuje intencję i pozwala optymalizatorowi zakończyć działanie wcześniej.

-- Works but counts everything
SELECT c.customer_id FROM customers c
WHERE (SELECT COUNT(*) FROM orders o
       WHERE o.customer_id = c.customer_id) > 0;

-- Better: stops at first match
SELECT c.customer_id FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o
              WHERE o.customer_id = c.customer_id);

Przykład z rozwiązaniem: produkty, których nigdy nie zamówiono

Klasyczne zadanie rekrutacyjne dotyczące antyzłączenia: należy wyświetlić produkty, których nigdy nie zamówiono. NOT EXISTS niemal bezpośrednio odzwierciedla wymaganie.

Dla każdego produktu należy sprawdzić, czy odwołuje się do niego jakakolwiek pozycja zamówienia, i zachować tylko te produkty, do których nie odwołuje się żadna pozycja.

SELECT p.product_id, p.name
FROM products p
WHERE NOT EXISTS (
    SELECT 1
    FROM order_items oi
    WHERE oi.product_id = p.product_id
);

EXISTS w NOT EXISTS w zapytaniach typu dzielenie relacji

Zagnieżdżenie EXISTS wewnątrz NOT EXISTS wyraża dzielenie relacji: „znajdź wiersze pasujące do wszystkich elementów zbioru”. Klasyczne zadanie brzmi: „znajdź klientów, którzy zamówili każdy produkt w kategorii”.

Logika jest następująca: należy zachować klienta, gdy nie ma żadnego produktu, którego nie zamówił. Ta podwójna negacja jest znakiem rozpoznawczym zapytania realizującego dzielenie relacji, a rekruterzy wykorzystują ją do sprawdzania zaawansowanej znajomości EXISTS.

SELECT c.customer_id
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM products p
    WHERE p.category = 'Coffee'
      AND NOT EXISTS (
          SELECT 1 FROM order_items oi
          JOIN orders o ON o.order_id = oi.order_id
          WHERE oi.product_id = p.product_id
            AND o.customer_id = c.customer_id
      )
);

Szybki test

Należy wybrać najbezpieczniejszy sposób znalezienia klientów, którzy nie mają żadnych zamówień.

Podsumowanie: skorelowane EXISTS i NOT EXISTS

Najważniejsze wnioski:

  • EXISTS sprawdza istnienie dla każdego wiersza i kończy działanie po pierwszym dopasowaniu; wybór kolumny wewnątrz nie ma znaczenia (należy użyć SELECT 1).
  • NOT EXISTS to bezpieczne względem NULL antyzłączenie służące do znajdowania wierszy bez dopasowania.
  • NOT IN z wartością NULL na liście nie zwraca żadnych wierszy; należy preferować NOT EXISTS.
  • Przy sprawdzaniu istnienia EXISTS jest lepsze niż skorelowane COUNT(*) > 0, ponieważ kończy działanie wcześniej.

Warto wspomnieć o pułapce NULL w NOT IN bez dodatkowej zachęty; jest to wiarygodny sygnał dojrzałości w SQL.

Często zadawane pytania

Czy lekcja „Skorelowane EXISTS i NOT EXISTS” jest bezpłatna?

Tak — pełny tekst „Skorelowane EXISTS i NOT EXISTS” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu SQL Interview Prep, przejdź na CoddyKit PRO. Kurs SQL Interview Prep zawiera 4 lekcji w sumie.

Co nauczysz się w „Skorelowane EXISTS i NOT EXISTS”?

Solidna alternatywa dla anti-join, która poprawnie obsługuje wartości NULL Ćwiczysz SQL Interview Prep z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć SQL Interview Prep?

Nie wymagamy żadnego doświadczenia. SQL Interview Prep w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Skorelowane EXISTS i NOT EXISTS”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji SQL Interview Prep?

Tak. Każda lekcja SQL Interview Prep zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Anatomia podzapytania skorelowanego
  2. Agregaty dla grup bez GROUP BY
  3. Skorelowane EXISTS i NOT EXISTS
  4. Przepisywanie podzapytań skorelowanych jako złączeń
← Powrót do SQL Interview Prep