В практике разработчика 1С нередко возникают ситуации, когда данные организованы в сложные иерархические структуры. Одним из таких примеров является ТаблицаЗначений, где в одной из колонок хранятся другие таблицы значений (например, выгруженные табличные части документов). Попытка передать такую структуру в запрос и извлечь данные из вложенной таблицы напрямую приводит к ошибке «Тип не может быть выбран в запросе». В этой статье мы подробно разберем, почему возникает эта проблема, рассмотрим ограничения платформы и изучим эффективные способы обхода данной ситуации.
Давайте разберем ситуацию, с которой столкнулся автор темы. Имеется внешняя таблица (параметр запроса), в которой одна из колонок имеет тип ТаблицаЗначений. При попытке выполнить следующий код:
ВЫБРАТЬ
ТаблицаДокументовПриемника.ТаблицаНоменклатуры
ПОМЕСТИТЬ ВТ_1
ИЗ
&ТаблицаДокументовПриемника КАК ТаблицаДокументовПриемника
Платформа выдает ошибку. Это происходит из-за фундаментальных различий между объектной моделью 1С и реляционной моделью СУБД (SQL), на которой базируется механизм запросов. Проанализируем ключевые причины:
ТаблицаЗначений в запрос, система 1С пытается создать временную таблицу в базе данных. Поля этой таблицы должны соответствовать примитивным типам SQL (строка, число, дата, булево) или ссылкам на объекты БД. Объект ТаблицаЗначений — это сложная структура в оперативной памяти, которая не имеет прямого аналога в типах данных SQL.Если нам нужно собрать данные из всех вложенных таблиц в одну общую таблицу (например, для последующего анализа или вывода), самым простым и надежным способом будет использование вложенных циклов. Рассмотрим пример кода, который предложил автор для решения своей задачи:
// Предположим, ТЗИсточник — это наша исходная таблица с вложенными ТЗ
// ТЗПриемник — таблица, в которую мы хотим собрать все данные
Для каждого СтрТЗИсточник из ТЗИсточник Цикл
// Обращаемся к колонке, содержащей вложенную таблицу
Для Каждого СтрСтрТЗИсточник из СтрТЗИсточник.ТаблицаНоменклатуры Цикл
// Добавляем новую строку в общую таблицу и копируем данные
НоваяСтрока = ТЗПриемник.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрСтрТЗИсточник);
КонецЦикла;
КонецЦикла;
В данном примере мы используем метод ЗаполнитьЗначенияСвойств, который значительно ускоряет процесс копирования данных между строками, если имена колонок во вложенной таблице и таблице-приемнике совпадают. Этот метод эффективен для небольших и средних объемов данных.
Если в дальнейшем вам все же необходимо использовать язык запросов для агрегации или фильтрации этих данных, рекомендуется «выпрямить» структуру до передачи в запрос. Вместо иерархической таблицы создайте одну плоскую таблицу значений.
Разберем этот процесс по шагам:
ТаблицаЗначений, которая содержит все необходимые колонки из вложенной таблицы, а также дополнительные идентификаторы из родительской таблицы (например, НомерДокумента или Ссылка), чтобы сохранить связь.ОписаниеТипов при создании колонок.Пример подготовки типизированной таблицы:
КЧ = Новый КвалификаторыЧисла(15, 2);
МассивТиповЧисло = Новый Массив;
МассивТиповЧисло.Добавить(Тип("Число"));
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТиповЧисло, , , КЧ);
ТЗ_ДляЗапроса = Новый ТаблицаЗначений;
ТЗ_ДляЗапроса.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ_ДляЗапроса.Колонки.Добавить("Количество", ОписаниеТипаЧисло);
// Заполняем данными через циклы...
Если данных очень много, и вы хотите использовать мощь запросов для их соединения, можно передать в запрос две отдельные таблицы:
В самом запросе мы можем выполнить ЛЕВОЕ СОЕДИНЕНИЕ этих таблиц по ключу, что позволит работать с данными так, как если бы они изначально были связаны в базе данных.
Как справедливо отмечено в обсуждении, колонки должны быть типизированы. Если вы передаете таблицу значений в запрос, платформа должна знать точный размер и тип данных для создания полей во временной таблице SQL. Если колонка в вашей ТЗ имеет тип «Произвольный» (что часто бывает по умолчанию при программном создании таблицы без указания типов), запрос выдаст ошибку еще до начала обработки данных, даже если там нет вложенных таблиц.
Проанализируем также использование метода Выгрузить() для табличных частей. Когда вы используете ДокументОбъект.Товары.Выгрузить(), вы получаете таблицу значений с уже типизированными колонками, соответствующими метаданным документа. Это упрощает задачу, так как такие таблицы легче передавать в параметры запроса после их предварительного объединения.
Работа с вложенными таблицами в запросах напрямую невозможна из-за ограничений архитектуры 1С и SQL. Для решения задачи мы рекомендуем:
Следуя этим правилам, вы сможете эффективно обрабатывать любые сложные структуры данных, сохраняя при этом производительность и стабильность вашего кода в системе 1С:Предприятие.