Как правильно проверить и обработать NULL в запросе 1С

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

При соединении таблиц в языке запросов 1С разработчики часто сталкиваются со значениями NULL. Типичная задача — выбрать значение из основной таблицы, а если запись или реквизит отсутствуют, подставить данные из соединенной таблицы или значение по умолчанию. Разберем по шагам, как устроена обработка NULL, почему возникают логические ошибки и как составить оптимальный запрос.

В чем разница между NULL и пустой ссылкой

Разберем фундаментальное отличие, из-за которого часто возникают ошибки в выборках. В платформе 1С:Предприятие NULL и пустая ссылка справочника (например, ЗНАЧЕНИЕ(Справочник.РабочиеМеста.ПустаяСсылка)) — это абсолютно разные типы данных:

  1. NULL — это признак полного отсутствия данных или отсутствия соответствующей строки при внешнем соединении (ЛЕВОЕ, ПРАВОЕ, ПОЛНОЕ).
  2. Пустая ссылка — это существующее ссылочное значение конкретного типа, указывающее на незаполненный реквизит в существующей записи базы данных.

Если применить оператор сравнения КОГДА Поле <> ЗНАЧЕНИЕ(Справочник.РабочиеМеста.ПустаяСсылка) к значению NULL, то результатом сравнения будет не Истина, а UNKNOWN (неопределенность), которая в логических ветках воспринимается как Ложь. Поэтому обычное сравнение с пустой ссылкой не обрабатывает отсутствующие записи.

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

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

Рассмотрим синтаксис функции:

ЕСТЬNULL(ПроверяемоеПоле, ЗначениеЗамены)

Если ПроверяемоеПоле равно NULL, функция вернет ЗначениеЗамены. Если же в поле находится любое другое значение (включая пустую ссылку), функция вернет само ПроверяемоеПоле без изменений.

Решение 1: Базовое использование ЕСТЬNULL при полном соединении

Посмотрим на пример, когда необходимо получить данные физического лица из справочников ИТ_КонтактныеЛица и Сотрудники, при этом физическое лицо может присутствовать только в одном из них или в обоих сразу. Напишем запрос с обработкой отсутствующих значений через ЕСТЬNULL:


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

Обратим внимание на условие в секции ГДЕ: обращение ФизЛицо.Ссылка = &Ссылка является избыточным, так как реквизит ФизЛицо уже содержит ссылку. Дополнительное разыменование через точку порождает лишнее соединение на уровне СУБД, поэтому пишем просто ФизЛицо = &Ссылка.

Решение 2: Одновременная проверка на NULL и пустую ссылку

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

Для корректного решения объединим проверку ЕСТЬ NULL и проверку на равенство пустой ссылке через конструкцию ВЫБОР:


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

Решение 3: Оптимальная структура запроса от ведущей таблицы

Конструкция с ПОЛНОЕ СОЕДИНЕНИЕ и условием ИЛИ в секции ГДЕ часто приводит к неоптимальному плану выполнения на больших объемах данных, так как СУБД выполняет полное сканирование таблиц.

Если поиск выполняется по конкретному физическому лицу, построим запрос от ведущей таблицы — справочника ФизическиеЛица — с последующим левым соединением подчиненных справочников:


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

Такой подход обеспечивает использование первичных индексов СУБД, устраняет необходимость сложных конструкций с условием ИЛИ и гарантирует однозначную подстановку данных.

← На главную