Как получить значение из другой таблицы в запросе 1С, если основное поле не заполнено?

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

В процессе разработки запросов в системе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда необходимо выбрать значение из одного источника, но если оно не заполнено (пустая ссылка), подставить данные из другой связанной таблицы. Распространенной ошибкой является попытка вставить вложенный запрос прямо в список выбора или в конструкцию ВЫБОР. Рассмотрим подробнее, почему это не работает и как реализовать задачу правильно.

Почему вложенный запрос в списке выбора возвращает «Булево»?

Разберем ситуацию, с которой столкнулся автор темы. В его коде использовалась конструкция:


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

Проанализируем этот фрагмент. Оператор В (аналог SQL-оператора IN) в языке запросов 1С является логическим оператором. Он предназначен исключительно для проверки условия: входит ли значение в набор данных, возвращаемый подзапросом или списком. Результатом такой проверки всегда будет логическое значение — Истина или Ложь (Булево). Именно поэтому в итоговой колонке вместо ссылки на справочник появлялся флаг ложности или истинности.

Важно помнить фундаментальное ограничение языка запросов 1С: в отличие от «чистого» T-SQL, платформа 1С не позволяет использовать вложенные запросы для получения конкретных значений внутри секции ВЫБРАТЬ или КОГДА ... ТОГДА. Вложенные запросы в 1С допустимы только в секции ИЗ (как источник данных) или в секции ГДЕ (как фильтр).

Правильный подход: использование ЛЕВОГО СОЕДИНЕНИЯ

Для решения задачи нам необходимо заранее подготовить данные из справочника и «приклеить» их к основной выборке. Проанализируем ситуацию: если в документе ТранспортноеСредство не указано, нам нужно найти машину, закрепленную за автором (водителем) в справочнике ТранспортныеСредства.

Рассмотрим алгоритм реализации по шагам:

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

Посмотрим на пример реализации кода:


ВЫБРАТЬ
    ВТ_1.Ссылка КАК Ссылка,
    ВТ_1.Автор КАК Автор,
    ВТ_1.ТранспортноеСредство КАК ТранспортИзДокумента,
    // Если в документе пусто, берем из справочника
    ЕСТЬNULL(ТранспортПоВодителю.Ссылка, ВТ_1.ТранспортноеСредство) КАК ТранспортИтоговый
ИЗ
    ВТ_1 КАК ВТ_1
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ТранспортныеСредства КАК ТранспортПоВодителю
        ПО ВТ_1.Автор = ТранспортПоВодителю.Водитель
            И ВТ_1.ТранспортноеСредство = ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.ПустаяСсылка)

Различие между NULL и Пустой ссылкой

Выясним важную техническую причину, по которой условия могут работать некорректно. В 1С существует два понятия «отсутствия» данных:

Пустая ссылка — это объектный тип данных. Она реально хранится в базе данных, если реквизит типа СправочникСсылка просто не заполнен пользователем. Она не равна NULL.

NULL — это специальный тип данных, который появляется в запросе только в результате ЛЕВОГО СОЕДИНЕНИЯ, если во второй (правой) таблице не нашлось подходящей записи.

В нашем случае, если мы хотим проверить, заполнено ли поле в документе, мы сравниваем его с ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.Пустаяссылка). А если мы проверяем результат соединения, мы используем ЕСТЬNULL.

Проблема дублирования строк (умножение выборки)

Проанализируем ситуацию, когда у одного водителя в справочнике может быть закреплено несколько транспортных средств. Если мы просто выполним ЛЕВОЕ СОЕДИНЕНИЕ, количество строк в итоговом отчете увеличится — на одну строку документа наложится несколько строк из справочника машин. Это критическая ошибка, которая приведет к неверным итогам.

Чтобы избежать этой проблемы, рекомендуется предварительно подготовить таблицу связей «Водитель — Машина», оставив только уникальные записи. Рассмотрим пример подготовки такой таблицы:


ВЫБРАТЬ
    ТранспортныеСредства.Водитель КАК Водитель,
    МАКСИМУМ(ТранспортныеСредства.Ссылка) КАК Ссылка
ПОМЕСТИТЬ ВТ_ОсновныеМашины
ИЗ
    Справочник.ТранспортныеСредства КАК ТранспортныеСредства
ГДЕ
    ТранспортныеСредства.Водитель <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
СГРУППИРОВАТЬ ПО
    ТранспортныеСредства.Водитель
;

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

Аналог функции COALESCE в 1С

В классическом SQL существует функция COALESCE, которая возвращает первый не-NULL аргумент из списка. В 1С функция ЕСТЬNULL ограничена только двумя аргументами. Однако мы можем имитировать поведение COALESCE, вкладывая функции друг в друга. Это полезно, если у вас более двух источников данных (например: сначала из документа, потом из личной машины водителя, потом дежурный автомобиль подразделения).

Пример вложенности функций:

ЕСТЬNULL(МашинаИзДокумента, ЕСТЬNULL(МашинаВодителя, ДежурнаяМашина))

Важный момент: функция ЕСТЬNULL работает быстрее, чем конструкция ВЫБОР ... КОГДА ... ТОГДА, так как она транслируется напрямую в соответствующую функцию SQL на уровне СУБД. Рекомендуется отдавать предпочтение именно ей, когда речь идет о проверке на NULL.

Оптимизация запроса

При использовании соединений по полям типа СправочникСсылка (как наш Автор или ТранспортноеСредство), 1С работает достаточно эффективно. Однако стоит обратить внимание на следующие аспекты:

Подводя итог, можно сказать: замена подзапроса на левое соединение — это стандарт «хорошего тона» и техническая необходимость в 1С. Это делает код читаемым, производительным и, самое главное, работающим в соответствии с типизацией платформы.

← На главную