В практике разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с ситуациями, когда язык запросов ведет себя не совсем так, как ожидается от классического SQL. Одной из самых коварных тем является использование значения NULL внутри операторов В (IN) и НЕ В (NOT IN). Разберем по шагам, почему возникают логические парадоксы и как правильно писать условия, чтобы избежать пустых результатов или ложных срабатываний.
Рассмотрим ситуацию, которая часто вводит в ступор: почему два очень похожих запроса возвращают принципиально разные результаты? Проанализируем первый пример кода:
ВЫБРАТЬ 1 ГДЕ НЕ 1 В (ВЫБРАТЬ NULL)
На первый взгляд, этот запрос не должен вернуть ничего, так как сравнение с NULL обычно дает неопределенность. Однако платформа 1С возвращает «1». Теперь посмотрим на второй пример, где мы просто добавили еще одно значение в подзапрос:
ВЫБРАТЬ 1 ГДЕ НЕ 1 В (ВЫБРАТЬ NULL ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2)
В этом случае результат будет пустым. Налицо странное поведение: добавление заведомо не подходящего значения «2» ломает логику получения результата «1». Разберемся подробнее, что происходит «под капотом» платформы.
Выясним причину такого поведения в первом случае. Когда транслятор запросов 1С видит подзапрос (ВЫБРАТЬ NULL), он применяет внутренние алгоритмы оптимизации. В определенных релизах платформы (например, 8.3.10 – 8.3.15) оптимизатор интерпретирует подзапрос, не возвращающий «реальных» данных, как пустое множество.
Посмотрим на логическую цепочку платформы:
(ВЫБРАТЬ NULL).1 В (Подзапрос) всегда будет ложным (Ложь).НЕ (Ложь) превращается в Истина.В итоге, если просмотреть реальный SQL-запрос, который платформа отправляет на СУБД (MS SQL Server или PostgreSQL), мы увидим нечто подобное:
SELECT 1.0 WHERE (NOT (1=0))
Так как NOT (1=0) — это всегда истина, мы получаем результат. Это специфика именно платформы 1С, а не правил SQL.
Проанализируем ситуацию во втором примере, когда результат становится пустым. Как только мы добавляем ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2, подзапрос перестает казаться оптимизатору 1С «пустым». Теперь он содержит реальное значение, и запрос передается в СУБД практически «как есть».
Здесь вступают в силу стандартные правила SQL. Оператор NOT IN (в 1С это НЕ В) эквивалентен последовательному сравнению через «НЕ РАВНО» и логическое «И» (AND). Разберем по шагам трансформацию условия НЕ 1 В (NULL, 2):
(1 <> NULL) И (1 <> 2)
В SQL любое сравнение с NULL возвращает результат UNKNOWN (неизвестно). Таким образом, выражение принимает вид:
UNKNOWN И TRUE
Согласно таблице истинности трехзначной логики, UNKNOWN AND TRUE = UNKNOWN. Предложение WHERE возвращает строку только в том случае, если итоговое условие равно TRUE. Если результат FALSE или UNKNOWN, строка отсекается. Именно поэтому результат запроса оказывается пустым.
Чтобы избежать подобных сюрпризов и сделать код надежным вне зависимости от версии платформы или настроек СУБД, рассмотрим основные правила работы с условиями.
1. Всегда исключайте NULL из подзапросов для оператора В
Если вы используете конструкцию В (ВЫБРАТЬ ...), обязательно добавляйте фильтрацию неопределенных значений. Это гарантирует предсказуемое поведение оператора НЕ В.
ВЫБРАТЬ
Номенклатура.Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка НЕ В (
ВЫБРАТЬ
Продажи.Номенклатура
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Номенклатура ЕСТЬ НЕ NULL
)
2. Использование конструкции ГДЕ НЕ СУЩЕСТВУЕТ (EXISTS)
Наиболее правильным и производительным способом проверки отсутствия данных является оператор ГДЕ НЕ СУЩЕСТВУЕТ. Он работает по двузначной логике: запись либо найдена, либо нет. Значения NULL внутри подзапроса никак не влияют на результат выполнения EXISTS.
ВЫБРАТЬ
Товары.Ссылка
ИЗ
Справочник.Номенклатура КАК Товары
ГДЕ
НЕ СУЩЕСТВУЕТ (
ВЫБРАТЬ 1
ИЗ
РегистрНакопления.Остатки КАК Остатки
ГДЕ
Остатки.Номенклатура = Товары.Ссылка
)
3. Применение ЛЕВОГО СОЕДИНЕНИЯ
Альтернативный классический метод — использование ЛЕВОЕ СОЕДИНЕНИЕ с проверкой на ЕСТЬ NULL в правой таблице. Это поведение абсолютно прозрачно и не зависит от оптимизаторов подзапросов.
ВЫБРАТЬ
Номенклатура.Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрНакопления.Продажи КАК Продажи
ПО
Номенклатура.Ссылка = Продажи.Номенклатура
ГДЕ
Продажи.Номенклатура ЕСТЬ NULL
Подведем итог нашему анализу:
НЕ 1 В (ВЫБРАТЬ NULL) в 1С является следствием специфической оптимизации транслятора, который упрощает условие до Истина.NULL делает всё условие НЕ В неопределенным (пустым).НЕ В на НЕ СУЩЕСТВУЕТ или использовать фильтрацию ЕСТЬ НЕ NULL внутри подзапросов.Понимание этих тонкостей работы платформы 1С позволяет избегать трудноуловимых ошибок в бизнес-логике отчетов и обработок, особенно при работе со сложными схемами данных, где NULL встречается повсеместно.