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

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

При разработке отчетов, обработок и процедур выгрузки данных (например, в форматы XML или JSON) разработчики часто сталкиваются с необходимостью одновременного получения реквизитов шапки объекта и строк его табличной части. Платформа 1С:Предприятие предоставляет для этого специальный синтаксис обращения к табличной части через точку со скобками.

Рассмотрим, является ли такой подход корректным, как он транслируется на уровне СУБД, к каким скрытым проблемам с производительностью может привести и какие существуют альтернативные способы решения этой задачи.

Синтаксис вложенных таблиц в тексте запроса

Разберем пример запроса, в котором поля табличной части выбираются непосредственно через имя реквизита-коллекции:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	Спр.Ссылка КАК Ссылка,
|	Спр.Поле1 КАК Поле1,
|	Спр.Поле2 КАК Поле2,
|	Спр.ТЧ1.(
|		ПолеТч1,
|		ПолеТч2
|	) КАК СоставТЧ
|ИЗ
|	Справочник.Номенклатура КАК Спр";

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

Данная конструкция полностью поддерживается языком запросов 1С. В результате выполнения такого запроса в основном наборе данных поле СоставТЧ будет содержать отдельный объект РезультатЗапроса для каждой строки шапки.

Как это работает на уровне СУБД и памяти

Проанализируем, что происходит на сервере при выполнении запроса с вложенной таблицей:

  1. Трансляция в SQL: Платформа 1С формирует на уровне СУБД классическое левое соединение (LEFT OUTER JOIN) таблицы шапки с таблицей табличной части. Сервер баз данных возвращает плоский результирующий набор строк, в котором поля шапки дублируются для каждой строки табличной части.
  2. Формирование вложенных результатов на сервере 1С: Сервер 1С принимает плоский поток данных и самостоятельно группирует его, преобразуя подчиненные строки во вложенные результаты запроса.
  3. Потребление оперативной памяти: Если при обходе выборки для каждой строки вызывать метод Выгрузить(), как в примере Выборка.СоставТЧ.Выгрузить(), платформа на каждой итерации будет создавать новый объект ТаблицаЗначений. При обработке сотен тысяч записей это приводит к лавинообразному расходу оперативной памяти и существенной нагрузке на сборщик мусора.

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


Пока Выборка.Следующий() Цикл
	// Обрабатываем поля шапки
	ЗначениеПоля1 = Выборка.Поле1;
	
	// Потоковый обход подчиненных записей без выгрузки в ТаблицуЗначений
	ВыборкаТЧ = Выборка.СоставТЧ.Выбрать();
	Пока ВыборкаТЧ.Следующий() Цикл
		ЗначениеТЧ = ВыборкаТЧ.ПолеТч1;
	КонецЦикла;
КонецЦикла;

Ограничения использования вложенных таблиц

Выясним, с какими жесткими ограничениями платформы можно столкнуться при использовании синтаксиса вложенных таблиц:

  1. Запрет на временные таблицы: Запрос, содержащий вложенную таблицу в секции ВЫБРАТЬ, нельзя поместить во временную таблицу с помощью конструкции ПОМЕСТИТЬ.
  2. Ограничения объединений: Запросы с вложенными таблицами нельзя использовать в операциях ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ.
  3. Невозможность фильтрации и группировки: Поля вложенных таблиц нельзя использовать в секциях ГДЕ, СГРУППИРОВАТЬ ПО, ИМЕЮЩИЕ, а также в секции УПОРЯДОЧИТЬ ПО основного запроса.
  4. Запрет во вложенных подзапросах: Конструкция не может быть использована внутри подзапросов в секциях ИЗ или ГДЕ.

Альтернативные способы получения табличных частей

Рассмотрим основные паттерны, которые применяются в профессиональной разработке взамен синтаксиса вложенных таблиц.

