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

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

Как передать массив в запрос 1С как временную таблицу

Как передать массив в запрос 1С как временную таблицу: помещение массива в МенеджерВременныхТаблиц, соединение с данными ИБ, типизация колонок и типовые ошибки.

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

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

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

Когда массив лучше передать как временную таблицу, а не через В (&Массив)

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

Временная таблица выигрывает в таких сценариях: соединение по нескольким полям одновременно; повторное использование одного набора данных в цепочке запросов; необходимость добавить к элементам массива вычисленные в коде поля (признак, сумму, комментарий) и потом прочитать их в результате; работа с виртуальными таблицами, где параметр-таблица требуется по синтаксису (например, отбор в остатках регистра).

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

Сравнение вариантов

Условие В (&Массив)

Подходит для простого отбора по одному полю. Ограничение: нельзя соединять по нескольким колонкам и переиспользовать данные в пакете запросов.

Временная таблица из таблицы значений

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

Параметр-таблица в виртуальной таблице регистра

Единственный штатный способ отбора в некоторых виртуальных таблицах (например, Остатки с отбором по списку номенклатуры). Таблица значений передаётся прямо в параметр виртуальной таблицы.

Подготовка: преобразуем массив в таблицу значений

Запрос не принимает массив как источник временной таблицы — источником может быть только таблица значений. Поэтому первый шаг: создать таблицу значений, описать колонки с типами и заполнить её элементами массива.

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

ТаблицаДанных = Новый ТаблицаЗначений;

ТаблицаДанных.Колонки.Добавить("Номенклатура",

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

ТаблицаДанных.Колонки.Добавить("Количество",

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

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

Строка = ТаблицаДанных.Добавить();

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

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

КонецЦикла;

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

Помещаем таблицу значений во временную таблицу запроса

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

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

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

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

"ВЫБРАТЬ

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

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

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

|ИЗ

| &ТаблицаДанных КАК ВТ

|;

|///////////////////

|ВЫБРАТЬ

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

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

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

|ИЗ

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

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

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

Запрос.УстановитьПараметр("ТаблицаДанных", ТаблицаДанных);

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

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

Порядок действий
  1. 1
    Соберите массив данных в коде и определите, какие поля нужны для соединения.
  2. 2
    Создайте таблицу значений и опишите колонки с точными типами через ОписаниеТипов.
  3. 3
    Заполните таблицу значений элементами массива, при необходимости сверните дубли.
  4. 4
    Создайте запрос, назначьте ему новый МенеджерВременныхТаблиц.
  5. 5
    Первым запросом пакета поместите параметр-таблицу во временную таблицу через ПОМЕСТИТЬ.
  6. 6
    Во втором запросе пакета соедините временную таблицу с таблицами базы.
  7. 7
    Установите параметр, выполните запрос и проверьте количество строк результата.

Соединение временной таблицы с данными информационной базы

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

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

Временная таблица живёт внутри своего менеджера временных таблиц. Если создать второй объект Запрос и назначить ему тот же менеджер (Запрос2.МенеджерВременныхТаблиц = Запрос1.МенеджерВременныхТаблиц), он увидит уже построенную ВТ_Данные. Это удобно, когда логика разбита на несколько процедур: одна готовит данные, другая читает.

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

Типизация колонок и производительность

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

Для больших списков (тысячи и десятки тысяч строк) имеет смысл: сворачивать таблицу перед передачей; передавать только те колонки, которые реально используются в запросе; при соединении с физическими таблицами проверять, что поле соединения индексировано (ссылки и измерения регистров индексированы по умолчанию).

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

Контрольные точки

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

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

Совпадает с числом уникальных позиций переданного списка (при левом соединении от временной таблицы)

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

Строк меньше — часть позиций потерялась, вероятно несовпадение типов или внутреннее соединение

Значения соединённых полей
Норма

Поля из базы заполнены там, где данные есть, и NULL там, где их нет

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

Все поля соединения NULL — соединение не сработало, проверяйте типы колонок

Выполнение пакета
Норма

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

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

Ошибка «Таблица не найдена» — не назначен или потерян МенеджерВременныхТаблиц

Типичные ошибки и ограничения

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

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

Типичные ошибки
01
Не назначен МенеджерВременныхТаблиц — запрос с ПОМЕСТИТЬ падает с ошибкой.
02
Колонки без ОписаниеТипов — соединение возвращает пустой результат из-за несовпадения типов.
03
Передача массива вместо таблицы значений как источника ПОМЕСТИТЬ — ошибка исполнения запроса.
04
Дубли строк в таблице значений — раздувание результата при соединении, лечится Свернуть().
05
Ожидание, что временная таблица видна в другом запросе без общего менеджера — ошибка «таблица не найдена».

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

Альтернатива: параметр-таблица в виртуальных таблицах регистров

Отдельный частный случай — когда массив нужен не для соединения, а для отбора в виртуальной таблице регистра. Например, получить остатки только по списку номенклатуры. Здесь таблицу значений передают прямо в параметр виртуальной таблицы: РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (ВЫБРАТЬ Т.Номенклатура ИЗ &Таблица КАК Т)) либо через условие В с вложенным запросом к параметру-таблице.

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

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

Как выбрать действие

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

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

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

Нет. Источником для ПОМЕСТИТЬ из параметра может быть только таблица значений. Массив сначала выгружают в таблицу значений: создают колонки с типами и заполняют строки в цикле. Это обязательный промежуточный шаг.

Почему соединение с временной таблицей возвращает пустой результат?

Чаще всего не совпадают типы: колонка таблицы значений не типизирована или имеет составной тип, а поле в базе — конкретный ссылочный тип. Задайте колонке точное ОписаниеТипов, совпадающее с типом поля соединения, и проверьте, что значения в таблице не пустые.

Как использовать временную таблицу в другом запросе?

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

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

На небольших списках разница практически незаметна. На больших объёмах и при соединении по нескольким полям временная таблица обычно предпочтительнее, но точный ответ даёт только план запроса на ваших данных — сравните оба варианта в консоли запросов.

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

Не обязательно: временные таблицы удаляются автоматически при уничтожении менеджера временных таблиц. Но в длинных пакетах, где таблица больше не нужна, явное УНИЧТОЖИТЬ освобождает ресурсы сервера раньше.

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

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

Итог

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

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