Pułapka WHERE w złączeniu zewnętrznym
Dlaczego filtrowanie kolumny ze złączenia zewnętrznego w WHERE po cichu zmienia je w złączenie wewnętrzne
Pułapka WHERE w złączeniu zewnętrznym to bezpłatna lekcja Coding Interview Prep na CoddyKit. To lekcja 4 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 Coding Interview Prep, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Coding Interview Prep zawiera 4 lekcji w sumie.
Pułapka, w którą wpadają prawie wszyscy
To najczęstszy błąd dotyczący złączeń zewnętrznych, celowo wykorzystywany przez osoby prowadzące rozmowy rekrutacyjne: „Pokaż każdego klienta i jego zamówienia z 2024 roku, uwzględniając klientów bez zamówień z 2024 roku”.
Kandydat używa LEFT JOIN, a następnie dodaje filtr daty w WHERE, przez co klienci bez zamówień z 2024 roku po cichu znikają. LEFT JOIN niepostrzeżenie staje się INNER JOIN. Zrozumienie przyczyny jest oznaką doświadczenia na poziomie seniora.
Błędne zapytanie
Oto błąd. Zapis wygląda rozsądnie: zachować wszystkich klientów, połączyć ich zamówienia i ograniczyć wynik do 2024 roku.
Jednak klienci bez zamówień lub bez zamówień z 2024 roku znikają z wyniku. Wymóg uwzględnienia ich nie zostaje spełniony.
-- BUG: drops customers with no 2024 order
SELECT c.name, o.id, o.order_date
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.order_date >= '2024-01-01';Dlaczego to nie działa
Proszę przypomnieć sobie kolejność operacji: najpierw wykonywane jest JOIN, które tworzy wiersze, w których niedopasowani klienci mają NULL we wszystkich kolumnach zamówień. Następnie wykonywane jest WHERE.
W przypadku niedopasowanego klienta o.order_date ma wartość NULL, więc wyrażenie o.order_date >= '2024-01-01' przyjmuje wartość UNKNOWN, a nie TRUE. WHERE zachowuje tylko wiersze, dla których wynik jest TRUE, dlatego wiersze z NULL zostają odfiltrowane — dokładnie te, które LEFT JOIN miał zachować.
NULL unieważnia filtr
Każde porównanie z NULL daje wynik UNKNOWN: NULL >= '2024-01-01' daje UNKNOWN, NULL = 5 daje UNKNOWN, a nawet NULL <> 5 daje UNKNOWN.
Ponieważ WHERE przepuszcza tylko wiersze, dla których wyrażenie ma wartość TRUE, każdy zachowany wiersz bez dopasowania zostaje odrzucony. Cały sens złączenia zewnętrznego zostaje zniweczony przez pojedynczy predykat WHERE dotyczący kolumny prawej tabeli.
Rozwiązanie: filtr w ON
Należy przenieść filtr do klauzuli ON. Wtedy staje się on częścią warunku dopasowania i jest stosowany przed zachowaniem wierszy, dzięki czemu niedopasowani klienci nadal pozostają w wyniku z wartościami NULL.
-- CORRECT: filter lives in ON
SELECT c.name, o.id, o.order_date
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
AND o.order_date >= '2024-01-01';
-- customers with no 2024 order: kept, NULL orderON a WHERE w jednym zdaniu
Oto zasada, którą warto powtórzyć podczas rozmowy:
W przypadku zachowywanej (zewnętrznej) tabeli warunki dotyczące drugiej tabeli należy umieszczać w ON, a warunki dotyczące samej zachowywanej tabeli — w WHERE.
ONdecyduje, co jest uznawane za dopasowanie (jest wykonywane podczas złączenia).WHEREfiltruje końcowe wiersze (jest wykonywane później i usuwa wiersze z wartościami NULL).
Wyniki obok siebie
Te same dane, dwa miejsca umieszczenia filtra i różne odpowiedzi. Załóżmy, że Carol nie ma zamówienia z 2024 roku.
- Filtr w WHERE: Carol znika. W praktyce jest to złączenie wewnętrzne.
- Filtr w ON: Carol pojawia się raz z wartościami NULL w kolumnach zamówienia, więc wymaganie zostaje spełnione.
Różnica w wyniku jest istotą tej pułapki.
-- ON version output
-- Alice | 50 | 2024-03-01
-- Bob | 20 | 2024-05-02
-- Carol | NULL | NULL <-- preservedKiedy WHERE jest rzeczywiście poprawne
Nie każde użycie WHERE przy złączeniu zewnętrznym jest błędem. Filtrowanie zachowywanej tabeli jest poprawne — nie dotyczy wartości NULL pochodzących ze złączenia.
Złączenie antysemi z poprzedniej lekcji celowo używa WHERE o.id IS NULL, aby wykorzystać właśnie to zachowanie. Umiejętność polega na rozpoznaniu, z którym przypadkiem ma się do czynienia.
-- Fine: filtering the preserved (left) table
SELECT c.name, o.id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE c.country = 'US';Heurystyka wykrywania problemu
Podczas przeglądania złączenia zewnętrznego należy sprawdzić klauzulę WHERE pod kątem predykatów dotyczących niezachowywanej tabeli (z wyjątkiem testów IS NULL używanych w złączeniach antysemi).
Jeśli w WHERE widzą Państwo o.someColumn = ... albo test zakresu lub równości po stronie zewnętrznej, należy podejrzewać tę pułapkę. Proszę zadać sobie pytanie: „Czy to zmienia moje LEFT JOIN w INNER JOIN?” Zwykle tak.
Wiele warunków
Można połączyć oba sposoby umieszczania warunków. Warunki dopasowania dotyczące prawej tabeli należy umieścić w ON, a rzeczywisty filtr wykonywany po złączeniu, dotyczący lewej tabeli, w WHERE. Oba warunki mogą współistnieć bez problemu.
SELECT c.name, o.id, o.amount
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
AND o.amount > 100 -- match condition
WHERE c.signup_year = 2023; -- preserved-table filterJak to wyjaśnić na głos
Podczas rozmowy należy opisać mechanizm, a nie tylko podać poprawkę:
„Najpierw wykonywane jest złączenie, które uzupełnia kolumny prawej tabeli wartościami NULL w niedopasowanych wierszach. Predykat WHERE dotyczący tych kolumn przyjmuje dla wierszy z NULL wartość UNKNOWN, a WHERE odrzuca wiersze, dla których wynik nie jest TRUE, więc złączenie zewnętrzne zmienia się w wewnętrzne. Umieszczenie predykatu w ON zachowuje go jako warunek dopasowania i pozwala zachować niedopasowane wiersze”. Takie wyjaśnienie sprawdza się za każdym razem.
Szybkie sprawdzenie
Należy wyświetlić wszystkich klientów i tylko ich zamówienia z 2024 roku, zachowując również klientów, którzy nie mieli żadnego takiego zamówienia.
Podsumowanie
Filtrowanie kolumny niezachowywanej tabeli w WHERE po cichu zmienia złączenie zewnętrzne w wewnętrzne, ponieważ wartości NULL z niedopasowanych wierszy nie spełniają predykatu (dają UNKNOWN), a WHERE je odrzuca.
- Warunki dopasowania dotyczące tabeli zewnętrznej należy umieszczać w
ON. - Filtry dotyczące zachowywanej tabeli należy umieszczać w
WHERE. IS NULLwWHEREoznacza celowe złączenie antysemi, a nie tę pułapkę.- Należy wyjaśnić kolejność operacji, aby pokazać pełne zrozumienie tematu.
Często zadawane pytania
Czy lekcja „Pułapka WHERE w złączeniu zewnętrznym” jest bezpłatna?
Tak — pełny tekst „Pułapka WHERE w złączeniu zewnętrznym” 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 Coding Interview Prep, przejdź na CoddyKit PRO. Kurs Coding Interview Prep zawiera 4 lekcji w sumie.
Co nauczysz się w „Pułapka WHERE w złączeniu zewnętrznym”?
Dlaczego filtrowanie kolumny ze złączenia zewnętrznego w WHERE po cichu zmienia je w złączenie wewnętrzne Ćwiczysz Coding 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ąć Coding Interview Prep?
Nie wymagamy żadnego doświadczenia. Coding 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 4 z 4.
Ile czasu zajmuje lekcja „Pułapka WHERE w złączeniu zewnętrznym”?
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 Coding Interview Prep?
Tak. Każda lekcja Coding 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
- LEFT JOIN i zachowywanie niedopasowanych wierszy
- Semantyka RIGHT i FULL OUTER JOIN
- Znajdowanie wierszy bez dopasowania (anti-join)
- Pułapka WHERE w złączeniu zewnętrznym