В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача сделать логику запроса гибкой. Например, когда необходимо присоединить данные из одного или другого регистра сведений в зависимости от внешнего условия (флага или параметра). На первый взгляд кажется, что язык запросов 1С статичен, но существует несколько эффективных приемов, позволяющих реализовать «условное соединение». Рассмотрим их подробно, проанализируем влияние на производительность и выберем оптимальный вариант для разных ситуаций.
Самый простой способ, который позволяет оставить текст запроса неизменным и при этом управлять логикой соединений — это добавление параметра в условие связи таблиц. Суть метода заключается в том, что мы прописываем соединение со всеми возможными таблицами, но «включаем» только нужное нам, делая условие связи заведомо ложным для остальных.
Проанализируем пример кода, где выбор между двумя регистрами (РС-1 и РС-2) зависит от параметра &СоединятьСПервымРегистром:
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
| МойСправочник.Код КАК Код,
| МойСправочник.Наименование КАК Наименование,
| ВЫБОР
| КОГДА &СоединятьСПервымРегистром ТОГДА МойРС1.Реквизит
| ИНАЧЕ МойРС2.Реквизит
| КОНЕЦ КАК РеквизитРегистра
|ИЗ
| Справочник.Номенклатура КАК МойСправочник
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПервыйРегистр КАК МойРС1
| ПО &СоединятьСПервымРегистром
| И МойСправочник.Ссылка = МойРС1.Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ВторойРегистр КАК МойРС2
| ПО (НЕ &СоединятьСПервымРегистром)
| И МойСправочник.Ссылка = МойРС2.Номенклатура";
Запрос.УстановитьПараметр("СоединятьСПервымРегистром", ЛогическоеУсловие);
Разберем, как это работает. Если параметр &СоединятьСПервымРегистром равен Истина, то первое соединение выполняется по стандартным правилам индексации, а второе соединение всегда получает условие ЛОЖЬ (так как НЕ Истина есть Ложь). В результате из второй таблицы не будет выбрано ни одной записи, и поля МойРС2 будут содержать NULL.
Важный нюанс производительности: Современные оптимизаторы СУБД (MS SQL Server, PostgreSQL) достаточно эффективно обрабатывают такие конструкции. Если условие соединения всегда ЛОЖЬ, система может применить так называемый Constant Scan и фактически не обращаться к данным таблицы. Однако при использовании параметров может возникнуть эффект Parameter Sniffing, когда СУБД строит план выполнения, ориентируясь на первое значение параметра, что не всегда оптимально для последующих вызовов.
Если мы хотим быть абсолютно уверены, что лишние таблицы не участвуют в обработке, или если структура соединений слишком сложна для одного текста, мы можем формировать текст запроса динамически. Этот метод часто пугает разработчиков тем, что запрос перестает открываться в Конструкторе запросов. Но есть хитрость: использование функции СтрЗаменить.
Посмотрим на алгоритм реализации:
РС1).Пример реализации этого подхода:
ТекстЗапроса =
"ВЫБРАТЬ
| Спр.Ссылка,
| ДанныеРегистра.ПолеЗначение
|ИЗ
| Справочник.Контрагенты КАК Спр
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОсновнойРегистр КАК ДанныеРегистра
| ПО Спр.Ссылка = ДанныеРегистра.Объект";
Если НужноИспользоватьАльтернативныйРегистр Тогда
ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
"РегистрСведений.ОсновнойРегистр",
"РегистрСведений.ДополнительныйРегистр");
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапроса);
Этот способ гарантирует, что СУБД получит максимально «чистый» запрос без лишних логических ветвлений. К тому же, такой текст легко отлаживать в консоли запросов.
Рассмотрим ситуацию, когда соединение по условию приводит к существенному замедлению. В таких случаях профессиональным стандартом является разделение запроса на две части с их последующим объединением. Это позволяет СУБД построить два простых и быстрых плана выполнения вместо одного сложного.
Пример структуры такого запроса:
"ВЫБРАТЬ
| Спр.Ссылка,
| МойРС1.Значение КАК Результат
|ИЗ
| Справочник.Номенклатура КАК Спр
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.Первый КАК МойРС1 ПО Спр.Ссылка = МойРС1.Номенклатура
|ГДЕ &УсловиеИспользоватьПервый
|
|ОБЪЕДИНЕНИЕ ВСЕ
|
|ВЫБРАТЬ
| Спр.Ссылка,
| МойРС2.Значение
|ИЗ
| Справочник.Номенклатура КАК Спр
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.Второй КАК МойРС2 ПО Спр.Ссылка = МойРС2.Номенклатура
|ГДЕ НЕ &УсловиеИспользоватьПервый"
Хотя кода становится больше, этот метод считается наиболее надежным с точки зрения стабильности скорости выполнения в высоконагруженных системах.
Выясним, какой метод лучше использовать в зависимости от контекста задачи:
Способ 1 (условие в ПО). Это сохранит читаемость кода и позволит легко вносить правки через конструктор.ОБЪЕДИНЕНИЕ ВСЕ.ВЫБОР внутри соединений. Платформа 1С может некорректно транслировать такие запросы, что приведет к полному сканированию таблиц (Full Scan) вместо использования индексов. В динамических списках лучше всего менять основной текст запроса в событии ПриСозданииНаСервере.СрезПоследних), лучше сначала подготовить данные во временной таблице, выбрав нужный регистр условием в коде 1С, проиндексировать её, и только потом делать соединение с основной выборкой.Подводя итог, можно сказать, что выбор между «умным» условием внутри запроса и динамическим формированием текста — это всегда баланс между удобством разработки и скоростью работы системы. Для большинства повседневных задач метод с параметром в секции ПО является наиболее сбалансированным решением.