При разработке на платформе 1С:Предприятие часто возникает задача: передать список значений (например, массив телефонных номеров, артикулов или идентификаторов) в запрос и использовать его не просто в условии ГДЕ Поле В (&Массив), а как полноценный источник данных для ЛЕВОЕ СОЕДИНЕНИЕ. Однако попытка написать в запросе ИЗ &Массив КАК Таблица приводит к ошибке типизации. Разберем подробно, почему так происходит, и изучим все доступные способы решения этой задачи.
Проанализируем причину возникновения ошибки «Содержимое объекта данных может быть выбрано только во временную таблицу». Система компоновки данных и механизм запросов 1С работают в тесной связке с SQL-сервером. Для СУБД крайне важно знать тип данных и длину поля еще на этапе компиляции плана запроса. Массив в языке 1С является нетипизированной коллекцией — в нем одновременно могут находиться числа, строки и ссылки. Поскольку платформа не может гарантировать единообразие типов в массиве, она запрещает использовать его как прямой источник строк для формирования таблицы.
Рассмотрим по шагам основные способы, которые позволяют обойти это ограничение и получить виртуальную таблицу (ВТ) из массива.
Это самый надежный и производительный метод. Его суть заключается в том, что мы создаем объект ТаблицаЗначений, явно описываем типы его колонок и передаем его в запрос как параметр. Платформа 1С автоматически преобразует такую таблицу во временную таблицу на уровне SQL-сервера.
Проанализируем пример кода для передачи массива телефонных номеров:
// 1. Подготовим массив данных
МассивНомеров = Новый Массив;
МассивНомеров.Добавить("79991112233");
МассивНомеров.Добавить("79995556677");
// 2. Создадим таблицу значений с явной типизацией
ТЗ_Номера = Новый ТаблицаЗначений;
// Важно: всегда указывайте описание типов, чтобы избежать NVARCHAR(MAX) в SQL
КвалификаторыСтроки = Новый КвалификаторыСтроки(20);
ОписаниеТипаСтрока = Новый ОписаниеТипов("Строка", , КвалификаторыСтроки);
ТЗ_Номера.Колонки.Добавить("Номер", ОписаниеТипаСтрока);
// 3. Заполним ТЗ из массива (быстрый способ без циклов)
ТЗ_Номера.ЗагрузитьКолонку(МассивНомеров, "Номер");
// 4. Передадим ТЗ в запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Номер КАК НомерТелефонный
|ПОМЕСТИТЬ ВТ_Номера
|ИЗ
| &ВнешняяТЗ КАК ТЗ
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Номера.НомерТелефонный,
| Контрагенты.Ссылка КАК Контрагент
|ИЗ
| ВТ_Номера КАК ВТ_Номера
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО ВТ_Номера.НомерТелефонный = Контрагенты.Телефон";
Запрос.УстановитьПараметр("ВнешняяТЗ", ТЗ_Номера);
Результат = Запрос.Выполнить();
Важный нюанс: Метод ЗагрузитьКолонку() работает значительно быстрее, чем обход массива в цикле Для Каждого, особенно если в массиве тысячи элементов. При использовании этого способа мы гарантируем системе, что в колонке будут только строки определенной длины, что критически важно для производительности ЛЕВОЕ СОЕДИНЕНИЕ.
Если по какой-то причине мы не хотим создавать объект ТаблицаЗначений (например, в очень простых скриптах или при работе в контексте, где создание объектов ограничено), можно использовать "хитрость" с объединением результатов выбора пустых значений. Рассмотрим, как реализовать этот подход программно.
Выясним, как сформировать текст запроса динамически:
// Предположим, у нас есть массив простых чисел или строк
МассивДанных = Новый Массив;
МассивДанных.Добавить(101);
МассивДанных.Добавить(102);
ТекстОбъединения = "";
Для Сч = 0 По МассивДанных.Количество() - 1 Цикл
ТекстОбъединения = ТекстОбъединения + "ВЫБРАТЬ &Пар" + Сч;
Если Сч < МассивДанных.Количество() - 1 Тогда
ТекстОбъединения = ТекстОбъединения + " ОБЪЕДИНИТЬ ВСЕ " + Символы.ПС;
КонецЕсли;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ (" + ТекстОбъединения + ") КАК Таб; ВЫБРАТЬ * ИЗ ВТ_Данные";
Для Сч = 0 По МассивДанных.Количество() - 1 Цикл
Запрос.УстановитьПараметр("Пар" + Сч, МассивДанных[Сч]);
КонецЦикла;
Проанализируем ограничения этого метода:
UNION (обычно от 256 до 500). Если в массиве будет 1000 элементов, запрос выдаст ошибку переполнения стека парсера.Иногда задача стоит так: нужно получить таблицу всех элементов, которые есть в массиве, но при этом они уже существуют в базе данных (например, это ссылки на справочник). В этом случае нам не нужно превращать массив в ВТ напрямую. Мы можем использовать массив как фильтр для существующей таблицы метаданных.
Посмотрим на пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Код КАК Код
|ПОМЕСТИТЬ ВТ_Существующие
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МойМассив);
Однако помните: если ваша цель — найти, каких элементов НЕТ в базе данных, этот метод не подойдет, так как запрос вернет только пересечение массива и данных таблицы. Для поиска отсутствующих элементов (через ЕСТЬ NULL при левом соединении) обязательно используйте Способ №1.
Проанализировав все варианты, мы приходим к выводу, что наиболее профессиональным и безопасным методом превращения массива в виртуальную таблицу является использование ТаблицыЗначений.
Основные правила при работе с этим механизмом:
ОписаниеТипов для колонок ТЗ. Это предотвращает создание неоптимальных полей в базе данных.ЗагрузитьКолонку() для быстрого наполнения таблицы данными.ПОМЕСТИТЬ) в первом же пакете запроса. Это позволит SQL-серверу построить статистику и ускорить последующие соединения.Используя эти подходы, вы сможете эффективно обрабатывать любые наборы данных в запросах, обеспечивая высокую скорость работы и стабильность вашей конфигурации 1С.