ПрограммированиеBI аналитик

Как оптимально реализовать условные выборки с фильтрацией по списку значений (IN/NOT IN) и отсутствующим данным (NULL) для аналитики в больших таблицах?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

История вопроса

Часто требуется формировать запросы, где критерии фильтрации определяются динамически — список значений задаётся пользователем интерфейса, а пропущенные значения (NULL) также должны уметь попадать или не попадать в результат. Такой запрос должен быстро работать на больших объёмах и быть корректным с точки зрения семантики SQL.

Проблема

Некорректное комбинирование условий IN, NOT IN и проверки на NULL приводит к неожиданным результатам из-за того, что в SQL выражение "NULL IN (...)" всегда возвращает UNKNOWN, а "NOT IN" может привести к пустому результату, если хотя бы один из элементов списка — NULL. Производительность на больших данных при плохо написанном условии может резко упасть.

Решение

Правильное комбинирование выглядит так:

  • Если выбираем значения из списка, дополняем условием на NULL если нужно вернуть и их:
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;
  • Для больших таблиц часто актуальны покрытия индексами, особенно если фильтр по списку — частая операция.

Ключевые особенности:

  • Управлять фильтрацией по NULL нужно явно, иначе теряем строки.
  • Правильное использование IN и NOT IN критично для корректных выборок.
  • Индексация по полям, участвующим в IN/NOT IN, сильно ускоряет фильтр.

Вопросы с подвохом.

Что произойдёт, если проверить "WHERE field NOT IN ('a', NULL)"?

Результат всегда пустой, поскольку любая строка сравнивается с NULL в списке, что даёт UNKNOWN и фильтрует всё.

Пример кода:

SELECT * FROM test WHERE name NOT IN ('Ivan', NULL);

Даст ли "IN (NULL,...)" значение для строк с NULL?

Нет. Выражение NULL IN (...) всегда возвращает UNKNOWN, а не TRUE.

Как ускорить фильтрацию по большому списку значений?

Использовать индексы и предпочтительно применять JOIN с временной таблицей, если список большой (сотни/тысячи значений) — это ускоряет фильтрацию по сравнению с длинным IN.

Типовые ошибки и анти-паттерны

  • Писать только IN/NOT IN без ЯВНОГО условия на NULL
  • Не учитывать особенности выполнения NOT IN с NULL
  • Не оптимизировать индексацию под поле фильтрации

Пример из жизни

Негативный кейс

В аналитическом отчёте применили "WHERE city NOT IN ('London', field_2, NULL)". Итог — полностью пустой результат, никто долго не мог понять причину потерянных строк.

Плюсы:

  • Короткий и "читабельный" запрос

Минусы:

  • Потеря данных, неочевидная логика для разработчиков

Позитивный кейс

В CRM выбрали список клиентских регионов и добавили дополнительную кнопку "Включить незаполненные значения". Логика фильтра:

... WHERE city IN ('SPB','NNov') OR city IS NULL

Плюсы:

  • Гибкая выборка, корректный учёт неполноты данных
  • Быстро работает за счёт индексирования

Минусы:

  • Нужно тестировать логику при каждом изменении модели