При работе с Системой Компоновки Данных (СКД) в 1С мы часто сталкиваемся с необходимостью построения иерархических отчетов. Стандартные механизмы СКД прекрасно справляются с иерархическими справочниками, где есть предопределенное поле Родитель. Однако что делать, если ваша иерархия не соответствует типовой структуре справочников, а формируется на основе произвольных связей, например, "Документ <-> Родитель" между элементами одного или разных типов документов, и при этом глубина вложенности заранее неизвестна? Именно эту задачу мы сегодня подробно рассмотрим и разберем.
Создание такой "своей" иерархии требует более гибкого подхода, выходящего за рамки простого указания признака "Иерархия" для поля. Мы выясним, как правильно оформить подобную структуру в СКД, используя вспомогательные наборы данных и рекурсивные связи, которые позволят системе самостоятельно "раскручивать" иерархию на любую необходимую глубину.
Для создания произвольной иерархии в СКД, даже если все необходимые поля для построения иерархической структуры присутствуют в одном основном наборе данных, часто используется подход с созданием вспомогательного набора данных. Этот вспомогательный набор будет содержать только ту информацию, которая описывает саму иерархическую структуру: уникальный идентификатор элемента и ссылку на его родителя. Рассмотрим, почему это так, и какие преимущества дает такой подход.
Разделение ответственности: Основной набор данных отвечает за предоставление детальной информации, которую мы хотим видеть в отчете (например, реквизиты документа, суммы, даты). Вспомогательный набор данных фокусируется исключительно на связях "Родитель-Потомок", что делает схему более прозрачной и управляемой.
Гибкость и переиспользование: Отдельный иерархический набор данных можно использовать для построения разных отчетов, не дублируя логику иерархии в каждом основном наборе. Это особенно удобно, если у вас несколько отчетов, использующих одну и ту же иерархическую структуру.
Принцип рекурсивного построения иерархии: СКД обладает мощным механизмом рекурсивного построения иерархии. Этот механизм активируется за счет правильной настройки связей наборов данных, где один и тот же набор связывается "сам с собой" по полям, представляющим элемент и его родителя. Таким образом, СКД "самостоятельно" обходит все уровни вложенности, формируя древовидную структуру без необходимости писать сложный рекурсивный запрос в SQL или программно.
Критически важно понимать, что СКД поддерживает рекурсивное построение иерархии даже тогда, когда связи заданы внутри одного набора данных. Однако, для произвольных структур, особенно с неизвестной глубиной, выделение иерархической части в отдельный набор данных значительно упрощает настройку и повышает читаемость схемы.
В зависимости от того, как формируется ваша иерархическая структура, мы можем выбрать один из двух основных типов наборов данных для ее описания:
Если иерархическая структура уже присутствует в вашей базе данных, например, в виде реквизитов документа (Документ.Ссылка и Документ.Родитель), в регистре сведений, или в табличной части, то наиболее удобным решением будет использование "Набора данных – запрос".
Когда использовать: Этот тип набора данных идеален, когда вы можете получить все связи "элемент-родитель" с помощью SQL-запроса к существующим данным.
Разберем по шагам создание такого набора:
Создайте новый набор данных в схеме компоновки данных. Выберите тип "Набор данных – запрос".
Напишите запрос, который выбирает два ключевых поля: поле, однозначно идентифицирующее элемент, и поле, ссылающееся на его родителя. Важно дать этим полям понятные имена, например, Ссылка и Родитель, или ДокументИД и РодительИД.
Посмотрим на пример запроса:
ВЫБРАТЬ
ДокументОбъект.Ссылка КАК СсылкаЭлемента,
ДокументОбъект.РодительДокумента КАК РодительЭлемента
ИЗ
Документ.МойТипДокумента КАК ДокументОбъект
ГДЕ
НЕ ДокументОбъект.ПометкаУдаления
В этом примере СсылкаЭлемента будет уникальным идентификатором, а РодительЭлемента — ссылкой на родительский документ. Убедитесь, что имена полей, используемых для связи между основным и иерархическим наборами данных, а также для рекурсивной связи внутри иерархического набора, совпадают по смыслу и типу.
В случае, если иерархия должна строиться только по "конечным" элементам, или только по "группам", необходимо добавить соответствующие условия в запрос. Например, если ДокументОбъект.РодительДокумента может быть пустым, это будет корневой элемент иерархии.
Если иерархия формируется динамически, программно, или на основе данных, которые нельзя получить прямым запросом к базе данных (например, из временной таблицы значений, сформированной на лету, или из сложных вычислений), то мы можем использовать "Набор данных – объект".
Когда использовать: Этот тип набора данных предпочтителен, когда вы управляете данными для иерархии программно, например, передавая в СКД готовую таблицу значений.
Проанализируем процесс создания:
Создайте новый набор данных в схеме компоновки данных. Выберите тип "Набор данных – объект".
На вкладке "Поля" вручную опишите поля, которые будут представлять элемент и его родителя. Обычно это Ссылка (или Идентификатор) и Родитель. Важно указать правильный тип данных для каждого поля (например, "Строка", "СсылкаНаДокумент", "УникальныйИдентификатор").
При формировании отчета программно передайте в СКД объект (например, ТаблицаЗначений), который будет служить источником данных для этого набора. Имена колонок в этой таблице значений должны строго соответствовать именам полей, описанных на вкладке "Поля" набора данных.
Пример программной передачи данных:
// ...
СхемаКомпоновкиДанных = ОтчетОбъект.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
// Формирование ТаблицыЗначений для иерархического набора
ТаблицаИерархии = Новый ТаблицаЗначений;
ТаблицаИерархии.Колонки.Добавить("СсылкаЭлемента", Новый ОписаниеТипов("ДокументСсылка.МойТипДокумента"));
ТаблицаИерархии.Колонки.Добавить("РодительЭлемента", Новый ОписаниеТипов("ДокументСсылка.МойТипДокумента"));
// ... Заполнение ТаблицаИерархии данными ...
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("НаборДанныхИерархия", ТаблицаИерархии);
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
// ...
Это самый важный шаг в создании произвольной иерархии. Правильная настройка связей позволяет СКД "понять", как элементы связаны друг с другом и как формировать древовидную структуру.
Разберем по шагам процесс настройки связей:
Связь основного набора данных с иерархическим набором:
Перейдите на вкладку "Связи наборов данных" в схеме компоновки данных.
Создайте новую связь.
В качестве "Набора-источника" укажите ваш основной набор данных (тот, который содержит детальные записи отчета).
В качестве "Набора-приемника" укажите ваш вспомогательный иерархический набор данных.
Настройте выражение источника и приемника. Например, если основной набор содержит поле Документ, а иерархический набор – поле СсылкаЭлемента, связь будет выглядеть как ОсновнойНабор.Документ = ИерархическийНабор.СсылкаЭлемента. Эта связь сообщает СКД, какие элементы из основного набора данных должны быть включены в иерархию.
Рекурсивная связь "сам с собой" для иерархического набора данных:
Создайте еще одну связь.
В качестве "Набора-источника" и "Набора-приемника" укажите один и тот же вспомогательный иерархический набор данных.
Настройте выражения следующим образом:
Выражение источника: Поле, которое представляет родителя текущего элемента (например, ИерархическийНабор.РодительЭлемента).
Выражение приемника: Поле, которое представляет сам элемент (его уникальный идентификатор) (например, ИерархическийНабор.СсылкаЭлемента).
Эта связь говорит СКД: "Чтобы найти родителей для элемента X, ищи их среди элементов, где их идентификатор равен родителю элемента X". СКД будет рекурсивно выполнять эту операцию, пока не дойдет до элементов без родителей (корневых элементов).
Важная настройка: В параметрах связи для поля, которое является приемником рекурсивной связи (в нашем примере ИерархическийНабор.СсылкаЭлемента), установите флаг "Параметр может принимать список значений". Это критически важно для корректного построения иерархии по множеству элементов и позволяет СКД эффективно работать с группами элементов на каждом уровне рекурсии.
Настройка полей в СКД:
На вкладке "Поля" для полей вашего иерархического набора (СсылкаЭлемента, РодительЭлемента) убедитесь, что у них установлен тип "Строка" или "СсылкаНаДокумент" в зависимости от того, что они хранят.
Для поля, которое является ссылкой на сам элемент (СсылкаЭлемента), в свойствах поля установите свойство "Использование" в "Иерархия" или "Группа". Это явно указывает СКД, что данное поле должно формировать иерархическую структуру.
При построении сложных иерархических отчетов могут возникать специфические ситуации, требующие дополнительной настройки или учета некоторых особенностей.
Иногда при построении произвольной иерархии возникает ситуация, когда родительский элемент выводится в отчете не только как группа (узел иерархии), но и как отдельная строка в своей же иерархической ветке. Это может привести к дублированию информации и затруднить восприятие отчета.
Причины дублирования: Обычно это происходит, когда в основном наборе данных присутствует строка, соответствующая элементу, который также является родителем для других элементов в иерархии.
Методы устранения дубликатов:
Исключение элементов из основного запроса: Если нам не нужно выводить родительские элементы как отдельные строки, а только как узлы иерархии, можно модифицировать запрос основного набора данных, исключив из него элементы, которые являются родителями для других. Это может быть сложно, так как требует проверки существования потомков.
Настройка макетов иерархии: В СКД есть возможность более тонкой настройки вывода иерархии. Можно создать собственный макет для группировки, где родительские элементы будут отображаться только как заголовки групп, без вывода дополнительных детальных строк.
Использование выражений для скрытия: Можно добавить условное форматирование или использовать выражения для скрытия строк, если они совпадают с текущей группировкой.
Дополнительное поле в запросе: Можно добавить в запрос иерархического набора данных поле-признак, например, ЭтоГруппа, и использовать его в настройках СКД для управления видимостью или типом вывода.
Построение сложной или глубокой иерархии может быть ресурсоемкой операцией. Важно учитывать производительность для обеспечения быстрого формирования отчетов.
Разберем факторы, влияющие на производительность:
Индексы в базе данных: Если поля, используемые для связей иерархии (СсылкаЭлемента и РодительЭлемента), не имеют индексов в базе данных, это может привести к значительному снижению скорости выполнения запросов и построению иерархии. Убедитесь, что эти поля проиндексированы.
Количество уровней и элементов: Чем больше уровней вложенности и чем больше элементов в иерархии, тем дольше будет формироваться отчет. Продумайте, нужна ли вам вся глубина иерархии или можно ограничиться несколькими уровнями.
Критерии отбора: Применяйте максимально точные критерии отбора на самых ранних этапах формирования данных (в запросах наборов данных). Отбор большого массива данных, а затем его фильтрация в СКД, менее эффективен, чем отбор уже отфильтрованных данных.
Оптимизация запросов: Убедитесь, что запросы, используемые в наборах данных, являются оптимальными и не содержат "тяжелых" операций или полных сканирований таблиц.
Помимо стандартной иерархии "сверху вниз" (когда мы видим корневой элемент и его потомков), СКД также позволяет строить иерархию "наоборот", то есть для заданного элемента показывать всех его предков до самого корневого элемента. Это особенно полезно, когда нам нужно быстро увидеть полную цепочку принадлежности для конкретного элемента.
Как достигается иерархия "снизу вверх":
Для построения иерархии "снизу вверх" необходимо изменить выражения источника и приемника в рекурсивной связи "сам с собой" для иерархического набора данных:
Исходное поле (для "сверху вниз"): ИерархическийНабор.РодительЭлемента (Источник) связывается с ИерархическийНабор.СсылкаЭлемента (Приемник).
Для "снизу вверх": Мы меняем их местами. Теперь Выражение источника будет ИерархическийНабор.СсылкаЭлемента, а Выражение приемника – ИерархическийНабор.РодительЭлемента.
Эта связь будет означать: "Чтобы найти предков элемента X, ищи их среди элементов, где их идентификатор равен родителю элемента X". В этом случае мы начинаем с конкретного элемента и "поднимаемся" по иерархии к его родителям.
Таким образом, мы видим, что СКД предоставляет мощные и гибкие инструменты для построения иерархий любой сложности, даже если они не соответствуют стандартным справочным структурам. Главное — правильно настроить наборы данных и, что самое важное, их связи, чтобы система могла эффективно "раскручивать" и отображать требуемую вложенность.