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

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

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

Разберем ограничения платформы 1С:Предприятие при работе с вложенными таблицами и проанализируем имеющиеся способы решения этой задачи.

Ограничение языка запросов 1С

В языке запросов 1С синтаксис выбора вложенных таблиц вида ИмяТаблицы.(Поле1, Поле2) поддерживается исключительно для табличных частей реальных объектов базы данных (документов, справочников, бизнес-процессов). Платформа не позволяет сформировать вложенную таблицу или колонку с типом ТаблицаЗначений прямо в секции ВЫБРАТЬ при обращении к временным таблицам, вложенным запросам или виртуальным таблицам.

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

Способ 1. Использование секции ИТОГИ ПО и иерархическая выборка

Наиболее эффективный подход без изменения схемы данных — использование конструкции ИТОГИ ПО с группировкой по ключевым полям первой временной таблицы. Это позволяет получить древовидную структуру прямо из запроса.

Рассмотрим пример запроса, связывающего две временные таблицы:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВТ1.Номенклатура КАК Номенклатура,
    |	ВТ1.ДокументОприходования КАК ДокументОприходования,
    |	ВТ1.Заказ КАК Заказ,
    |	ВТ2.Номенклатура КАК ВложеннаяНоменклатура,
    |	ВТ2.ДокументОприходования КАК ВложенныйДокумент
    |ИЗ
    |	ВТ_Шапка КАК ВТ1
    |		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Детали КАК ВТ2
    |		ПО ВТ1.Номенклатура = ВТ2.НоменклатураДляСвязи
    |			И ВТ1.ДокументОприходования = ВТ2.ДокументОприходованияДляСвязи
    |ИТОГИ ПО
    |	Номенклатура,
    |	ДокументОприходования";

РезультатЗапроса = Запрос.Выполнить();

После выполнения такого запроса мы можем обойти результат по группировкам или выгрузить его в дерево значений:


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

// Вариант 2. Выгрузка в Дерево Значений
ДеревоРезультата = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);

Способ 2. Программное формирование колонок с типом ТаблицаЗначений

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

Проанализируем этот алгоритм по шагам:

  1. Выполняем запрос с левым соединением первой и второй временных таблиц.
  2. Выгружаем результат первой таблицы в отдельную таблицу значений без дублей строк.
  3. Добавляем в полученную таблицу значений новую колонку (например, Состав) без указания типа или с типом ТаблицаЗначений.
  4. Выгружаем детализирующие строки второй таблицы в отдельную таблицу значений.
  5. В цикле заполняем колонку Состав с помощью метода Скопировать() или НайтиСтроки().

Посмотрим на пример реализации данного подхода:


// Допустим, ТЗ_Шапка содержит строки первой таблицы, а ТЗ_Детали — строки второй таблицы

ТЗ_Шапка.Колонки.Добавить("ИзЧегоСостоит", Новый ОписаниеТипов("ТаблицаЗначений"));

Для Каждого СтрИз Шапка Из ТЗ_Шапка Цикл
    Отбор = Новый Структура;
    Отбор.Вставить("НоменклатураДляСвязи", СтрИз Шапка.Номенклатура);
    Отбор.Вставить("ДокументОприходованияДляСвязи", СтрИз Шапка.ДокументОприходования);
    
    НайденныеСтроки = ТЗ_Детали.НайтиСтроки(Отбор);
    Если НайденныеСтроки.Количество() > 0 Тогда
        СтрИз Шапка.ИзЧегоСостоит = ТЗ_Детали.Скопировать(НайденныеСтроки);
    Иначе
        СтрИз Шапка.ИзЧегоСостоит = ТЗ_Детали.СкопироватьКолонки();
    КонецЕсли;
КонецЦикла;

Способ 3. Связи наборов данных в СКД

Если задача решается в контексте отчета или обработки на базе Схемы компоновки данных (СКД), проблему вложенности таблиц можно решить средствами компоновщика без написания кода обработки:

  1. Создаем два независимых набора данных (Набор данных - Запрос или Набор данных - Объект).
  2. В первом наборе выбираем данные первой временной таблицы.
  3. Во втором наборе выбираем данные второй временной таблицы.
  4. На вкладке Связи наборов данных настраиваем связь от первого набора ко второму по полям Номенклатура = НоменклатураДляСвязи и ДокументОприходования = ДокументОприходованияДляСвязи.
  5. В настройках структуры отчета добавляем вложенную группировку. СКД автоматически сформирует иерархический вывод подчинённых строк.

Рекомендации по производительности

При работе с временными таблицами большого объема всегда соблюдайте следующие правила:

← На главную