Передаём коллекцию значений в запрос 1С: условие В, таблицы значений и СКД

· 9 мин

Содержание

Массив передают в запрос 1С как обычный параметр: Запрос.УстановитьПараметр("Список", МассивСсылок), а в тексте запроса используют условие ГДЕ Поле В (&Список). Платформа 8.3 сама разворачивает коллекцию в перечень значений, цикл и склейка строк не нужны.

Тот же приём работает со СписокЗначений и ФиксированныйМассив. Массив структур или объектов так передать нельзя: его превращают в таблицу значений или в массив ссылок. Ниже синтаксис, ограничения, рабочие примеры и то, как поступать со списками на тысячи элементов.

Как массив попадает в запрос: что принимает параметр

Параметр запроса принимает значение любого типа, который можно хранить в базе: ссылку, число, строку, дату, булево. Если в параметр положить коллекцию, платформа считает её набором таких значений и подставляет в условие целиком.

Какие коллекции подходят:

  • ✅ Массив — основной вариант, элементы могут быть разных типов;
  • ✅ ФиксированныйМассив — то же самое, удобно, когда массив пришёл с клиента через реквизит формы;
  • ✅ СписокЗначений — в запрос уходят только значения, представления и пометки игнорируются;
  • ✅ ТаблицаЗначений — но не в условие В, а как источник данных ИЗ &Таблица (о нём ниже);
  • ❌ массив структур, соответствие, массив ДокументОбъект или СправочникОбъект — запрос их не понимает.

Запрос выполняется только на сервере. Если массив собран на клиенте (например, выделенные строки списка), передайте его в серверную процедуру формы с директивой &НаСервере или в общий модуль. Массив ссылок сериализуется без проблем.

Дубликаты в массиве не мешают: условие В проверяет вхождение, а не количество совпадений. Чистить массив от повторов перед передачей не обязательно.

Синтаксис условия В (&Параметр) с массивом

Минимальный рабочий пример — выбрать контрагентов по списку ИНН:

МассивИНН = Новый Массив;
МассивИНН.Добавить("7701234567");
МассивИНН.Добавить("7809876543");

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Контрагенты.Ссылка КАК Контрагент,
|	Контрагенты.ИНН КАК ИНН
|ИЗ
|	Справочник.Контрагенты КАК Контрагенты
|ГДЕ
|	Контрагенты.ИНН В (&СписокИНН)";

Запрос.УстановитьПараметр("СписокИНН", МассивИНН);
Выборка = Запрос.Выполнить().Выбрать();

Что здесь обязательно:

  1. Скобки вокруг параметра: В (&СписокИНН). Без них конструктор и платформа выдадут синтаксическую ошибку.
  2. Имя параметра в тексте и в УстановитьПараметр совпадает посимвольно, регистр не важен.
  3. Тип элементов массива совпадает с типом поля. Строка «7701234567» не найдёт ссылку на контрагента, а число 7701234567 не найдёт строковый ИНН.

Отрицание и иерархия

Для обратного отбора пишут ГДЕ НЕ Поле В (&Список). Для справочников с группами есть В ИЕРАРХИИ:

"ГДЕ Товары.Номенклатура В ИЕРАРХИИ (&ГруппыНоменклатуры)"

Такое условие отберёт и сами элементы массива, и всё, что лежит внутри переданных групп на любом уровне вложенности.

Пустой массив

Если массив пуст, условие В (&Список) ложно для всех строк, и запрос вернёт пустой результат. Ошибки не будет. Частая логика «пустой список — значит, без отбора» делается явно:

"ГДЕ (&БезОтбора ИЛИ Товары.Номенклатура В (&Список))"
...
Запрос.УстановитьПараметр("БезОтбора", Список.Количество() = 0);
Запрос.УстановитьПараметр("Список", Список);

Ограничения и типичные ошибки при передаче массива

Большинство проблем с массивами в запросах сводятся к типам и к тому, что именно положили в параметр. Сводка по симптомам:

Диагностика: выберите симптом

Ещё несколько ограничений, о которых лучше знать заранее:

  • 🔸 Только одно поле слева. Конструкция (Поле1, Поле2) В (...) работает с вложенным запросом, но не с массивом. Для пар значений нужна таблица значений.
  • 🔸 Массив массивов не разворачивается. Вложенный массив параметр не примет, предварительно соберите плоский список.
  • 🔸 Составной тип поля. Если поле может хранить ссылки разных типов, в массиве тоже могут быть разные типы — это корректно, отбор сработает по каждому.
  • 🔸 Ссылки из другой базы. Ссылка, полученная по ПолучитьСсылку() с чужим УИД, формально валидна, но строк в выборке не даст.

