Почему в запросе 1С не срабатывает проверка на НЕОПРЕДЕЛЕНО при левом соединении и как правильно обрабатывать пустые значения?

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

В практике разработки на платформе 1С:Предприятие 8 часто возникает задача: получить данные из одной таблицы (например, регистра сведений), а если данных там нет — подставить значение по умолчанию из другой таблицы (например, из справочника). Однако начинающие разработчики часто сталкиваются с «необъяснимым» поведением оператора ВЫБОР при попытке сравнить поле с литералом НЕОПРЕДЕЛЕНО. Проанализируем, почему так происходит, и разберем правильные подходы к решению этой задачи.

Разбор ситуации: почему равенство не работает, а неравенство — да?

Рассмотрим ситуацию, описанную в проблеме. Автор пытается выполнить левое соединение справочника Машины с виртуальной таблицей СрезПоследних регистра сведений. Если записи в регистре нет, он хочет взять дату из справочника.

Вариант 1 (не работает):


ВЫБРАТЬ
    Машины.Ссылка КАК Машина,
    ВЫБОР
        КОГДА СостоянияМашинСрезПоследних.Регистратор = НЕОПРЕДЕЛЕНО
            ТОГДА Машины.DateНачалаЭксплуатации    
        ИНАЧЕ СостоянияМашинСрезПоследних.Регистратор.Дата
    КОНЕЦ КАК ДатаД
ИЗ
    Справочник.Машины КАК Машины
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияМашин.СрезПоследних(&Дата) КАК СостоянияМашинСрезПоследних 
    ПО СостоянияМашинСрезПоследних.Машина = Машины.Ссылка

В этом случае, если записи в регистре нет, условие Регистратор = НЕОПРЕДЕЛЕНО не выполняется, и система уходит в ветку ИНАЧЕ, возвращая пустоту.

Вариант 2 (работает, но случайно):


ВЫБОР
    КОГДА СостоянияМашинСрезПоследних.Регистратор <> НЕОПРЕДЕЛЕНО
        ТОГДА СостоянияМашинСрезПоследних.Регистратор.Дата
    ИНАЧЕ Машины.ДатаНачалаЭксплуатации    
КОНЕЦ

Этот код отрабатывает «как задумано», но не потому, что условие верно. Выясним причину этого парадокса. Все дело в трехзначной логике SQL и разнице между NULL и НЕОПРЕДЕЛЕНО.

В чем разница между NULL и НЕОПРЕДЕЛЕНО?

Для программиста 1С важно четко разделять эти понятия, особенно в контексте запросов:

  1. НЕОПРЕДЕЛЕНО — это конкретное значение типа Неопределено. Оно может храниться в базе данных (в полях составного типа) или использоваться во встроенном языке.
  2. NULL — это отсутствие значения. Оно не является типом данных. NULL появляется в запросах в двух основных случаях: при использовании левого или полного соединения (когда в правой таблице нет соответствующей строки) и при агрегатных функциях над пустой выборкой.

В регистре сведений поле Регистратор никогда не может содержать значение НЕОПРЕДЕЛЕНО. Оно либо содержит ссылку на документ, либо, если запись при левом соединении не найдена, вся строка таблицы регистра заполняется значениями NULL.

Трехзначная логика и сравнение с NULL

В языке запросов 1С результат логического выражения может быть не только Истина или Ложь, но и Неизвестно (Unknown). Любое сравнение с NULL (будь то =, <>, > или <) всегда возвращает результат Неизвестно.

Проанализируем наши примеры с точки зрения этой логики:

Таким образом, второй вариант сработал только потому, что нужный результат был помещен в секцию ИНАЧЕ, а само условие сравнения никогда не было истинным.

Правильные способы обработки отсутствующих значений

Рассмотрим, как правильно написать такой запрос, чтобы он был надежным и понятным.

Способ 1: Использование оператора ЕСТЬ NULL

Для проверки на «пустоту» в результате соединения необходимо использовать специальный оператор ЕСТЬ NULL (IS NULL). Он единственный корректно обрабатывает это значение.


ВЫБОР
    КОГДА СостоянияМашинСрезПоследних.Регистратор ЕСТЬ NULL
        ТОГДА Машины.ДатаНачалаЭксплуатации    
    ИНАЧЕ СостоянияМашинСрезПоследних.Регистратор.Дата
КОНЕЦ КАК ДатаД

Способ 2: Использование функции ЕСТЬNULL()

Это самый лаконичный и производительный способ. Функция ЕСТЬNULL(Поле, ЗначениеЗамены) проверяет первое выражение на NULL и, если это так, возвращает второе выражение.


ВЫБРАТЬ
    Машины.Ссылка КАК Машина,
    ЕСТЬNULL(СостоянияМашинСрезПоследних.Регистратор.Дата, Машины.ДатаНачалаЭксплуатации) КАК ДатаД
ИЗ
    Справочник.Машины КАК Машины
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияМашин.СрезПоследних(&Дата) КАК СостоянияМашинСрезПоследних 
    ПО СостоянияМашинСрезПоследних.Машина = Машины.Ссылка

Важное замечание: При обращении через точку от NULL (например, Регистратор.Дата, когда Регистратор равен NULL), результатом всего выражения также будет NULL. Поэтому функция ЕСТЬNULL отлично справляется с этой цепочкой.

Особенности консоли запросов

Часто путаница возникает из-за визуального отображения в консоли запросов. Многие консоли отображают и NULL, и НЕОПРЕДЕЛЕНО, и пустую ссылку одинаково — в виде пустой ячейки или текста «Неопределено». Чтобы точно выяснить, что находится в поле, рекомендуется использовать функции проверки типа в самом запросе:


ВЫБРАТЬ
    ТИПЗНАЧЕНИЯ(СостоянияМашинСрезПоследних.Регистратор) КАК ТипПоля
...

Это поможет вам убедиться, что вы работаете именно с NULL, возникшим в результате левого соединения.

Подведем итоги

Проанализировав ситуацию, мы выяснили: Во-первых, при левом соединении отсутствие записи всегда дает NULL, а не НЕОПРЕДЕЛЕНО. Во-вторых, обычные операторы сравнения (=, <>) не работают с NULL так, как мы ожидаем. В-третьих, для корректной работы всегда следует использовать ЕСТЬ NULL в конструкции ВЫБОР или специализированную функцию ЕСТЬNULL().

← На главную