Почему выборка в условии запроса 1С с NULL ведет себя странно?

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

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

Суть проблемы и парадокс «пустого» NULL

Рассмотрим ситуацию, которая часто вводит в ступор: почему два очень похожих запроса возвращают принципиально разные результаты? Проанализируем первый пример кода:

ВЫБРАТЬ 1 ГДЕ НЕ 1 В (ВЫБРАТЬ NULL)

На первый взгляд, этот запрос не должен вернуть ничего, так как сравнение с NULL обычно дает неопределенность. Однако платформа 1С возвращает «1». Теперь посмотрим на второй пример, где мы просто добавили еще одно значение в подзапрос:

ВЫБРАТЬ 1 ГДЕ НЕ 1 В (ВЫБРАТЬ NULL ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2)

В этом случае результат будет пустым. Налицо странное поведение: добавление заведомо не подходящего значения «2» ломает логику получения результата «1». Разберемся подробнее, что происходит «под капотом» платформы.

Особенность оптимизатора 1С: Трансляция в SQL

Выясним причину такого поведения в первом случае. Когда транслятор запросов 1С видит подзапрос (ВЫБРАТЬ NULL), он применяет внутренние алгоритмы оптимизации. В определенных релизах платформы (например, 8.3.10 – 8.3.15) оптимизатор интерпретирует подзапрос, не возвращающий «реальных» данных, как пустое множество.

Посмотрим на логическую цепочку платформы:

  1. Платформа анализирует подзапрос (ВЫБРАТЬ NULL).
  2. Для оптимизации она считает, что раз в подзапросе нет конкретных значений, то условие 1 В (Подзапрос) всегда будет ложным (Ложь).
  3. Следовательно, НЕ (Ложь) превращается в Истина.

В итоге, если просмотреть реальный SQL-запрос, который платформа отправляет на СУБД (MS SQL Server или PostgreSQL), мы увидим нечто подобное:

SELECT 1.0 WHERE (NOT (1=0))

Так как NOT (1=0) — это всегда истина, мы получаем результат. Это специфика именно платформы 1С, а не правил SQL.

Трехзначная логика (Three-Valued Logic) и эффект «отравления» NULL

Проанализируем ситуацию во втором примере, когда результат становится пустым. Как только мы добавляем ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 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 встречается повсеместно.

← На главную