К статье
Практический материал

Разработка и Конфигурация · 12 мин

Массив в запросе 1С как таблица: передача и использование

Как передать массив значений в запрос 1С как таблицу: преобразование в таблицу значений, параметр &ТЗ, объединение через ОБЪЕДИНИТЬ ВСЕ, ошибки и производительность.

Задача «передать массив в запрос как таблицу» возникает у разработчика 1С всякий раз, когда нужно отфильтровать данные по списку значений, сопоставить строки документа с расчётными данными или соединить результаты обработки в коде с данными информационной базы. Сам язык запросов не умеет работать с массивом как с набором строк напрямую, поэтому существует несколько рабочих приёмов.

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

Ниже разобраны оба способа с готовыми фрагментами кода, типичные ошибки при передаче параметров, влияние на производительность и способы проверки результата до вывода данных пользователю.

Когда массив нужен именно как таблица, а не как список значений

Условие ГДЕ Ссылка В (&Массив) решает только задачу фильтрации: платформа подставляет элементы массива в оператор сравнения. Это удобно, когда нужно выбрать документы по списку ссылок или номенклатуру по списку кодов. Но такой приём не позволяет передать в запрос связанные данные — например, пары «номенклатура + количество» или «контрагент + сумма».

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

Маршрут решения

Как выбрать подход

Нужно только отфильтровать записи по списку однотипных значенийИспользуйте условие В (&Массив) — это проще и быстрее
К каждому элементу массива привязаны дополнительные данные (количество, цена, комментарий)Преобразуйте массив в таблицу значений и передайте параметром &ТЗ
Массив получен из другого запроса через Выгрузить()У вас уже есть таблица значений — передавайте её напрямую, без промежуточного массива

Базовый приём: условие В (&Массив)

Самый простой сценарий — фильтрация по списку ссылок. Массив передаётся как обычный параметр запроса, платформа сама раскрывает его в набор значений:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| РеализацияТоваровУслуг.Ссылка,

| РеализацияТоваровУслуг.СуммаДокумента

|ИЗ

| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

|ГДЕ

| РеализацияТоваровУслуг.Ссылка В (&МассивДокументов)";

Запрос.УстановитьПараметр("МассивДокументов", МассивДокументов);

Результат = Запрос.Выполнить().Выгрузить();

Элементы массива должны быть одного типа и соответствовать типу поля, с которым идёт сравнение. Если в массиве встретится значение другого типа (например, строка вместо ссылки), запрос либо вернёт неполный результат, либо выдаст ошибку несоответствия типов — поведение зависит от СУБД. Перед передачей массив стоит очистить от пустых значений и дублей: метод ВГраница() и цикл проверки типа помогают отловить проблему до выполнения запроса.

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

Передача массива как таблицы значений через параметр &ТЗ

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

Порядок действий
  1. 1
    Создайте объект ТаблицаЗначений и опишите колонки через ОписаниеТипов
  2. 2
    Заполните таблицу строками из массива в цикле
  3. 3
    Напишите текст запроса: ВЫБРАТЬ поля ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ
  4. 4
    Установите параметр ТЗ и выполните запрос с менеджером временных таблиц
  5. 5
    Используйте временную таблицу в последующих запросах пакета
ТЗ = Новый ТаблицаЗначений;

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

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

Для Каждого Элемент Из МассивСтрок Цикл

НоваяСтрока = ТЗ.Добавить();

НоваяСтрока.Номенклатура = Элемент.Номенклатура;

НоваяСтрока.Количество = Элемент.Количество;

КонецЦикла;

Запрос = Новый Запрос;

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

Запрос.Текст =

"ВЫБРАТЬ

| ТЗ.Номенклатура,

| ТЗ.Количество

|ПОМЕСТИТЬ ВТ_Данные

|ИЗ

| &ТЗ КАК ТЗ

|;

|ВЫБРАТЬ

| ВТ.Номенклатура,

| ВТ.Количество,

| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток

|ИЗ

| ВТ_Данные КАК ВТ

| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки

| ПО ВТ.Номенклатура = Остатки.Номенклатура";

Запрос.УстановитьПараметр("ТЗ", ТЗ);

Результат = Запрос.Выполнить().Выгрузить();

Ключевые моменты этого кода: типы колонок заданы явно через ОписаниеТипов, временная таблица создана в первом пакете запроса и использована во втором, соединение выполнено по ссылочному полю. Без явного описания типов платформа попытается вывести тип из данных, что при пустой таблице или смешанных значениях приводит к ошибке.

Альтернатива без таблицы значений: ОБЪЕДИНИТЬ ВСЕ по литералам

Если массив небольшой (единицы или десятки элементов) и состоит из простых значений, таблицу можно собрать прямо в тексте запроса через цепочку ОБЪЕДИНИТЬ ВСЕ. Каждый элемент массива становится отдельным подзапросом с литеральным значением.

Технически это работает, но у приёма жёсткие ограничения. Текст запроса строится конкатенацией строк, что усложняет поддержку и создаёт риск подстановки небезопасных значений. При большом числе элементов запрос раздувается, а СУБД тратит время на разбор длинного текста. Поэтому приём допустим только для маленьких фиксированных списков, сформированных внутри кода, а не из пользовательского ввода.

Типизация колонок и составные типы

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

МассивТипов = Новый Массив;

МассивТипов.Добавить(Тип("ДокументСсылка.РеализацияТоваровУслуг"));

МассивТипов.Добавить(Тип("ДокументСсылка.ПоступлениеТоваровУслуг"));

ТЗ.Колонки.Добавить("Документ", Новый ОписаниеТипов(МассивТипов));

