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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на платформе 1С:Предприятие часто возникает задача: передать список значений (например, массив телефонных номеров, артикулов или идентификаторов) в запрос и использовать его не просто в условии ГДЕ Поле В (&Массив), а как полноценный источник данных для ЛЕВОЕ СОЕДИНЕНИЕ. Однако попытка написать в запросе ИЗ &Массив КАК Таблица приводит к ошибке типизации. Разберем подробно, почему так происходит, и изучим все доступные способы решения этой задачи.

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

Проанализируем причину возникновения ошибки «Содержимое объекта данных может быть выбрано только во временную таблицу». Система компоновки данных и механизм запросов 1С работают в тесной связке с SQL-сервером. Для СУБД крайне важно знать тип данных и длину поля еще на этапе компиляции плана запроса. Массив в языке 1С является нетипизированной коллекцией — в нем одновременно могут находиться числа, строки и ссылки. Поскольку платформа не может гарантировать единообразие типов в массиве, она запрещает использовать его как прямой источник строк для формирования таблицы.

Рассмотрим по шагам основные способы, которые позволяют обойти это ограничение и получить виртуальную таблицу (ВТ) из массива.

Способ №1: Использование типизированной Таблицы Значений (Рекомендуемый)

Это самый надежный и производительный метод. Его суть заключается в том, что мы создаем объект ТаблицаЗначений, явно описываем типы его колонок и передаем его в запрос как параметр. Платформа 1С автоматически преобразует такую таблицу во временную таблицу на уровне SQL-сервера.

Проанализируем пример кода для передачи массива телефонных номеров:


// 1. Подготовим массив данных
МассивНомеров = Новый Массив;
МассивНомеров.Добавить("79991112233");
МассивНомеров.Добавить("79995556677");

// 2. Создадим таблицу значений с явной типизацией
ТЗ_Номера = Новый ТаблицаЗначений;
// Важно: всегда указывайте описание типов, чтобы избежать NVARCHAR(MAX) в SQL
КвалификаторыСтроки = Новый КвалификаторыСтроки(20);
ОписаниеТипаСтрока = Новый ОписаниеТипов("Строка", , КвалификаторыСтроки);
ТЗ_Номера.Колонки.Добавить("Номер", ОписаниеТипаСтрока);

// 3. Заполним ТЗ из массива (быстрый способ без циклов)
ТЗ_Номера.ЗагрузитьКолонку(МассивНомеров, "Номер");

// 4. Передадим ТЗ в запрос
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.Номер КАК НомерТелефонный
    |ПОМЕСТИТЬ ВТ_Номера
    |ИЗ
    |	&ВнешняяТЗ КАК ТЗ
    |;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ_Номера.НомерТелефонный,
    |	Контрагенты.Ссылка КАК Контрагент
    |ИЗ
    |	ВТ_Номера КАК ВТ_Номера
    |		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
    |		ПО ВТ_Номера.НомерТелефонный = Контрагенты.Телефон";

Запрос.УстановитьПараметр("ВнешняяТЗ", ТЗ_Номера);
Результат = Запрос.Выполнить();

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

Способ №2: Динамическое формирование текста запроса (UNION ALL)

Если по какой-то причине мы не хотим создавать объект ТаблицаЗначений (например, в очень простых скриптах или при работе в контексте, где создание объектов ограничено), можно использовать "хитрость" с объединением результатов выбора пустых значений. Рассмотрим, как реализовать этот подход программно.

Выясним, как сформировать текст запроса динамически:


// Предположим, у нас есть массив простых чисел или строк
МассивДанных = Новый Массив;
МассивДанных.Добавить(101);
МассивДанных.Добавить(102);

ТекстОбъединения = "";
Для Сч = 0 По МассивДанных.Количество() - 1 Цикл
    ТекстОбъединения = ТекстОбъединения + "ВЫБРАТЬ &Пар" + Сч;
    Если Сч < МассивДанных.Количество() - 1 Тогда
        ТекстОбъединения = ТекстОбъединения + " ОБЪЕДИНИТЬ ВСЕ " + Символы.ПС;
    КонецЕсли;
КонецЦикла;

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ (" + ТекстОбъединения + ") КАК Таб; ВЫБРАТЬ * ИЗ ВТ_Данные";

Для Сч = 0 По МассивДанных.Количество() - 1 Цикл
    Запрос.УстановитьПараметр("Пар" + Сч, МассивДанных[Сч]);
КонецЦикла;

Проанализируем ограничения этого метода:

Способ №3: Выборка из существующей таблицы через "ГДЕ В (&Массив)"

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

Посмотрим на пример:


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

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

Однако помните: если ваша цель — найти, каких элементов НЕТ в базе данных, этот метод не подойдет, так как запрос вернет только пересечение массива и данных таблицы. Для поиска отсутствующих элементов (через ЕСТЬ NULL при левом соединении) обязательно используйте Способ №1.

Подведем итоги

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

Основные правила при работе с этим механизмом:

  1. Всегда создавайте ОписаниеТипов для колонок ТЗ. Это предотвращает создание неоптимальных полей в базе данных.
  2. Используйте метод ЗагрузитьКолонку() для быстрого наполнения таблицы данными.
  3. Если в массиве содержатся примитивные типы (строки, числа), убедитесь, что длина и точность в описании типа соответствуют данным, которые вы планируете сравнивать в запросе.
  4. Помещайте данные из параметра-таблицы в Временную Таблицу (ПОМЕСТИТЬ) в первом же пакете запроса. Это позволит SQL-серверу построить статистику и ускорить последующие соединения.

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

← На главную