Массив структур и массив объектов: как подготовить данные

Типовая ситуация: из внешнего файла или веб-сервиса пришёл массив структур вида Новый Структура("Номенклатура, Количество", ...). В условие В его не подставить. Есть два пути.

Путь 1: нужны только ссылки

Выберите из структур одно поле в отдельный массив:

Ссылки = Новый Массив;
Для Каждого Элемент Из МассивСтруктур Цикл
	Ссылки.Добавить(Элемент.Номенклатура);
КонецЦикла;
Запрос.УстановитьПараметр("Номенклатура", Ссылки);

Для массива объектов (ДокументОбъект, СправочникОбъект) то же самое делают со свойством Ссылка. Если исходные данные уже лежат в таблице значений, нужный массив возвращает Таблица.ВыгрузитьКолонку("Номенклатура").

Путь 2: нужны все поля — таблица значений во временной таблице

Когда в запросе требуются и ссылка, и количество, и цена, массив структур переносят в ТаблицаЗначений. Колонкам обязательно задайте типы, иначе платформа не сможет поместить таблицу во временную.

Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура",
	Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Таблица.Колонки.Добавить("Количество",
	Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));

Для Каждого Элемент Из МассивСтруктур Цикл
	ЗаполнитьЗначенияСвойств(Таблица.Добавить(), Элемент);
КонецЦикла;

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Данные.Номенклатура КАК Номенклатура,
|	Данные.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
|	&Таблица КАК Данные
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ_Данные.Номенклатура,
|	ВТ_Данные.Количество,
|	ВТ_Данные.Номенклатура.Артикул КАК Артикул
|ИЗ
|	ВТ_Данные КАК ВТ_Данные";
Запрос.УстановитьПараметр("Таблица", Таблица);

Таблица значений в ИЗ &Таблица допустима только в запросе, который помещает результат во временную таблицу. Сразу соединять её с регистрами в одном запросе нельзя — сначала ПОМЕСТИТЬ, потом работа с временной таблицей.

Имена колонок в таблице должны совпадать с ключами структуры, иначе ЗаполнитьЗначенияСвойств молча пропустит поле, и в запрос уйдут пустые значения.

Оптимизация запросов с массивом в параметре

Для списка из десятков или сотен ссылок В (&Массив) работает быстро, особенно если поле индексировано: ссылки, реквизиты с флажком индексирования, измерения регистров.

Когда элементов становятся тысячи, SQL-текст разрастается, а СУБД хуже подбирает план. В таких случаях выручает временная таблица с индексом:

"ВЫБРАТЬ
|	Список.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТ_Отбор
|ИЗ
|	&ТаблицаОтбора КАК Список
|
|ИНДЕКСИРОВАТЬ ПО
|	Номенклатура
|;
|ВЫБРАТЬ
|	Остатки.Номенклатура,
|	Остатки.КоличествоОстаток
|ИЗ
|	РегистрНакопления.ТоварыНаСкладах.Остатки(
|		,
|		Номенклатура В (ВЫБРАТЬ ВТ_Отбор.Номенклатура ИЗ ВТ_Отбор КАК ВТ_Отбор)
|	) КАК Остатки"

Таблицу отбора с одной колонкой проще всего получить так: создать ТаблицаЗначений с типизированной колонкой и заполнить её ЗагрузитьКолонку(Массив, "Номенклатура").

Что выбрать в зависимости от ситуации:

СпособКогда подходитМинусы
В (&Массив)До сотен значений, простой отборНа больших списках растёт текст SQL
В (&Массив) в параметрах виртуальной таблицыОстатки и обороты регистров по спискуТе же, что выше
Временная таблица + ИНДЕКСИРОВАТЬ ПОТысячи значений, соединения с несколькими таблицамиБольше кода, нужен типизированный источник
Вложенный запрос вместо массиваСписок можно получить запросом из базыНе подходит для данных извне

Ещё два практических приёма:

  • 💡 Если массив сам получен запросом, не выгружайте его в память — оставьте результат во временной таблице через МенеджерВременныхТаблиц и сошлитесь на неё в следующем запросе.
  • 💡 Отбор по массиву для виртуальных таблиц Остатки и Обороты ставьте в параметры таблицы, а не в ГДЕ. Так СУБД режет данные до агрегации.

Пример: отбор реализаций по массиву номенклатуры в БП 3.0

