Как передать Таблицу Значений с колонкой типа Массив в запрос 1С?

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

При разработке сложных механизмов в 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью обработки данных, которые уже подготовлены в оперативной памяти в виде ТаблицыЗначений. Одной из специфических и неприятных проблем является попытка передать в запрос таблицу, одна из колонок которой имеет тип Массив. В этой статье мы подробно разберем, почему возникает ошибка «Тип не может быть выбран в запросе», и рассмотрим эффективные способы решения этой задачи.

Выясним причину возникновения ошибки

Проанализируем ситуацию, с которой столкнулся автор вопроса. При попытке поместить ТаблицуЗначений (ТЗ) во временную таблицу запроса с помощью конструкции ПОМЕСТИТЬ, платформа 1С выполняет трансляцию этой таблицы в реальную физическую таблицу на стороне СУБД (MS SQL Server, PostgreSQL и др.).

Важный технический нюанс: в системе управления базами данных нет прямого аналога объекту Массив из 1С. СУБД понимает простые типы данных: числа, строки, даты, булево и ссылки (которые по сути являются уникальными идентификаторами — GUID). Когда платформа встречает в колонке тип Массив, она не понимает, как именно нужно «разложить» этот объект в структуру таблицы базы данных, и выдает ошибку {(5, 2)}: Тип не может быть выбран в запросе <<?>>ТЗ.Оплаты КАК Оплаты.

Рассмотрим, какие типы данных допустимы для передачи из ТЗ в запрос:

Любые коллекции (Структура, Массив, СписокЗначений, ТаблицаЗначений) внутри колонки приведут к ошибке.

Способ №1: Метод «Развертывания» (Создание второй таблицы)

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

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

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


// Создаем основную таблицу
ТЗ_Шапка = Новый ТаблицаЗначений;
ТЗ_Шапка.Колонки.Добавить("КлючСвязи", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0)));
ТЗ_Шапка.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТЗ_Шапка.Колонки.Добавить("СуммаЧека", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));

// Создаем таблицу для "Массивов" (Развернутую)
ТЗ_Оплаты = Новый ТаблицаЗначений;
ТЗ_Оплаты.Колонки.Добавить("КлючСвязи", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0)));
ТЗ_Оплаты.Колонки.Добавить("ЗначениеОплаты", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));

Счетчик = 0;
Для Каждого СтрокаОплаты Из МассивОплат Цикл
    Счетчик = Счетчик + 1;
    
    // Заполняем шапку
    НоваяСтрокаШапки = ТЗ_Шапка.Добавить();
    НоваяСтрокаШапки.КлючСвязи = Счетчик;
    НоваяСтрокаШапки.Дата = ТекущаяДата();
    
    // Разворачиваем массив во вторую таблицу
    Для Каждого ТекущаяОплата Из СтрокаОплаты.МассивОплат Цикл
        НоваяСтрокаОплаты = ТЗ_Оплаты.Добавить();
        НоваяСтрокаОплаты.КлючСвязи = Счетчик;
        НоваяСтрокаОплаты.ЗначениеОплаты = ТекущаяОплата;
    КонецЦикла;
КонецЦикла;

Теперь в запросе мы можем легко соединить эти таблицы:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ * ПОМЕСТИТЬ ВтШапка ИЗ &ТЗ_Шапка КАК ТЗ_Шапка;
    |ВЫБРАТЬ * ПОМЕСТИТЬ ВтОплаты ИЗ &ТЗ_Оплаты КАК ТЗ_Оплаты;
    |
    |ВЫБРАТЬ
    |    ВтШапка.Дата,
    |    ВтОплаты.ЗначениеОплаты
    |ИЗ
    |    ВтШапка КАК ВтШапка
    |ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВтОплаты КАК ВтОплаты
    |    ПО ВтШапка.КлючСвязи = ВтОплаты.КлючСвязи";

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

Способ №2: Сериализация в строку JSON

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

Алгоритм действий:

  1. При создании ТаблицыЗначений укажите для колонки «Оплаты» тип Строка (длина 0 или неограниченная).
  2. Перед передачей таблицы в запрос преобразуйте каждый массив в строку формата JSON.
  3. В запросе выберите эту колонку как обычную строку.
  4. После получения результата запроса выполните десериализацию обратно в массив.

Посмотрим на пример кода трансформации:


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

Для Каждого СтрТЗ Из ТЗ Цикл
    // Используем ЗаписьJSON для преобразования массива в строку
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, СтрТЗ.Оплаты); // Здесь Оплаты - это еще массив
    СтрТЗ.Оплаты = ЗаписьJSON.Закрыть();   // Теперь в колонке строка
КонецЦикла;

// Теперь таблицу можно смело передавать в запрос
Запрос.УстановитьПараметр("ТЗ", ТЗ);

Сравнительный анализ методов

Проанализируем ситуацию: какой метод выбрать? Если ваша задача — посчитать итоги по оплатам, выполнить соединение с другими таблицами базы данных по значениям из массива или наложить фильтр, то Способ №1 (Развертывание) является единственно верным. Он позволяет использовать всю мощь языка запросов SQL.

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

Рекомендация по описанию типов

Помните, что при передаче ТаблицыЗначений в запрос крайне важно, чтобы типы колонок были жестко типизированы. Если вы создадите колонку без указания типа (тип Произвольный), платформа попытается определить тип по первой строке, что часто приводит к непредсказуемым ошибкам, если в первой строке значение NULL или Неопределено.

Всегда используйте конструктор Новый ОписаниеТипов(...) при добавлении колонок, которые планируете использовать в запросах. Это гарантирует стабильную работу вашего кода и отсутствие ошибок при помещении данных во временные таблицы.

← На главную