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

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

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

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

Выясняем причину проблемы: как работают стандартные ИТОГИ

Рассмотрим исходную структуру данных. У нас есть таблица со следующими колонками: Заявка, ТМЦ, Параметр1, Параметр2, Физлицо, Сумма. Нам необходимо получить дерево значений следующего вида:

  1. Уровень 1: Заявка
  2. Уровень 2: ТМЦ, Параметр1, Параметр2 (все три поля в одной строке дерева)
  3. Уровень 3: Физлицо, Сумма (детальные записи в подчинении у ТМЦ)

Если написать запрос со стандартными итогами следующим образом:


ИТОГИ ПО
    Заявка,
    ТМЦ,
    Параметр1,
    Параметр2,
    Физлицо

Платформа создаст пятиуровневое дерево. Нам же нужно объединить три поля в один узел на втором уровне. Ниже мы разберем рабочие и эффективные методики решения этой задачи.

Решение 1: Использование агрегатных функций МАКСИМУМ / МИНИМУМ в запросе

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

Проанализируем структуру такого запроса. Мы исключаем сопутствующие параметры из секции ПО конструкции ИТОГИ, но рассчитываем по ним агрегатное значение МАКСИМУМ (или МИНИМУМ) на уровне итогов:


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

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

Как это работает под капотом платформы 1С?

Когда мы указываем ИТОГИ ПО Заявка, ТМЦ, Физлицо, дерево значений формируется строго по этим трем уровням группировки. Но за счет того, что мы добавили конструкции МАКСИМУМ(Параметр1) и МАКСИМУМ(Параметр2) в секцию расчетных полей итогов, платформа автоматически «подтягивает» и заполняет значения этих параметров на уровне строки ТМЦ. В результате в строке второго уровня мы увидим заполненные колонки ТМЦ, Параметр1 и Параметр2 одновременно.

Решение 2: Использование СКД (Система компоновки данных)

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

Разберем по шагам, как настроить такую схему компоновки программно или в конструкторе:

  1. Создадим схему компоновки данных и добавим набор данных - запрос. Наш запрос может быть самым простым, без секции ИТОГИ.
  2. Перейдем на вкладку «Настройки» СКД.
  3. Создадим структуру группировок:
    • Верхний уровень: группировка по полю Заявка.
    • Вложенный уровень: группировка, в которую мы добавляем сразу три поля в таблицу «Поля группировки»: ТМЦ, Параметр1 и Параметр2.
    • Нижний уровень: группировка по полю Физлицо.

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


// Создаем процессор компоновки и выполняем вывод в дерево значений
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки());

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);

ДеревоРезультата = Новый ДеревоЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоРезультата);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

// Теперь ДеревоРезультата содержит структуру с объединенными полями на одном уровне!

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

Решение 3: Ручная сборка дерева значений в цикле

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

Рассмотрим детальный пример сборки дерева вручную. Для этого мы выполним запрос без итогов, но упорядочим результат для последовательного обхода:


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

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

// Создаем структуру дерева значений вручную
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Заявка");
Дерево.Колонки.Добавить("ТМЦ");
Дерево.Колонки.Добавить("Параметр1");
Дерево.Колонки.Добавить("Параметр2");
Дерево.Колонки.Добавить("Физлицо");
Дерево.Колонки.Добавить("Сумма");

ТекущаяСтрокаЗаявки = Неопределено;
ТекущаяСтрокаТМЦ    = Неопределено;

Пока Выборка.Следующий() Цикл
    
    // Проверяем смену первого уровня (Заявка)
    Если ТекущаяСтрокаЗаявки = Неопределено Или ТекущаяСтрокаЗаявки.Заявка <> Выборка.Заявка Тогда
        ТекущаяСтрокаЗаявки = Дерево.Строки.Добавить();
        ТекущаяСтрокаЗаявки.Заявка = Выборка.Заявка;
        ТекущаяСтрокаТМЦ = Неопределено; // Сбрасываем подчиненный уровень
    КонецЕсли;
    
    // Проверяем смену второго уровня (ТМЦ + Параметры)
    Если ТекущаяСтрокаТМЦ = Неопределено 
        Или ТекущаяСтрокаТМЦ.ТМЦ <> Выборка.ТМЦ 
        Или ТекущаяСтрокаТМЦ.Параметр1 <> Выборка.Параметр1 
        Или ТекущаяСтрокаТМЦ.Параметр2 <> Выборка.Параметр2 Тогда
        
        ТекущаяСтрокаТМЦ = ТекущаяСтрокаЗаявки.Строки.Добавить();
        ТекущаяСтрокаТМЦ.ТМЦ       = Выборка.ТМЦ;
        ТекущаяСтрокаТМЦ.Параметр1 = Выборка.Параметр1;
        ТекущаяСтрокаТМЦ.Параметр2 = Выборка.Параметр2;
    КонецЕсли;
    
    // Добавляем детальные записи на третий уровень (Физлицо + Сумма)
    СтрокаДеталей = ТекущаяСтрокаТМЦ.Строки.Добавить();
    СтрокаДеталей.Физлицо = Выборка.Физлицо;
    СтрокаДеталей.Сумма    = Выборка.Сумма;
    
КонецЦикла;

Этот подход гарантирует 100% контроль над структурой дерева, исключает любые дублирования и позволяет гибко управлять процессом заполнения колонок.

Использование «Автономера записи» для разделения строк

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

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

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

Резюме и рекомендации

Для решения задачи объединения полей в одну строку дерева значений мы рекомендуем придерживаться следующих правил выбора метода:

← На главную