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

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

Часто перед разработчиками 1С встает задача получить данные в специфическом формате, например, для последующей конвертации в JSON или передачи во внешние системы. Типичный пример — выборка основной информации о Номенклатуре, где для каждой позиции должна быть привязана «вложенная» таблица с данными из регистров сведений или других источников. В этой статье мы разберем, почему стандартный язык запросов 1С имеет ограничения на создание произвольных вложенных таблиц и какими методами можно достичь желаемого результата.

Ограничения языка запросов 1С и понятие вложенной таблицы

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

Рассмотрим ситуацию, описанную в теме: автор пытается поместить данные из регистра во временную таблицу и «вставить» её в основной запрос. Прямым путем это сделать невозможно. Однако мы можем имитировать такое поведение, используя несколько проверенных подходов: ИТОГИ, Дерево значений или Систему компоновки данных (СКД). Разберем каждый из них подробно.

Способ №1: Использование предложения ИТОГИ и иерархического обхода

Этот метод является классическим для решения задач, где нужно получить структуру «Заголовок — Состав». Мы объединяем данные основной таблицы и подчиненной таблицы с помощью ЛЕВОЕ СОЕДИНЕНИЕ, а затем группируем результат с помощью ИТОГИ.

Рассмотрим по шагам реализацию этого метода в коде:


// 1. Подготовим текст запроса
ТекстЗапроса = 
"ВЫБРАТЬ
|	Номенклатура.Ссылка КАК Номенклатура,
|	Номенклатура.Наименование КАК Наименование,
|	Номенклатура.Артикул КАК Артикул,
|	ДанныеГрузов.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
|	ДанныеГрузов.Вес КАК Вес,
|	ДанныеГрузов.Объем КАК Объем
|ИЗ
|	Справочник.Номенклатура КАК Номенклатура
|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.уатНоменклатураГрузов КАК ДанныеГрузов
|		ПО ДанныеГрузов.Номенклатура = Номенклатура.Ссылка
|ГДЕ
|	Номенклатура.Наименование ПОДОБНО &Наименование
|ИТОГИ ПО
|	Номенклатура";

Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("Наименование", "%" + ИскомоеИмя + "%");

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

Теперь самое важное — как правильно прочитать этот результат, чтобы получить вложенность. Мы будем использовать метод Выбрать с параметром ОбходРезультатаЗапроса.ПоГруппировкам. Проанализируем цикл обхода:


// 2. Обходим иерархию для формирования структуры (например, для JSON)
МассивРезультат = Новый Массив;
ВыборкаНоменклатура = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаНоменклатура.Следующий() Цикл
	
	СтруктураОбъекта = Новый Структура;
	СтруктураОбъекта.Вставить("Наименование", ВыборкаНоменклатура.Наименование);
	СтруктураОбъекта.Вставить("Артикул", ВыборкаНоменклатура.Артикул);
	
	// Здесь мы формируем массив, который станет "вложенной таблицей"
	ВложеннаяТаблица = Новый Массив;
	
	ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		// Если данных в регистре нет, левое соединение вернет NULL, проверим это
		Если ВыборкаДетальныеЗаписи.ЕдиницаИзмерения = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		СтрокаГруза = Новый Структура;
		СтрокаГруза.Вставить("Вес", ВыборкаДетальныеЗаписи.Вес);
		СтрокаГруза.Вставить("Объем", ВыборкаДетальныеЗаписи.Объем);
		ВложеннаяТаблица.Добавить(СтрокаГруза);
	КонецЦикла;
	
	СтруктураОбъекта.Вставить("ДанныеИзРег", ВложеннаяТаблица);
	МассивРезультат.Добавить(СтруктураОбъекта);
	
КонецЦикла;

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

Способ №2: Выгрузка в Дерево Значений

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

Разберем, как быстро преобразовать результат запроса в дерево:


// Используем тот же запрос с ИТОГАМИ
Дерево = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);

// Теперь в переменной "Дерево" находится иерархическая коллекция.
// Верхний уровень - это строки Номенклатуры.
// Каждая строка содержит коллекцию "Строки" - это и есть наша вложенная таблица.

Преимущество этого метода в том, что ДеревоЗначений легко сериализуется в XDTO или может быть передано на форму для отображения пользователю в виде иерархического списка.

Способ №3: Использование Системы Компоновки Данных (СКД)

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

Проанализируем алгоритм работы через СКД:

  1. Создадим первый Набор данных - запрос для выбора Номенклатуры.
  2. Создадим второй Набор данных - запрос для выбора данных из Регистра Сведений.
  3. Перейдем на вкладку Связи наборов данных. Укажем источником связи первый набор, приемником — второй. В качестве условия связи выберем поле Номенклатура.
  4. В настройках структуры СКД добавим группировку по Номенклатуре, а внутрь неё поместим Детальные записи.

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

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

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

Посмотрим на пример правильной организации пакета запросов:


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Первый запрос пакета: помещаем данные во временную таблицу
Запрос.Текст = 
"ВЫБРАТЬ
|	уатНоменклатураГрузов.ЕдиницаИзмерения,
|	уатНоменклатураГрузов.Вес,
|	уатНоменклатураГрузов.Номенклатура
|ПОМЕСТИТЬ ВТ_ДанныеГрузов
|ИЗ
|	РегистрСведений.уатНоменклатураГрузов КАК уатНоменклатураГрузов";

Запрос.Выполнить();

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

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

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

Резюме

Для формирования иерархического результата (объект + вложенный список) в 1С следует использовать:

  1. Запрос с предложением ИТОГИ — если данные нужно получить быстро и в одном соединении.
  2. Обход по группировкам — для ручного формирования вложенных структур или массивов JSON.
  3. Дерево значений — для удобного хранения иерархии в памяти.
  4. Связи наборов данных в СКД — если логика получения «вложенных» данных слишком сложна для одного запроса.

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

← На главную