В процессе разработки запросов в системе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда необходимо выбрать значение из одного источника, но если оно не заполнено (пустая ссылка), подставить данные из другой связанной таблицы. Распространенной ошибкой является попытка вставить вложенный запрос прямо в список выбора или в конструкцию ВЫБОР. Рассмотрим подробнее, почему это не работает и как реализовать задачу правильно.
Разберем ситуацию, с которой столкнулся автор темы. В его коде использовалась конструкция:
КОГДА ВТ_1.ТранспортноеСредство = ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.Пустаяссылка)
ТОГДА ВТ_1.ТранспортноеСредство В (ВЫБРАТЬ ...)
Проанализируем этот фрагмент. Оператор В (аналог SQL-оператора IN) в языке запросов 1С является логическим оператором. Он предназначен исключительно для проверки условия: входит ли значение в набор данных, возвращаемый подзапросом или списком. Результатом такой проверки всегда будет логическое значение — Истина или Ложь (Булево). Именно поэтому в итоговой колонке вместо ссылки на справочник появлялся флаг ложности или истинности.
Важно помнить фундаментальное ограничение языка запросов 1С: в отличие от «чистого» T-SQL, платформа 1С не позволяет использовать вложенные запросы для получения конкретных значений внутри секции ВЫБРАТЬ или КОГДА ... ТОГДА. Вложенные запросы в 1С допустимы только в секции ИЗ (как источник данных) или в секции ГДЕ (как фильтр).
Для решения задачи нам необходимо заранее подготовить данные из справочника и «приклеить» их к основной выборке. Проанализируем ситуацию: если в документе ТранспортноеСредство не указано, нам нужно найти машину, закрепленную за автором (водителем) в справочнике ТранспортныеСредства.
Рассмотрим алгоритм реализации по шагам:
ТранспортныеСредства с помощью ЛЕВОЕ СОЕДИНЕНИЕ.Автор (из основной таблицы) и Водитель (из справочника).ЕСТЬNULL или конструкцию ВЫБОР, чтобы отдать приоритет тому или иному значению.Посмотрим на пример реализации кода:
ВЫБРАТЬ
ВТ_1.Ссылка КАК Ссылка,
ВТ_1.Автор КАК Автор,
ВТ_1.ТранспортноеСредство КАК ТранспортИзДокумента,
// Если в документе пусто, берем из справочника
ЕСТЬNULL(ТранспортПоВодителю.Ссылка, ВТ_1.ТранспортноеСредство) КАК ТранспортИтоговый
ИЗ
ВТ_1 КАК ВТ_1
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ТранспортныеСредства КАК ТранспортПоВодителю
ПО ВТ_1.Автор = ТранспортПоВодителю.Водитель
И ВТ_1.ТранспортноеСредство = ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.ПустаяСсылка)
Выясним важную техническую причину, по которой условия могут работать некорректно. В 1С существует два понятия «отсутствия» данных:
Пустая ссылка — это объектный тип данных. Она реально хранится в базе данных, если реквизит типа СправочникСсылка просто не заполнен пользователем. Она не равна NULL.
NULL — это специальный тип данных, который появляется в запросе только в результате ЛЕВОГО СОЕДИНЕНИЯ, если во второй (правой) таблице не нашлось подходящей записи.
В нашем случае, если мы хотим проверить, заполнено ли поле в документе, мы сравниваем его с ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.Пустаяссылка). А если мы проверяем результат соединения, мы используем ЕСТЬNULL.
Проанализируем ситуацию, когда у одного водителя в справочнике может быть закреплено несколько транспортных средств. Если мы просто выполним ЛЕВОЕ СОЕДИНЕНИЕ, количество строк в итоговом отчете увеличится — на одну строку документа наложится несколько строк из справочника машин. Это критическая ошибка, которая приведет к неверным итогам.
Чтобы избежать этой проблемы, рекомендуется предварительно подготовить таблицу связей «Водитель — Машина», оставив только уникальные записи. Рассмотрим пример подготовки такой таблицы:
ВЫБРАТЬ
ТранспортныеСредства.Водитель КАК Водитель,
МАКСИМУМ(ТранспортныеСредства.Ссылка) КАК Ссылка
ПОМЕСТИТЬ ВТ_ОсновныеМашины
ИЗ
Справочник.ТранспортныеСредства КАК ТранспортныеСредства
ГДЕ
ТранспортныеСредства.Водитель <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
СГРУППИРОВАТЬ ПО
ТранспортныеСредства.Водитель
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_1.Ссылка,
ВЫБОР
КОГДА ВТ_1.ТранспортноеСредство = ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.ПустаяСсылка)
ТОГДА ЕСТЬNULL(ВТ_ОсновныеМашины.Ссылка, ЗНАЧЕНИЕ(Справочник.ТранспортныеСредства.ПустаяСсылка))
ИНАЧЕ ВТ_1.ТранспортноеСредство
КОНЕЦ КАК Транспорт
ИЗ
ВТ_1 КАК ВТ_1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ОсновныеМашины КАК ВТ_ОсновныеМашины
ПО ВТ_1.Автор = ВТ_ОсновныеМашины.Водитель
В классическом SQL существует функция COALESCE, которая возвращает первый не-NULL аргумент из списка. В 1С функция ЕСТЬNULL ограничена только двумя аргументами. Однако мы можем имитировать поведение COALESCE, вкладывая функции друг в друга. Это полезно, если у вас более двух источников данных (например: сначала из документа, потом из личной машины водителя, потом дежурный автомобиль подразделения).
Пример вложенности функций:
ЕСТЬNULL(МашинаИзДокумента, ЕСТЬNULL(МашинаВодителя, ДежурнаяМашина))
Важный момент: функция ЕСТЬNULL работает быстрее, чем конструкция ВЫБОР ... КОГДА ... ТОГДА, так как она транслируется напрямую в соответствующую функцию SQL на уровне СУБД. Рекомендуется отдавать предпочтение именно ей, когда речь идет о проверке на NULL.
При использовании соединений по полям типа СправочникСсылка (как наш Автор или ТранспортноеСредство), 1С работает достаточно эффективно. Однако стоит обратить внимание на следующие аспекты:
Водитель в справочнике ТранспортныеСредства), должно быть проиндексировано. Обычно ссылки индексируются платформой автоматически, если они являются реквизитами, но проверьте свойство «Индексировать» в конфигураторе.ВЫРАЗИТЬ(Поле КАК Справочник.Название). Это позволит оптимизатору SQL построить более эффективный план запроса, исключив лишние проверки типов.Подводя итог, можно сказать: замена подзапроса на левое соединение — это стандарт «хорошего тона» и техническая необходимость в 1С. Это делает код читаемым, производительным и, самое главное, работающим в соответствии с типизацией платформы.