Вариант 1. Иерархическая выборка с помощью секции ИТОГИ ПО

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	Спр.Ссылка КАК Ссылка,
|	Спр.Поле1 КАК Поле1,
|	ТЧ.ПолеТч1 КАК ПолеТч1,
|	ТЧ.ПолеТч2 КАК ПолеТч2
|ИЗ
|	Справочник.Номенклатура КАК Спр
|	ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ТЧ1 КАК ТЧ
|		ПО Спр.Ссылка = ТЧ.Ссылка
|ИТОГИ
|	МАКСИМУМ(Поле1)
|ПО
|	Ссылка";

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

Пока ВыборкаШапка.Следующий() Цикл
	// Обработка данных шапки
	СсылкаЭлемента = ВыборкаШапка.Ссылка;
	
	// Обход детальных записей табличной части
	ВыборкаСтроки = ВыборкаШапка.Выбрать();
	Пока ВыборкаСтроки.Следующий() Цикл
		Если ЗначениеЗаполнено(ВыборкаСтроки.ПолеТч1) Тогда
			// Обработка строк табличной части
		КонецЕсли;
	КонецЦикла;
КонецЦикла;

Вариант 2. Прямой запрос к табличной части с контролем смены ссылки

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ТЧ.Ссылка КАК Ссылка,
|	ТЧ.Ссылка.Поле1 КАК Поле1,
|	ТЧ.ПолеТч1 КАК ПолеТч1,
|	ТЧ.ПолеТч2 КАК ПолеТч2
|ИЗ
|	Справочник.Номенклатура.ТЧ1 КАК ТЧ
|УПОРЯДОЧИТЬ ПО
|	ТЧ.Ссылка,
|	ТЧ.НомерСтроки";

Выборка = Запрос.Выполнить().Выбрать();
ТекущаяСсылка = Неопределено;

Пока Выборка.Следующий() Цикл
	Если ТекущаяСсылка <> Выборка.Ссылка Тогда
		ТекущаяСсылка = Выборка.Ссылка;
		// Выполняем действия при переходе к новому объекту (заголовок узла XML)
	КонецЕсли;
	
	// Выполняем обработку строки табличной части
КонецЦикла;

Вариант 3. Пакетный запрос с раздельной выгрузкой

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	Спр.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТ_ОтобранныеСсылки
|ИЗ
|	Справочник.Номенклатура КАК Спр
|ГДЕ
|	Спр.ПометкаУдаления = ЛОЖЬ
|ИНДЕКСИРОВАТЬ ПО
|	Ссылка;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	Спр.Ссылка КАК Ссылка,
|	Спр.Поле1 КАК Поле1
|ИЗ
|	ВТ_ОтобранныеСсылки КАК ВТ
|	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Спр
|		ПО ВТ.Ссылка = Спр.Ссылка;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ТЧ.Ссылка КАК Ссылка,
|	ТЧ.ПолеТч1 КАК ПолеТч1,
|	ТЧ.ПолеТч2 КАК ПолеТч2
|ИЗ
|	ВТ_ОтобранныеСсылки КАК ВТ
|	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ТЧ1 КАК ТЧ
|		ПО ВТ.Ссылка = ТЧ.Ссылка
|УПОРЯДОЧИТЬ ПО
|	ТЧ.Ссылка,
|	ТЧ.НомерСтроки";

ПакетРезультатов = Запрос.ВыполнитьПакет();
ТЗ_Шапки = ПакетРезультатов[1].Выгрузить();
ТЗ_Строки = ПакетРезультатов[2].Выгрузить();

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

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

  1. Использование вложенных таблиц Спр.ТЧ1.(...) допустимо: в простых сервисных обработках, небольших разовых выгрузках и сценариях, где объем обрабатываемых данных невелик и нет потребности в сложных фильтрах или помещении данных во временные таблицы. Обязательным условием является отказ от метода Выгрузить() в пользу потокового метода Выбрать().
  2. Использование секции ИТОГИ ПО рекомендуется: во всех задачах массовой потоковой выгрузки данных, генерации отчетов и формирования XML/JSON-документов большого объема. Этот подход обеспечивает оптимальную скорость работы и минимальное потребление оперативной памяти.
  3. Пакетные запросы и соединения рекомендуются: в высоконагруженных модулях проведения, сложных расчетных алгоритмах и при необходимости гибкой фильтрации данных по условиям как шапки, так и табличных частей.
← На главную