Почему в запросе 1С не срабатывает условие "НЕ В (Список)" и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработчики 1С часто сталкиваются с ситуацией, когда запрос с оператором В работает ожидаемо, а противоположное условие НЕ В (или NOT IN) внезапно возвращает пустой результат, хотя в базе точно есть записи, не входящие в список. Давайте подробно разберем, почему это происходит, как работает троичная логика SQL и какие способы решения этой проблемы существуют.

Суть проблемы: когда логика дает сбой

Рассмотрим типичную ситуацию, описанную в теме. У нас есть справочник сотрудников и временная таблица, в которой собрана часть этих сотрудников. Наша задача — получить всех сотрудников, которых нет в этой временной таблице. Проанализируем пример кода, который приводит к ошибке:


ВЫБРАТЬ
    Сотрудники.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВременнаяТаблица
ИЗ
    Справочник.Сотрудники КАК Сотрудники
 ОБЪЕДИНИТЬ ВСЕ
 ВЫБРАТЬ
    NULL
;
 ////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Сотрудники.Ссылка КАК Ссылка
ИЗ
    Справочник.Сотрудники КАК Сотрудники
ГДЕ
    НЕ Сотрудники.Ссылка В
                (ВЫБРАТЬ
                    ВременнаяТаблица.Ссылка
                ИЗ
                    ВременнаяТаблица)

В этом примере во временную таблицу намеренно добавлено значение NULL. В результате основной запрос вернет пустую выборку, даже если в справочнике сотни сотрудников, не попавших в таблицу. Почему так происходит? Ответ кроется в особенностях обработки неопределенных значений в языке запросов.

Разбор причины: Троичная логика (Three-Valued Logic)

В обычных языках программирования мы привыкли к бинарной логике: выражение может быть либо Истиной, либо Ложью. Однако в SQL существует третье состояние — «Неизвестно» (UNKNOWN), которое неразрывно связано с типом NULL.

Когда мы используем конструкцию Ссылка НЕ В (Значение1, Значение2, NULL), СУБД разворачивает её в последовательность сравнений через оператор И (AND):

Ссылка <> Значение1 И Ссылка <> Значение2 И Ссылка <> NULL

Проанализируем выполнение этого условия:

  1. Сравнение любого значения с NULL (даже самого NULL) всегда возвращает результат UNKNOWN.
  2. Согласно таблице истинности оператора И, если хотя бы один из операндов равен UNKNOWN, а остальные TRUE, то и весь результат будет UNKNOWN.
  3. Оператор ГДЕ (WHERE) включает в итоговую выборку только те строки, для которых условие вычисляется как ИСТИНА. Значения ЛОЖЬ и UNKNOWN отсекаются.

Таким образом, наличие хотя бы одного NULL в подзапросе при использовании НЕ В «отравляет» весь результат, делая условие невыполнимым для любой строки.

Почему вариант с "ВЫБОР" работал?

Автор темы заметил, что использование конструкции ВЫБОР решало проблему. Давайте выясним причину:


ГДЕ ВЫБОР 
    КОГДА АктыБиллинг.Ссылка В (ВЫБРАТЬ АктБиллинг ИЗ АктыБУ) ТОГДА ЛОЖЬ 
    ИНАЧЕ ИСТИНА 
КОНЕЦ

Здесь оператор В (IN) работает по-другому. Если соответствие найдено среди не-NULL значений, возвращается ИСТИНА. Если же соответствия нет, но в списке есть NULL, возвращается UNKNOWN. В конструкции ВЫБОР ветка ИНАЧЕ срабатывает во всех случаях, когда условие КОГДА не является истинным (то есть и для ЛОЖЬ, и для UNKNOWN). Именно поэтому такой обходной путь сработал, но он не является оптимальным.

Решение №1: Исключение NULL из подзапроса

Самый простой и логичный способ — гарантировать, что в списке для сравнения нет неопределенных значений. Мы можем добавить фильтрацию прямо в подзапрос. Рассмотрим исправленный вариант:


ГДЕ
    Сотрудники.Ссылка НЕ В
        (ВЫБРАТЬ
            ВременнаяТаблица.Ссылка
        ИЗ
            ВременнаяТаблица
        ГДЕ
            ВременнаяТаблица.Ссылка ЕСТЬ НЕ NULL)

Это решение эффективно, если вы работаете с небольшими списками или временными таблицами. Однако в 1С есть более профессиональные подходы.

Решение №2: Использование ЛЕВОГО СОЕДИНЕНИЯ (Рекомендуется)

Использование ЛЕВОГО СОЕДИНЕНИЯ с последующей проверкой на NULL — это классический прием в 1С, который зачастую работает быстрее, чем подзапросы с IN. Проанализируем, как переписать наш запрос:


ВЫБРАТЬ
    Сотрудники.Ссылка КАК Ссылка
ИЗ
    Справочник.Сотрудники КАК Сотрудники
ЛЕВОЕ СОЕДИНЕНИЕ ВременнаяТаблица КАК ВТ
    ПО Сотрудники.Ссылка = ВТ.Ссылка
ГДЕ
    ВТ.Ссылка ЕСТЬ NULL

Преимущества этого метода:

Решение №3: Оператор ГДЕ НЕ СУЩЕСТВУЕТ (NOT EXISTS)

Еще один надежный способ избежать проблем с троичной логикой — использование оператора СУЩЕСТВУЕТ. Этот оператор всегда возвращает только ИСТИНА или ЛОЖЬ, игнорируя неопределенность.


ВЫБРАТЬ
    Сотрудники.Ссылка
ИЗ
    Справочник.Сотрудники КАК Сотрудники
ГДЕ
    НЕ СУЩЕСТВУЕТ
        (ВЫБРАТЬ
            1
        ИЗ
            ВременнаяТаблица КАК ВТ
        ГДЕ
            ВТ.Ссылка = Сотрудники.Ссылка)

В данном случае подзапрос проверяет только сам факт наличия записи. Даже если в ВТ.Ссылка будет NULL, условие связи ВТ.Ссылка = Сотрудники.Ссылка просто не выполнится, и оператор СУЩЕСТВУЕТ вернет ЛОЖЬ, что после инверсии НЕ даст нам нужную ИСТИНУ.

Подведем итоги и рекомендации

Проанализировав ситуацию, мы можем сформулировать «золотые правила» написания запросов на исключение в 1С:

  1. Избегайте NOT IN, если вы не уверены на 100%, что в источнике данных нет значений NULL. Это особенно актуально для внешних источников или таблиц, сформированных сложными объединениями.
  2. Используйте ЛЕВОЕ СОЕДИНЕНИЕ как основной инструмент для поиска отсутствующих записей. Это наиболее производительный и безопасный метод в экосистеме 1С.
  3. Проверяйте данные при помещении во временные таблицы. Хорошим тоном считается использование ЕСТЬ НЕ NULL еще на этапе формирования временной таблицы, чтобы исключить передачу «мусорных» данных в последующие части запроса.

Понимание троичной логики SQL позволяет не только исправлять подобные ошибки, но и проектировать более надежные механизмы обработки данных в ваших конфигурациях.

← На главную