Для строковых колонок обязательно задавайте длину через КвалификаторыСтроки. Строка неограниченной длины в параметре запроса может привести к проблемам при соединениях и группировках на некоторых СУБД. Для чисел указывайте разрядность с запасом, чтобы не потерять дробную часть при расчётах количества или сумм.

Если колонка содержит значения NULL или незаполненные ссылки, учитывайте это в соединениях: NULL не равен NULL, поэтому строки с пустыми значениями выпадут из внутреннего соединения. Используйте ЕСТЬNULL и левые соединения там, где потеря строк недопустима.

Производительность: индексы, объёмы и пакетные запросы

Временная таблица, созданная из параметра, физически размещается на сервере СУБД. При передаче больших таблиц (десятки тысяч строк и больше) сама передача данных занимает заметное время, а соединение с такой таблицей может выполняться полным перебором, если оптимизатор не построит подходящий план.

Практические рекомендации по производительности:

  • удаляйте дубли из таблицы перед передачей — лишние строки увеличивают и передачу, и соединение;
  • передавайте только те колонки, которые реально используются в запросе;
  • при больших объёмах добавьте в первый подзапрос ИНДЕКСИРОВАТЬ ПО для полей соединения — это подсказка платформе создать индекс временной таблицы;
  • объединяйте создание временной таблицы и основной запрос в один пакет, чтобы не выполнять лишние обращения к серверу.
Контрольные точки

Как проверить результат

Количество строк в выгрузке запроса
Норма

Совпадает с ожидаемым числом строк исходной таблицы после соединения

Тревожный признак

Строк меньше — вероятно, потеря из-за внутреннего соединения или NULL

Типы колонок результата
Норма

Ссылки и числа имеют ожидаемые типы, группировки работают

Тревожный признак

Типы смешанные или строковые — ошибка в ОписаниеТипов

Время выполнения
Норма

Запрос выполняется за приемлемое для операции время

Тревожный признак

Резкий рост времени при увеличении таблицы — нет индекса или лишние колонки

Типичные ошибки и их признаки

Типичные ошибки
01
Передача таблицы без описания типов колонок — ошибка «неверный тип параметра» или пустой результат
02
Использование временной таблицы во втором запросе без общего МенеджераВременныхТаблиц — ошибка «таблица не найдена»
03
Соединение по строковому полю без задания длины строки — несовпадение значений из-за разных квалификаторов
04
Передача массива с элементами разных типов в условие В — часть значений молча не срабатывает
05
Конкатенация пользовательских строк в текст запроса — риск синтаксических ошибок и уязвимостей

Отдельно стоит упомянуть ошибку с пустой таблицей. Если массив пуст, таблица значений не будет содержать ни одной строки, и запрос вернёт пустой результат — это нормально. Но если логика обработки предполагает, что данных нет, лучше проверить ТЗ.Количество() = 0 до выполнения запроса и пропустить его: это сэкономит обращение к СУБД и упростит диагностику.

Ещё одна тонкость — имена параметров. Параметр &ТЗ в тексте запроса и имя, переданное в УстановитьПараметр, должны совпадать с точностью до символа. Опечатка в имени приводит к ошибке «переменная не определена» на этапе выполнения, а не компиляции модуля, поэтому такие ошибки всплывают только в момент работы пользователя.

Проверка результата и отладка

Чтобы убедиться, что массив корректно превратился в таблицу внутри запроса, выполните отладочный запрос, который просто выбирает содержимое временной таблицы:

Запрос.Текст = "ВЫБРАТЬ * ИЗ ВТ_Данные КАК ВТ";

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить(Строка(Выборка.Номенклатура) + " — " + Выборка.Количество);

КонецЦикла;

Если содержимое временной таблицы совпадает с исходным массивом — проблема в основном запросе (соединениях, условиях). Если данные искажены уже на этом этапе — ищите ошибку в описании типов и заполнении таблицы значений. Такое разделение этапов отладки экономит время: не нужно гадать, на каком шаге теряются данные.

В консоли запросов (типовой или внешней обработкой) параметр-таблицу можно заполнить вручную и пошагово проверить текст запроса без изменения кода конфигурации. Это удобный способ отладить сложные соединения до переноса текста в модуль.

Итог

Что важно запомнить

Массив передаётся в запрос двумя способами: как список в условии В (&Массив) для фильтрации и как таблица значений через параметр &ТЗ с ПОМЕСТИТЬ для соединений. Всегда описывайте типы колонок явно, используйте менеджер временных таблиц и проверяйте результат отладочной выборкой.

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

Можно ли передать массив структур в запрос напрямую?

Нет, параметром запроса можно передать только таблицу значений, массив простых значений, список значений и примитивные типы. Массив структур сначала нужно развернуть в таблицу значений в цикле: структура не является допустимым типом параметра.

Что быстрее: условие В (&Массив) или временная таблица?

Для фильтрации по одному полю с небольшим списком условие В обычно эффективнее — не тратятся ресурсы на создание временной таблицы. При больших списках и необходимости соединения по нескольким полям выигрывает временная таблица с индексацией.

Почему запрос не видит временную таблицу во втором вызове Выполнить()?

Потому что не задан общий МенеджерВременныхТаблиц. Создайте его один раз, присвойте свойству запроса и используйте тот же объект для всех запросов, которым нужна эта таблица.

Как передать в запрос результат другого запроса?

Выполните первый запрос, получите таблицу через Выгрузить() и передайте её параметром во второй запрос. Промежуточный массив не нужен — результат выгрузки уже является таблицей значений с готовыми типами колонок.

Есть ли ограничение на размер передаваемой таблицы?

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

Можно ли изменить данные в базе через таблицу, переданную параметром?

Нет, параметр-таблица доступен только для чтения внутри запроса. Запросы в 1С вообще не изменяют данные — для записи используются объекты документов, наборы записей регистров и другие механизмы платформы.