programowanieAnalityk BI

Jak optymalnie wdrożyć warunkowe selekcje z filtrowaniem po liście wartości (IN/NOT IN) oraz danymi brakującymi (NULL) do analizy w dużych tabelach?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

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.

Problem

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ść.

Rozwiązanie

Poprawne łączenie wygląda następująco:

  • Jeśli wybieramy wartości z listy, uzupełniamy warunkiem na NULL, jeśli musimy je zwrócić:
SELECT * FROM sales WHERE region IN ('Moscow','Samara') OR region IS NULL;
  • Dla negacji używamy jawnej logiki:
SELECT * FROM sales WHERE (region NOT IN ('Moscow','Samara') OR region IS NULL);
  • Dla ścisłego wykluczenia braków:
SELECT * FROM sales WHERE region NOT IN ('Moscow','Samara') AND region IS NOT NULL;
  • Dla dużych tabel często ważne są pokrycia indeksami, szczególnie jeśli filtr po liście jest częstą operacją.

Kluczowe cechy:

  • Zarządzanie filtrowaniem po NULL musi być jawne, w przeciwnym razie tracimy wiersze.
  • Poprawne używanie IN i NOT IN jest krytyczne dla poprawnych selekcji.
  • Indeksacja po polach biorących udział w IN/NOT IN znacznie przyspiesza filtr.

Pytania z pułapką.

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.

Typowe błędy i antywzorce

  • Pisanie tylko IN/NOT IN bez JAWNEGO warunku na NULL
  • Nie branie pod uwagę szczególności wykonania NOT IN z NULL
  • Nieoptymalizowanie indeksacji pod pole filtrowania

Przykład z życia

Negatywny przypadek

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:

  • Krótkie i "czytelne" zapytanie

Wady:

  • Utrata danych, nieoczywista logika dla programistów

Pozytywny przypadek

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:

  • Elastyczna selekcja, poprawne uwzględnienie niepełności danych
  • Działa szybko dzięki indeksowaniu

Wady:

  • Należy testować logikę przy każdej zmianie modelu