Czym jest NULL, dlaczego nie da się go porównać znakiem równości i jak wyszukiwać brakujące dane operatorami IS NULL oraz IS NOT NULL.
NULL oznacza brak wartości – informację, której w bazie nie ma. To nie to samo co zero i nie to samo co pusty tekst. Zero to konkretna liczba, pusty tekst to konkretny (choć zerowej długości) napis, a NULL to znacznik mówiący „tego pola nikt nie wypełnił".
Z tego jednego faktu wynika cała reszta. Skoro w polu nic nie ma, to nie da się go z niczym porównać. Pytanie „czy ta pusta wartość równa się 5" nie może dać odpowiedzi „tak" ani „nie" – jedyną uczciwą odpowiedzią jest „nie wiadomo".
Dlatego = NULL nigdy nie zadziała. I to jest najważniejsze zdanie w tym haśle: warunek WHERE email = NULL nie zgłosi błędu, tylko zwróci pustą tabelę. Zawsze, dla każdych danych. Bo porównanie z pustym polem nie daje ani prawdy, ani fałszu, a WHERE przepuszcza wyłącznie wiersze, dla których warunek wyszedł prawdziwy.
Z tego samego powodu NULL <> NULL też nie jest prawdą. Dwa puste pola nie są sobie ani równe, ani różne – nie wiadomo, co miało w nich być, więc nie wiadomo, jak się mają do siebie.
Do szukania braków służą osobne operatory: IS NULL sprawdza, czy wartości brakuje, a IS NOT NULL – czy jest obecna. To jedyny poprawny sposób i jedyny, który działa.
Logika trójwartościowa. Warunek w SQL-u nie ma dwóch możliwych wyników, tylko trzy: prawda, fałsz i nieznane.
Tu potrzebne jest rozróżnienie, które łatwo przeoczyć, a bez którego reszta się rozmywa. NULL to brak wartości w kolumnie – coś, co leży w tabeli. „nieznane" to wynik warunku – to, co wychodzi ze sprawdzenia, a nie to, co jest w komórce. Łączy je jedno: warunek dotyczący pola z NULL-em daje w wyniku „nieznane". W anglojęzycznych materiałach ta trzecia wartość nazywa się UNKNOWN i nigdy nie jest nazywana NULL.
Wynika stąd zachowanie, które na pierwszy rzut oka zaskakuje, a po chwili jest oczywiste: warunek nieznany połączony przez AND z fałszywym daje fałsz (skoro drugi człon jest fałszywy, całość jest fałszywa niezależnie od pierwszego), ale połączony z prawdziwym daje nieznane. Podobnie warunek nieznany połączony przez OR z prawdziwym daje prawdę, bo do spełnienia OR wystarczy jeden człon.
NULL cicho wypada z wyników. Jeśli filtrujesz po kolumnie, w której są braki, wiersze z NULL nie trafią do wyniku ani przy warunku, ani przy jego zaprzeczeniu. Suma wyników zapytania i zapytania odwrotnego może więc być mniejsza niż liczba wierszy w tabeli – i nie jest to błąd. To najczęstsza przyczyna „gubiących się" wierszy.
Gdzie jeszcze NULL zmienia wynik:
Funkcje agregujące pomijają puste wartości – AVG liczy średnią z wierszy, które mają wartość, a nie traktuje braku jak zera. Wyjątkiem jest COUNT(*), który liczy wiersze, a nie wartości.
DISTINCT traktuje wszystkie NULL jak jedną wartość i zostawia z nich jeden wiersz – choć formalnie nie da się ich ze sobą porównać.
NOT IN potrafi zwrócić pustą tabelę, jeśli na liście po prawej stronie znajdzie się choć jeden NULL. To wprost konsekwencja tego, że porównanie z pustym polem nigdy nie wychodzi fałszywe – a NOT IN potrzebuje właśnie fałszu, żeby wiersz przepuścić.
Złączenie wewnętrzne pomija wiersze bez dopasowania – ale to inne zjawisko niż NULL: tam wiersza po prostu nie ma, a tu wiersz jest, tylko jedna jego kolumna jest pusta.
| Wynik warunku A | Wynik warunku B | A AND B | A OR B |
|---|---|---|---|
| prawda | prawda | prawda | prawda |
| prawda | fałsz | fałsz | prawda |
| fałsz | fałsz | fałsz | fałsz |
| prawda | nieznane | nieznane | prawda |
| fałsz | nieznane | fałsz | nieznane |
| nieznane | nieznane | nieznane | nieznane |
-- Poprawnie: operator IS NULL SELECT nazwisko, email FROM uczniowie WHERE email IS NULL
| nazwisko | |
|---|---|
| Nowak | |
| Zając |
-- Błędnie: = NULL nie zgłasza błędu, tylko ZAWSZE zwraca pustą tabelę SELECT nazwisko, email FROM uczniowie WHERE email = NULL
-- IS NOT NULL: wiersze, w których wartość jest obecna SELECT nazwisko, email FROM uczniowie WHERE email IS NOT NULL
| nazwisko | |
|---|---|
| Kowalska | a.kowalska@example.com |
| Wiśniewski | m.wisniewski@example.com |
| Dąbrowski | k.dabrowski@example.com |
Gdy zapytanie zwraca pustą tabelę bez powodu, sprawdź, czy nie porównujesz z NULL. To błąd bez komunikatu – składnia jest poprawna, więc nic nie sygnalizuje problemu. Objaw jest zawsze ten sam: zero wierszy przy danych, w których na oko coś powinno być.
Sprawdź, czy liczby się sumują. Jeśli zadanie dzieli tabelę na dwie grupy warunkiem i jego zaprzeczeniem, policz, czy obie części dają razem całą tabelę. Jeśli nie – w kolumnie warunku są puste wartości i trzeba je obsłużyć osobno przez IS NULL.
Nie myl braku wartości z brakiem wiersza. „Uczeń bez adresu e-mail" to wiersz z NULL w kolumnie – szukasz go przez IS NULL. „Uczeń bez ani jednej oceny" to brak wierszy w drugiej tabeli – szukasz go przez NOT IN z podzapytaniem albo złączenie zewnętrzne. To dwa różne pytania i dwa różne narzędzia.
Uważaj przy średnich. Średnia policzona przez bazę pomija braki, więc dzieli przez liczbę wypełnionych pól. Jeśli zadanie wymaga potraktowania braku jak zera, trzeba to zrobić jawnie – sama funkcja tego nie zrobi.
Nie, to dwie różne rzeczy na dwóch różnych poziomach. NULL jest wartością (a dokładniej: jej brakiem) i siedzi w kolumnie tabeli. „nieznane" jest wynikiem warunku i stoi obok prawdy i fałszu w tabeli prawdy. W komórce tabeli nigdy nie zobaczysz „nieznane", a wynikiem warunku nigdy nie jest NULL. Powiązanie jest jednokierunkowe: gdy warunek dotyczy pola z NULL-em, jego wynikiem jest „nieznane".
Nie. Pusty tekst jest konkretną wartością o zerowej długości – da się go znaleźć zwykłym porównaniem = ''. NULL to brak jakiejkolwiek wartości i wymaga IS NULL. W jednej kolumnie mogą występować oba naraz, co bywa źródłem trudnych do wykrycia pomyłek.
Część baz akceptuje NOT (kolumna IS NULL), co znaczy to samo. IS NOT NULL jest jednak krótsze i czytelniejsze, więc nie ma powodu, żeby pisać inaczej.
Również NULL. Dodanie czegokolwiek do pustego pola znów daje puste pole – więc srednia + 1 dla wiersza z pustą średnią nie da 1, tylko NULL. To samo dotyczy sklejania tekstów: dołączenie NULL do napisu potrafi wyzerować całe wyrażenie.
Zagadnienia z tego samego obszaru matury – warto je powtórzyć razem: