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

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

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

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

Способ 1. Итоги в запросе и обход по группировкам (Рекомендуемый программный метод)

Если печатная форма формируется программно с помощью кода, самым быстрым и правильным с точки зрения производительности решением будет перенос логики группировки на уровень СУБД. Для этого в языке запросов 1С предусмотрена конструкция ИТОГИ ПО.

Использование итогов позволяет платформе самостоятельно построить древовидную (иерархическую) структуру данных. Нам не придется вручную отбирать строки или удалять дубликаты дат — СУБД вернет уже готовую для обхода структуру.

Рассмотрим по шагам, как реализовать этот подход:

  1. Создание запроса с итогами: В конце текста запроса мы указываем предложение ИТОГИ ПО с перечислением полей группировки.
  2. Выборка по группировкам: При обработке результатов запроса мы используем специальный параметр обхода ОбходРезультатаЗапроса.ПоГруппировкам.
  3. Вложенные циклы: Во внешнем цикле мы получаем группировочные поля (периоды), а во внутреннем — детальные записи, относящиеся только к текущему периоду.

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


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

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

// 2. Инициализируем табличный документ и макет
ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("МакетПечати");

ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ТабДок.Вывести(ОбластьШапка);

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

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

Преимущества метода: Максимальная скорость работы. База данных оптимизирует выполнение запроса, а оперативная память сервера не перегружается лишними итерациями циклов.

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

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

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

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

Выясним, как программно вывести ТаблицуЗначений через СКД с группировкой по шагам:

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

// Предположим, что ТаблицаДанных — это наша подготовленная ТЗ с колонками Дата, Номенклатура, Сумма

СхемаКомпоновкиДанных = ПолучитьМакет("СхемаКомпоновкиДанных");
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);

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

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

ТабДок = Новый ТабличныйДокумент;

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

ТабДок.Показать();

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

Способ 3. Оптимизированная работа с Таблицей Значений (ТЗ)

Иногда разработчики идут по пути наименьшего сопротивления и реализуют группировку "в лоб": выгружают результат в ТЗ, получают массив уникальных дат, а затем в двойном цикле перебирают всю таблицу для каждой даты. Рассмотрим, почему это плохо.

При объеме таблицы всего в 10 000 строк и количестве уникальных дат равным 100, вложенный цикл совершит 1 000 000 итераций (сложность алгоритма составляет $O(N \times M)$). Это приведет к сильному зависанию системы.

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

Разберем оптимизированный алгоритм по шагам:


// Исходная ТаблицаДанных содержит колонки: Дата, Номенклатура, Количество

// Шаг 1. Индексируем таблицу по группировочному полю для мгновенного поиска строк
ТаблицаДанных.Индексы.Добавить("Дата");

// Шаг 2. Получаем список уникальных дат без дублей методом Свернуть()
ТаблицаДат = ТаблицаДанных.Скопировать(, "Дата");
ТаблицаДат.Свернуть("Дата");
ТаблицаДат.Сортировать("Дата Возр"); // Упорядочиваем по возрастанию

// Шаг 3. Быстрый обход без полного перебора во внутреннем цикле
ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("МакетПечати");

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

Преимущества метода: Скорость работы возрастает в десятки и сотни раз на больших объемах данных благодаря индексам СУБД-подобного поиска внутри коллекции ТаблицаЗначений в оперативной памяти.

Визуальное оформление группировок (Интерактивное сворачивание строк)

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

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

Посмотрим на практический пример интеграции автогруппировки в код вывода:


ТабДок = Новый ТабличныйДокумент;
ТабДок.НачатьАвтогруппировкуСтрок();

// Уровень 1 — это родительская группировка (наша дата)
// Уровень 2 — детальные строки (номенклатура)

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

ТабДок.ЗакончитьАвтогруппировкуСтрок();

// Указываем отображать кнопки сворачивания групп ("плюсы/минусы")
ТабДок.ПоказатьСетку = Ложь;
ТабДок.ПараметрыПечати.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;

// Опционально: сворачиваем отчет до первого уровня при открытии
ТабДок.ПоказатьУровеньГруппировокСтрок(1);

Резюме и рекомендации по выбору подхода

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

← На главную