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