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