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

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

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

Анализируем причину возникновения ошибки

Давайте разберем ситуацию, с которой столкнулся автор темы. Имеется внешняя таблица (параметр запроса), в которой одна из колонок имеет тип ТаблицаЗначений. При попытке выполнить следующий код:


ВЫБРАТЬ
    ТаблицаДокументовПриемника.ТаблицаНоменклатуры
ПОМЕСТИТЬ ВТ_1
ИЗ
    &ТаблицаДокументовПриемника КАК ТаблицаДокументовПриемника

Платформа выдает ошибку. Это происходит из-за фундаментальных различий между объектной моделью 1С и реляционной моделью СУБД (SQL), на которой базируется механизм запросов. Проанализируем ключевые причины:

  1. Несоответствие типов данных: Когда мы передаем ТаблицаЗначений в запрос, система 1С пытается создать временную таблицу в базе данных. Поля этой таблицы должны соответствовать примитивным типам SQL (строка, число, дата, булево) или ссылкам на объекты БД. Объект ТаблицаЗначений — это сложная структура в оперативной памяти, которая не имеет прямого аналога в типах данных SQL.
  2. Отсутствие автоматической сериализации: Запрос не умеет «распаковывать» сложные объекты внутри ячеек. Для SQL-движка содержимое такой колонки — это просто указатель на область памяти, с которой он не может работать.
  3. Первая нормальная форма: Классические реляционные базы данных требуют, чтобы значения в колонках были атомарными. Вложенные таблицы нарушают этот принцип, поэтому механизм запросов 1С, следующий логике SQL, блокирует такие операции.

Решение 1: Программный перебор коллекций (Циклы)

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


// Предположим, ТЗИсточник — это наша исходная таблица с вложенными ТЗ
// ТЗПриемник — таблица, в которую мы хотим собрать все данные

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

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

Решение 2: Денормализация (Подготовка «плоской» таблицы)

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

Разберем этот процесс по шагам:

  1. Создание структуры: Создаем новую ТаблицаЗначений, которая содержит все необходимые колонки из вложенной таблицы, а также дополнительные идентификаторы из родительской таблицы (например, НомерДокумента или Ссылка), чтобы сохранить связь.
  2. Типизация: Это критически важный момент. Чтобы таблица могла быть передана в запрос, её колонки должны быть строго типизированы. Используйте объект ОписаниеТипов при создании колонок.
  3. Заполнение: Переносим данные из всех вложенных таблиц в эту общую структуру.
  4. Вызов запроса: Передаем подготовленную «плоскую» таблицу в запрос как параметр.

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


КЧ = Новый КвалификаторыЧисла(15, 2);
МассивТиповЧисло = Новый Массив;
МассивТиповЧисло.Добавить(Тип("Число"));
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТиповЧисло, , , КЧ);

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

Решение 3: Разделение на несколько временных таблиц

Если данных очень много, и вы хотите использовать мощь запросов для их соединения, можно передать в запрос две отдельные таблицы:

  1. Таблица «Шапки»: Содержит основные данные документов и уникальный ключ связи.
  2. Таблица «Строки»: Содержит данные всех вложенных таблиц, объединенные в один массив, с тем же ключом связи.

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

Важные замечания по типизации колонок

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

Проанализируем также использование метода Выгрузить() для табличных частей. Когда вы используете ДокументОбъект.Товары.Выгрузить(), вы получаете таблицу значений с уже типизированными колонками, соответствующими метаданным документа. Это упрощает задачу, так как такие таблицы легче передавать в параметры запроса после их предварительного объединения.

Резюме

Работа с вложенными таблицами в запросах напрямую невозможна из-за ограничений архитектуры 1С и SQL. Для решения задачи мы рекомендуем:

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

← На главную