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

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

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

Выясним причину: почему поле не отображается в строке итогов

Проанализируем механику работы секции ИТОГИ в языке запросов 1С. Когда в запросе описывается иерархия с помощью конструкции ИТОГИ ... ПО, для каждого уровня группировки платформа создает отдельную сводную запись.

В строках итоговых записей платформа заполняет только два типа полей:

  1. Поля, явно перечисленные в секции ПО для текущей группировки.
  2. Поля, для которых в секции ИТОГИ указаны агрегатные функции (СУММА, МАКСИМУМ, МИНИМУМ, КОЛИЧЕСТВО).

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

Реквизиты самого группировочного объекта (например, ЗаказПокупателя.ДатаОтгрузки) при этом могут быть доступны, так как они получаются через разыменование ссылки самого поля группировки ЗаказПокупателя.

Способ 1. Использование агрегатной функции МАКСИМУМ в секции ИТОГИ

Если бизнес-логика предполагает, что на один заказ покупателя приходится ровно один связанный документ (или связь отсутствует), самым простым и эффективным решением является добавление ссылки в список агрегируемых полей секции ИТОГИ с функцией МАКСИМУМ.

Рассмотрим, как модифицировать завершающую часть запроса:


ВЫБРАТЬ
    ТабСвободныеОстатки.Приложение КАК Приложение,
    ТабСвободныеОстатки.ЗаказПокупателя КАК ЗаказПокупателя,
    ТабСвободныеОстатки.Номенклатура КАК Номенклатура,
    ТабСвободныеОстатки.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
    ТабСвободныеОстатки.ОбъемОтгрузкиПоЗаказу КАК ОбъемОтгрузкиПоЗаказу,
    ТабСвободныеОстатки.Цена КАК Цена,
    ТабСвободныеОстатки.Сумма КАК Сумма,
    ТабСвободныеОстатки.ПриказНаряд КАК ПриказНаряд
ИЗ
    ТабСвободныеОстатки КАК ТабСвободныеОстатки
ИТОГИ
    МАКСИМУМ(ПриказНаряд),
    СУММА(ОбъемОтгрузкиПоЗаказу),
    СУММА(Сумма)
ПО
    Приложение,
    ЗаказПокупателя,
    Номенклатура

В этом случае значение поля ПриказНаряд будет рассчитано платформой для каждого уровня группировки (включая строку заказа покупателя) и станет доступно как в выборке по группировкам, так и в строках дерева значений.

Способ 2. Включение поля в состав иерархии или составной группировки

Если требуется выделить связанный документ в самостоятельный уровень дерева значений между заказом и номенклатурой (структура вида: Условие поставки -> Заказ покупателя -> Приказ-наряд -> Номенклатура), добавим поле в список группировок секции ИТОГИ.

Посмотрим на пример иерархического разбиения:


ИТОГИ
    СУММА(ОбъемОтгрузкиПоЗаказу),
    СУММА(Сумма)
ПО
    Приложение,
    ЗаказПокупателя,
    ПриказНаряд,
    Номенклатура

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


ИТОГИ
    СУММА(ОбъемОтгрузкиПоЗаказу),
    СУММА(Сумма)
ПО
    Приложение,
    (ЗаказПокупателя, ПриказНаряд),
    Номенклатура

Способ 3. Построение дерева значений программным обходом

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

Разберем по шагам алгоритм программного формирования дерева:


Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса; // Запрос без секции ИТОГИ
РезультатЗапроса = Запрос.Выполнить();

ТаблицаДанных = РезультатЗапроса.Выгрузить();
ДеревоРезультат = Новый ДеревоЗначений;

// Добавляем необходимые колонки
ДеревоРезультат.Колонки.Добавить("Ссылка");
ДеревоРезультат.Колонки.Добавить("ПриказНаряд");
ДеревоРезультат.Колонки.Добавить("Количество");

// Группируем и заполняем уровни дерева вручную
ТаблицаПриложений = ТаблицаДанных.Скопировать();
ТаблицаПриложений.Свернуть("Приложение");

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

Важные рекомендации по оптимизации запроса

При написании подобных комплексных запросов обратим внимание на критические моменты производительности:

  1. Параметры виртуальных таблиц: Условия соединения таблиц остатков по полям ЗаказПокупателя, Номенклатура или Склад необходимо передавать в параметры виртуальной таблицы (например, РегистрНакопления.лзПриказНаряды.Остатки(&Период, ЗаказПокупателя = &Заказ)), а не накладывать в секциях ПО или ГДЕ. Это исключает избыточное чтение всех остатков регистра в оперативную память СУБД.
  2. Разыменование ссылок через точку: Многократные конструкции вида ЗаказПокупателя.неоУсловиеПоставкиПоДоговору.ДатаНачала порождают неявные соединения с таблицами базы данных. Рекомендуется помещать реквизиты документов во временную таблицу один раз на начальном этапе выполнения запроса.
← На главную