NULL oznacza "nieznane"
Każda inna wartość w SQLite oznacza coś konkretnego: liczbę, tekst, blob. NULL jest inny. To znacznik wartości, której brakuje albo której nie znamy. Ta jedna myśl wyjaśnia wszystkie dziwne rzeczy, które NULL wyprawia w zapytaniach.
Przygotuj małą tabelę do eksperymentów:
Dwie kolumny dopuszczają NULL. Boris nie ma e-maila. Cleo nie ma wieku. Dan nie ma ani jednego, ani drugiego. Reszta strony pokazuje, jak odpytywać takie wiersze i nie dać się oszukać.
= i <> nie działają z NULL
Pierwszy odruch to napisać WHERE email = NULL. Wygląda sensownie. Nie zwraca nic:
Zero wierszy, choć Boris i Dan wyraźnie mają e-mail równy NULL. Powód: porównanie czegokolwiek z NULL daje NULL, a nie prawdę ani fałsz. Klauzula WHERE w SQLite zostawia tylko wiersze, w których warunek jest prawdziwy, a NULL prawdą nie jest. Dlatego wiersz zostaje odfiltrowany.
Ta sama pułapka z <>:
Można by oczekiwać, że zwróci wszystkich poza Adą. Zwraca tylko Cleo. Boris i Dan, których e-maile są NULL, wypadają, bo NULL <> 'ada@example.com' to też NULL, a nie prawda.
To najczęstsza pułapka w SQL. Kiedy zapytanie "gubi wiersze", których się nie spodziewasz, podejrzewaj kolumnę z NULL.
Używaj IS NULL i IS NOT NULL
Właściwy sposób sprawdzania NULL to operator IS. W przeciwieństwie do = rozumie NULL i zwraca prawdę albo fałsz, nigdy NULL:
Pierwsze zapytanie zwraca Borisa i Dana. Drugie zwraca Adę i Cleo. IS NULL i IS NOT NULL to dwa operatory stworzone specjalnie po to, by zapytać "czy tej wartości brakuje?". Używaj ich wszędzie tam, gdzie kusi cię napisanie = NULL lub <> NULL.
Jeśli chcesz dostać "wszystkich poza Adą, łącznie z nieznanymi", połącz warunki jawnie:
Teraz pojawiają się Boris, Cleo i Dan.
NULL przechodzi przez arytmetykę i łączenie tekstów
Zasada "nieznanego" nie dotyczy tylko porównań. Każda operacja, która dotyka NULL, daje NULL:
next_year i doubled są NULL dla Cleo i Dana. labelled_age też jest dla nich NULL: połączenie tekstu z NULL daje NULL, a nie 'Wiek: '. Jeśli kolumna może być NULL, a na wyjściu potrzebujesz użytecznej wartości, musisz to obsłużyć. Do tego służą dwie kolejne funkcje.
IFNULL: wartość zastępcza z dwoma argumentami
IFNULL(a, b) zwraca a, chyba że jest NULL, a wtedy zwraca b. To najprostszy sposób, by zamienić NULL na wartość domyślną:
Boris i Dan dostają (brak e-maila). Cleo i Dan dostają 0. Dane w tabeli się nie zmieniają, IFNULL przepisuje tylko wynik.
IFNULL zawsze przyjmuje dokładnie dwa argumenty. Jeśli potrzebujesz więcej wartości zastępczych, sięgnij po COALESCE.
COALESCE: wygrywa pierwsza wartość różna od NULL
COALESCE(a, b, c, ...) przechodzi przez argumenty od lewej do prawej i zwraca pierwszy, który nie jest NULL. Uogólnia IFNULL na dowolną liczbę wartości zastępczych:
Dla Ady i Cleo używany jest e-mail. U Borisa i Dana e-mail to NULL, więc SQLite próbuje drugiego argumentu: adresu zbudowanego z imienia. Gdyby on też był NULL, zostałby użyty 'anonim'.
COALESCE to wybór przenośny: każda popularna baza SQL obsługuje go tak samo. IFNULL to wygodny skrót z SQLite i MySQL dla przypadku z dwoma argumentami. Domyślnie wybieraj COALESCE, a po IFNULL sięgaj tylko wtedy, gdy naprawdę masz dwa argumenty i chcesz krótszej nazwy.
NULL to nie pusty ciąg znaków
Częste nieporozumienie: ludzie traktują NULL i '' jako wymienne. Tak nie jest.
'' to prawdziwy tekst, który akurat ma zero znaków. NULL to brak wartości. length('') wynosi 0, a length(NULL) samo jest NULL. Do tego NULL = NULL daje NULL, a nie 1, i właśnie dlatego istnieje IS NULL.
Jeśli kolumna może zawierać zarówno '', jak i NULL, zdecyduj, które z nich oznacza "brak", i trzymaj się tego. Mieszanie obu zmusza każde zapytanie do obsługi dwóch przypadków, a o jednym na pewno kiedyś zapomnisz.
NULL w IN, NOT IN i DISTINCT
Jeszcze kilka miejsc, w których NULL potrafi zaskoczyć.
IN z listą zawierającą NULL może dać dziwne wyniki, zwłaszcza w połączeniu z NOT IN:
Można by oczekiwać wszystkich, których wiek nie wynosi 25. Nie dostajesz nic. SQLite rozwija NOT IN (25, NULL) mniej więcej do age <> 25 AND age <> NULL, a age <> NULL to zawsze NULL, więc cały warunek nigdy nie jest prawdziwy. Rozwiązanie: usuń NULL z listy (albo z kolumny) przed porównaniem.
Z kolei DISTINCT przy usuwaniu duplikatów traktuje wartości NULL jako równe sobie:
Dostajesz trzy wiersze: e-mail Ady, e-mail Cleo i jeden NULL (złożony z Borisa i Dana). Tak samo działają GROUP BY i UNION: traktują NULL jako jedną grupę, czyli odwrotnie niż =. SQL nie zawsze jest w tym konsekwentny, więc warto wiedzieć, po której stronie stoi każdy operator.
Krótka ściągawka
- Brakujące wartości sprawdzaj przez
IS NULL/IS NOT NULL. Nigdy przez= NULL. - Każda arytmetyka, łączenie tekstów czy porównanie z udziałem
NULLzwracaNULL. - Używaj
COALESCE(a, b, c, ...), by zastąpić NULL wartością zastępczą.IFNULL(a, b)to skrót dla dwóch argumentów. - Pusty ciąg
''to nie to samo coNULL. W każdej kolumnie wybierz jedno z nich jako "brak". NOT IN (..., NULL)to prawie zawsze błąd. Najpierw usuń NULL z listy.
Dalej: sortowanie wyników
Gdy już umiesz poprawnie filtrować wiersze, także te z NULL, kolejnym krokiem jest ułożenie ich w sensownej kolejności. Następna strona to ORDER BY, a ta klauzula ma własne zdanie o tym, gdzie w posortowanym wyniku lądują wartości NULL.
Najczęściej zadawane pytania
Dlaczego column = NULL nie działa w SQLite?
column = NULL nie działa w SQLite?Bo NULL oznacza "nieznane", a każde porównanie z czymś nieznanym też jest nieznane, a nie prawdziwe. Dlatego WHERE col = NULL nie zwraca żadnego wiersza, nawet tych, w których kolumna naprawdę ma wartość NULL. Zamiast tego użyj WHERE col IS NULL. To samo dotyczy <>: używaj IS NOT NULL.
Czym różni się IFNULL od COALESCE w SQLite?
IFNULL(a, b) przyjmuje dokładnie dwa argumenty i zwraca a, chyba że jest NULL, a wtedy zwraca b. COALESCE(a, b, c, ...) przyjmuje dowolną liczbę argumentów i zwraca pierwszy, który nie jest NULL. IFNULL to skrót dla dwóch argumentów, a COALESCE to wersja ogólna, działająca w większości baz SQL.
Czy NULL to to samo co pusty ciąg znaków w SQLite?
Nie. NULL oznacza "brak jakiejkolwiek wartości", a '' to ciąg o długości zero, czyli prawdziwa, znana wartość. '' IS NULL zwraca 0 (fałsz), length('') daje 0, a length(NULL) daje NULL. Jeśli kolumna dopuszcza oba przypadki, zapytania muszą obsłużyć je osobno albo sprowadzić jeden do drugiego.