Как вывести таблицу в таблице в СКД 1С: ограничения платформы и обходные пути

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

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

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

Почему стандартный механизм «вложенной схемы» не работает в таблицах СКД

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

  1. Блокировка интерфейса настроек: В СКД технически невозможно добавить объект Вложенный отчет внутрь строк или колонок элемента структуры Таблица (кросс-таблицы). В контекстном меню для группировок таблицы данный пункт меню будет неактивен.
  2. Ограничение вложенности: Платформа не позволяет вложить один элемент структуры Таблица внутрь другого элемента типа Таблица на одном уровне компоновки.
  3. Проблемы производительности: Использование механизма Вложенный отчет для вывода детальных данных в циклах строк родительского отчета крайне неэффективно. На уровне СУБД это порождает проблему «N+1 запросов» (Nested Loops), когда для каждой строки основного отчета платформа выполняет отдельный запрос к базе данных. На реальных базах данных это мгновенно приведет к критическому падению производительности и зависанию сервера.

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

Вариант А. Построение структуры «Группировка -> Таблица»

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

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

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

Вариант Б. Использование связей наборов данных

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

  1. В схеме компоновки данных создадим два независимых набора данных-запроса: НаборДанных1 (основные показатели, например, общие продажи по подразделениям) и НаборДанных2 (детализация продаж по видам номенклатуры).
  2. Перейдем на вкладку «Связи наборов данных».
  3. Добавим новую связь, где источником выступит НаборДанных1, приемником — НаборДанных2. В качестве условия связи укажем ключевое поле (например, Подразделение).
  4. В параметрах связи укажем передачу параметров для оптимизации запроса приемника. Это позволит платформе автоматически склеивать запросы на уровне СУБД и предотвратит падение производительности.
  5. В структуре настроек отчета выведем данные через стандартные подчиненные группировки. Детальные записи второго набора будут аккуратно выводиться внутри родительских строк.

Вариант В. Сворачивание данных через функцию «СоединитьСтроки»

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

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

  1. Создадим вычисляемое поле или выберем детальное поле в качестве ресурса.
  2. На вкладке «Ресурсы» для этого поля в выражении укажем встроенную функцию СКД СоединитьСтроки(...).
  3. Синтаксис функции выглядит следующим образом: СоединитьСтроки(ВидКорма, ", ", "-"), где вторым параметром передается разделитель строк.

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

Вариант Г. Программный вывод и постобработка в «ПриКомпоновкеРезультата»

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

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

  1. В модуле объекта отчета перехватим стандартное событие компоновки, добавив процедуру ПриКомпоновкеРезультата() и установив параметр СтандартнаяОбработка = Ложь.
  2. Выгрузим результат компоновки не напрямую в табличный документ, а в коллекцию ДеревоЗначений.
  3. Программно обойдем полученное дерево и выведем данные в результирующий ТабличныйДокумент с применением ручной группировки строк через методы НачатьГруппуСтрок() и ЗакончитьГруппуСтрок().

Посмотрим на пример реализации кода в модуле объекта отчета:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // Формируем макет компоновки данных
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
    
    // Инициализируем процессор компоновки
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
    
    // Выводим результат компоновки в дерево значений
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВДеревоЗначений;
    ДеревоРезультата = Новый ДеревоЗначений;
    ПроцессорВывода.УстановитьОбъект(ДеревоРезультата);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
    // Очищаем результирующий документ и запускаем ручной вывод
    ДокументРезультат.Очистить();
    ВывестиДеревоДанныхВДокумент(ДеревоРезультата.Строки, ДокументРезультат);
    
КонецПроцедуры

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


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

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

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

← На главную