При написании сложных запросов в системе 1С:Предприятие программисты часто сталкиваются с необходимостью наложить условие на соответствие пары значений (например, Номенклатура и Характеристика) списку из временной таблицы. Ситуация осложняется, когда логика требует применения условия ВЫБОР (CASE) непосредственно перед оператором В. Попытка обернуть кортеж полей в одну конструкцию ВЫБОР приводит к ошибке: «В запросах операции "в" должно выбираться столько полей, сколько операндов имеет левая часть». Рассмотрим подробнее, почему это происходит и как правильно решить данную задачу.
Разберем природу проблемы. В языке запросов 1С конструкция ВЫБОР ... КОГДА ... ТОГДА ... КОНЕЦ является скалярным выражением. Это означает, что она всегда возвращает одно-единственное значение определенного типа (ссылку, число, строку и так далее). Она не может вернуть структуру, массив или кортеж (набор полей).
Когда мы пишем условие (Поле1, Поле2) В (ВЫБРАТЬ Колонку1, Колонку2...), левая часть условия состоит из двух операндов. Если же мы пытаемся написать ВЫБОР КОГДА ... ТОГДА (Поле1, Поле2) ... КОНЕЦ В (...), система воспринимает весь блок ВЫБОР как один операнд, в то время как в правой части (в подзапросе) ожидается два поля. Из-за этого несоответствия парсер запроса выдает ошибку.
Проанализируем самый прямой способ исправления синтаксиса. Если нам необходимо оставить условие в формате (Операнд1, Операнд2) В (...), то каждый операнд должен быть самостоятельным выражением. Мы можем использовать два отдельных блока ВЫБОР внутри скобок кортежа.
Посмотрим на пример реализации такого подхода:
|ГДЕ
| (ВЫБОР
| КОГДА Номенклатура.ВестиУчетПоХарактеристикам = ЛОЖЬ
| ТОГДА Номенклатура
| ИНАЧЕ Номенклатура
| КОНЕЦ,
| ВЫБОР
| КОГДА Номенклатура.ВестиУчетПоХарактеристикам = ЛОЖЬ
| ТОГДА ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка)
| ИНАЧЕ ХарактеристикаНоменклатуры
| КОНЕЦ)
| В (ВЫБРАТЬ ВТ.Номенклатура, ВТ.Характеристика ИЗ ВремНоменклатураСХарактеристиками КАК ВТ)
В данном случае мы четко соблюдаем правило: в левой части два выражения (два ВЫБОР), и в правой части подзапрос возвращает две колонки. Система успешно сопоставит их между собой.
Рассмотрим более гибкий и часто более читаемый вариант — использование коррелированного подзапроса. Вместо того чтобы пытаться подстроить левую часть под структуру временной таблицы, мы можем перенести всю логику проверки внутрь вложенного запроса.
Выясним, как это работает: мы проверяем условие 1 В (ВЫБРАТЬ 1...), где во вложенном запросе связываем основную таблицу с временной по нужным нам правилам, включая проверку флага учета характеристик.
Пример кода:
|ГДЕ
| 1 В (
| ВЫБРАТЬ ПЕРВЫЕ 1
| 1
| ИЗ
| ВремНоменклатураСХарактеристиками КАК ВТ
| ГДЕ
| ТвояТаблица.Номенклатура = ВТ.Номенклатура
| И (
| (ТвояТаблица.Номенклатура.ВестиУчетПоХарактеристикам И ТвояТаблица.Характеристика = ВТ.Характеристика)
| ИЛИ
| (НЕ ТвояТаблица.Номенклатура.ВестиУчетПоХарактеристикам И ВТ.Характеристика = ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка))
| )
| )
Этот метод хорош тем, что он позволяет описывать сколь угодно сложные условия сопоставления, не ограничиваясь простым равенством полей. Важный нюанс: при использовании такого подхода убедитесь, что поля, по которым идет связь (в данном случае Номенклатура), проиндексированы во временной таблице.
Проанализируем ситуацию с точки зрения производительности. Использование оператора В с подзапросом может работать медленно на больших объемах данных. Часто эффективнее заменить такое условие на ЛЕВОЕ СОЕДИНЕНИЕ с последующей фильтрацией в блоке ГДЕ или использованием функции ЕСТЬNULL.
Рассмотрим по шагам, как перестроить запрос:
ВремНоменклатураСХарактеристиками к основной таблице.ПО прописываем логику соответствия.ГДЕ отсекаем те записи, которые не нашли соответствия во временной таблице.Пример реализации через соединение:
|ВЫБРАТЬ
| ОсновнаяТаблица.Ссылка
|ИЗ
| Справочник.Номенклатура КАК ОсновнаяТаблица
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВремНоменклатураСХарактеристиками КАК ВТ
| ПО ОсновнаяТаблица.Ссылка = ВТ.Номенклатура
| И (ВЫБОР
| КОГДА ОсновнаяТаблица.ВестиУчетПоХарактеристикам
| ТОГДА ОсновнаяТаблица.Характеристика = ВТ.Характеристика
| ИНАЧЕ ВТ.Характеристика = ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка)
| КОНЕЦ)
При работе с подобными конструкциями стоит помнить о нескольких важных моментах:
1. Обращение через точку. В примерах выше используется Номенклатура.ВестиУчетПоХарактеристикам. В языке запросов 1С это приводит к неявному соединению (Left Join) с таблицей справочника номенклатуры. Если ваш запрос обрабатывает тысячи строк, это может создать лишнюю нагрузку. Проанализируем возможность оптимизации: лучше заранее выгрузить флаг ВестиУчетПоХарактеристикам во временную таблицу вместе с данными основной выборки.
2. Пустые ссылки и NULL. При сопоставлении характеристик всегда помните, что "Пустая ссылка" и значение NULL — это разные вещи. Если в вашей временной таблице характеристика может отсутствовать (быть NULL), то прямое сравнение = не сработает. В таких случаях используйте функцию ЕСТЬNULL(ВТ.Характеристика, ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка)).
3. Понятность кода. Несмотря на то что Решение №1 (кортеж из ВЫБОР) является самым коротким, оно может быть трудным для чтения при увеличении количества полей. Решение №2 (через 1 В...) или использование соединений считается более "профессиональным" подходом в среде разработчиков 1С, так как оно легче поддается отладке и оптимизации через индексы.
Подведем итог: конструкция ВЫБОР не может возвращать список полей. Если вам нужно динамически менять значения в условии В для кортежа, используйте либо отдельные ВЫБОР для каждого поля, либо переходите к использованию коррелированных подзапросов и соединений таблиц.