При написании сложных запросов в 1С разработчики часто сталкиваются с необходимостью реализовать динамические связи между таблицами. Одна из распространенных ситуаций — необходимость наложить условие соединения только в том случае, если определенное поле или параметр заполнены. Если же значение пустое, условие должно игнорироваться, пропуская все записи.
Давайте подробно разберем, как правильно использовать конструкцию ВЫБОР (CASE) внутри секции связей таблиц, рассмотрим "магический" вариант с ИСТИНА, проанализируем альтернативы с логическим ИЛИ и, самое главное, обсудим влияние этих методов на производительность системы.
Представим ситуацию, описанную автором вопроса. У нас есть две таблицы, которые нужно соединить. Однако одно из условий соединения должно срабатывать не всегда:
втМаршрутный) указан конкретный "ФильтрТорговыйПредставитель", то мы должны соединиться со второй таблицей строго по этому представителю.Попытка использовать стандартную конструкцию ВЫБОР часто ставит в тупик именно на моменте описания ветки ИНАЧЕ.
| И (ВЫБОР
| КОГДА НЕ втМаршрутный.Фильтр = ЗНАЧЕНИЕ(Справочник.Сотрудники.ПустаяСсылка)
| ТОГДА втМаршрутный.Фильтр = Таблица2.Ответственный
| ИНАЧЕ ??? // Что написать здесь, чтобы условие исчезло?
| КОНЕЦ)
Давайте рассмотрим рабочие варианты решения этой головоломки.
Самый лаконичный и изящный способ, предложенный в обсуждении, заключается в использовании литерала ИСТИНА в ветке ИНАЧЕ.
Рассмотрим, как это выглядит в коде:
| И (ВЫБОР
| КОГДА втМаршрутный.Фильтр <> ЗНАЧЕНИЕ(Справочник.Сотрудники.ПустаяСсылка)
| ТОГДА втМаршрутный.Фильтр = РасходнаяНакладная.Контрагент.Ответственный
| ИНАЧЕ ИСТИНА
| КОНЕЦ)
Почему это работает?
Здесь вступают в силу законы булевой алгебры. Когда СУБД выполняет запрос, она оценивает условия соединения. Если условие в КОГДА не выполняется (то есть фильтр пустой), управление переходит в ветку ИНАЧЕ.
В этом случае выражение превращается в:
... И ИСТИНА
Согласно логике, выражение Условие И ИСТИНА всегда равно самому Условие. То есть добавление "И ИСТИНА" никак не меняет результат выборки, оно фактически "растворяется", позволяя запросу работать так, будто этого дополнительного фильтра нет.
Второй способ — отказаться от конструкции ВЫБОР в пользу классических логических операторов И / ИЛИ. Это делает запрос более читаемым с точки зрения стандарта SQL и часто понятнее для последующей поддержки.
Логика следующая: запись нам подходит, если (Фильтр пустой) ИЛИ (Фильтр совпадает с полем таблицы).
| И (втМаршрутный.Фильтр = ЗНАЧЕНИЕ(Справочник.Сотрудники.ПустаяСсылка)
| ИЛИ втМаршрутный.Фильтр = РасходнаяНакладная.Контрагент.Ответственный)
Давайте проанализируем этот подход:
Фильтр = ПустаяСсылка) возвращает ИСТИНА. Поскольку используется оператор ИЛИ, всё выражение становится истинным, и вторая часть (сравнение ответственных) уже не важна. Связь устанавливается.Хотя оба приведенных выше решения синтаксически верны и возвращают правильный результат, мы обязаны рассмотреть вопрос производительности, который был затронут опытными участниками обсуждения.
Использование сложных условий (ВЫБОР или ИЛИ) внутри секции соединения (JOIN ON) может негативно сказаться на скорости выполнения запроса, особенно на больших объемах данных.
Почему это происходит?
ПолеА = ПолеБ). Когда мы добавляем ИЛИ или ВЫБОР, условие становится "размытым". Оптимизатор SQL-сервера часто не может заранее предсказать, какие строки нужны, и вынужден перебирать таблицу целиком (Scan), проверяя условие для каждой строки.CASE. Это может привести к выбору неоптимального алгоритма соединения (например, вложенные циклы вместо хэш-соединения).Если мы хотим добиться максимальной производительности и избежать проблем с индексами, наилучшим решением будет формирование текста запроса на уровне встроенного языка 1С до его отправки в СУБД.
Вместо того чтобы заставлять базу данных проверять "пустая ссылка или нет" для каждого миллиона записей, мы проверим это один раз в коде 1С.
Рассмотрим алгоритм действий:
ЗначениеЗаполнено().Пример реализации на встроенном языке:
ТекстЗапроса =
"ВЫБРАТЬ
| ...
|ИЗ
| ВТ_Маршрутный КАК втМаршрутный
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.РасходнаяНакладная КАК РасходнаяНакладная
| ПО втМаршрутный.ОсновноеПоле = РасходнаяНакладная.ОсновноеПоле";
// Динамически добавляем условие, только если оно реально нужно
Если ЗначениеЗаполнено(ТорговыйПредставительОтбор) Тогда
ТекстЗапроса = ТекстЗапроса +
" И втМаршрутный.ФильтрТорговыйПредставитель = РасходнаяНакладная.Контрагент.Ответственный";
КонецЕсли;
Запрос.Текст = ТекстЗапроса;
Преимущества архитектурного подхода:
=), которое идеально использует индексы.Давайте подведем итоги выбора метода решения задачи:
ИНАЧЕ ИСТИНА — Отличный вариант для быстрых, разовых запросов или небольших таблиц. Легко пишется, решает проблему синтаксиса.ИЛИ — Более стандартизированный вариант, но имеет те же риски производительности, что и первый метод.Используйте метод с ВЫБОР ... ИНАЧЕ ИСТИНА осторожно, понимая, как он работает, но стремитесь к упрощению текста запроса, генерируя его программно там, где это возможно.