Задача: найти проведённые документы «Реализация (акты, накладные, УПД)» за период, в которых есть хотя бы одна позиция из переданного списка товаров. Табличная часть документа в БП 3.0 называется Товары.

&НаСервере
Функция ДокументыПоНоменклатуре(МассивНоменклатуры, НачалоПериода, КонецПериода)

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	Товары.Ссылка КАК Документ,
	|	Товары.Ссылка.Дата КАК Дата,
	|	Товары.Ссылка.Контрагент КАК Контрагент
	|ИЗ
	|	Документ.РеализацияТоваровУслуг.Товары КАК Товары
	|ГДЕ
	|	Товары.Номенклатура В (&Номенклатура)
	|	И Товары.Ссылка.Дата МЕЖДУ &Начало И &Конец
	|	И Товары.Ссылка.Проведен
	|
	|УПОРЯДОЧИТЬ ПО
	|	Дата";

	Запрос.УстановитьПараметр("Номенклатура", МассивНоменклатуры);
	Запрос.УстановитьПараметр("Начало", НачалоДня(НачалоПериода));
	Запрос.УстановитьПараметр("Конец", КонецДня(КонецПериода));

	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Документ");

КонецФункции

Разбор решений в этом примере:

  1. ВЫБРАТЬ РАЗЛИЧНЫЕ нужен, потому что документ с тремя подходящими строками иначе попадёт в выборку трижды.
  2. НачалоДня и КонецДня защищают от потери документов последнего дня: дата без времени означает полночь.
  3. Функция возвращает массив ссылок, который можно сразу передать в следующий запрос тем же способом.

Если в списке есть группы номенклатуры, замените В (&Номенклатура) на В ИЕРАРХИИ (&Номенклатура), и в выборку попадут товары из этих групп.

Массив в параметрах отчёта на СКД

В системе компоновки данных параметр-список настраивается в самой схеме. Откройте схему в Конфигураторе, перейдите на вкладку «Параметры» и у нужного параметра установите флажок «Доступен список значений». В тексте запроса набора данных используется та же конструкция В (&Номенклатура).

Из кода значение задают через параметры данных компоновщика. Надёжнее передавать СписокЗначений:

Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(МассивНоменклатуры);

Параметр = Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных
	.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Номенклатура"));
Если Параметр <> Неопределено Тогда
	Параметр.Значение = Список;
	Параметр.Использование = Истина;
КонецЕсли;

Альтернатива параметру — отбор компоновки с видом сравнения «В списке». Пользователь видит его в настройках отчёта и может поправить сам. Отличие в том, где срабатывает условие:

ПодходГде работает условиеКогда выбирать
Параметр со списком значенийПрямо в тексте запроса набораСписок обязателен для логики отчёта, нужен в параметрах виртуальных таблиц
Отбор «В списке»Компоновка добавляет условие самаОтбор пользовательский, его можно снять или изменить

Параметр-список без значения работает как пустой массив: отчёт ничего не покажет. Если отбор по нему необязателен, заключите условие в тексте запроса набора в фигурные скобки {} — тогда при снятом флажке использования параметра СКД просто уберёт этот фрагмент из запроса.

Частые вопросы

Можно ли передать в запрос массив чисел или строк, а не ссылок?
Да. Условие В работает с любыми примитивными типами. Важно, чтобы тип элементов совпадал с типом поля, а строки поля имели ограниченную длину.
Чем СписокЗначений лучше массива в параметре запроса?
Для обычного запроса разницы нет: используются только значения. СписокЗначений удобен, когда тот же список показывают пользователю или передают в параметр СКД.
Сколько элементов можно передать в массиве?
Жёсткого числа в языке нет, но на тысячах элементов запрос заметно тяжелеет. Начиная с нескольких сотен значений лучше переходить на временную таблицу с индексом.
Как передать в запрос пары значений, например номенклатуру и склад?
Через таблицу значений с двумя типизированными колонками, помещённую во временную таблицу. Затем соедините её с регистром по обоим полям или используйте (Поле1, Поле2) В (ВЫБРАТЬ … ИЗ ВТ).
Почему запрос с массивом находит не всё, что ожидалось?
Обычно в массиве лежат ссылки на группы, а в условии стоит В вместо В ИЕРАРХИИ: тогда находятся только записи с самой группой, без её содержимого. Второй частый случай — массив собрали из справочника другого вида, например из «Номенклатурных групп» вместо «Номенклатуры».
Можно ли использовать один массив в нескольких местах запроса?
Да. Один параметр можно указать в тексте сколько угодно раз, в том числе в разных запросах пакета. Устанавливать его повторно не нужно.