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