Często zachodzi potrzeba formułowania zapytań, w których kryteria filtrowania określane są dynamicznie — lista wartości jest podawana przez użytkownika interfejsu, a brakujące wartości (NULL) również powinny być w stanie znaleźć się w wyniku lub nie. Takie zapytanie powinno działać szybko na dużych zbiorach danych i być poprawne w kontekście semantyki SQL.
Nieprawidłowe łączenie warunków IN, NOT IN oraz sprawdzenie NULL prowadzi do nieoczekiwanych rezultatów, ponieważ w SQL wyrażenie "NULL IN (...)" zawsze zwraca UNKNOWN, a "NOT IN" może prowadzić do pustego wyniku, jeśli chociaż jeden z elementów listy to NULL. Wydajność na dużych danych przy źle napisanym warunku może gwałtownie spaść.
Poprawne łączenie wygląda następująco:
SELECT * FROM sales WHERE region IN ('Moscow','Samara') OR region IS NULL;
SELECT * FROM sales WHERE (region NOT IN ('Moscow','Samara') OR region IS NULL);
SELECT * FROM sales WHERE region NOT IN ('Moscow','Samara') AND region IS NOT NULL;
Kluczowe cechy:
Co się stanie, jeśli sprawdzimy "WHERE field NOT IN ('a', NULL)"?
Wynik zawsze będzie pusty, ponieważ każdy wiersz porównywany z NULL w liście daje UNKNOWN i filtruje wszystko.
Przykład kodu:
SELECT * FROM test WHERE name NOT IN ('Ivan', NULL);
Czy "IN (NULL,...)" zwróci wartość dla wierszy z NULL?
Nie. Wyrażenie NULL IN (...) zawsze zwraca UNKNOWN, a nie TRUE.
Jak przyspieszyć filtrowanie po dużej liście wartości?
Używać indeksów i preferencyjnie stosować JOIN z tabelą tymczasową, gdy lista jest duża (setki/tysiące wartości) — to przyspiesza filtrowanie w porównaniu do długiego IN.
W raporcie analitycznym zastosowano "WHERE city NOT IN ('London', field_2, NULL)". Efekt — całkowicie pusty wynik, nikt przez długi czas nie mógł zrozumieć przyczyny utraty wierszy.
Zalety:
Wady:
W CRM wybrano listę regionów klientów i dodano dodatkowy przycisk "Uwzględnij puste wartości". Logika filtru:
... WHERE city IN ('SPB','NNov') OR city IS NULL
Zalety:
Wady: