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

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

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

При стандартной настройке иерархической группировки в СКД возникают две проблемы:

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

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

Почему СКД агрегирует ресурсы и дублирует элементы

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

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

Вариант 1. Использование функции ВычислитьВыражение на закладке «Ресурсы»

Этот способ является наиболее универсальным и быстрым, так как не перегружает СУБД дополнительными запросами и не требует сложного изменения структуры набора данных.

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

  1. В схеме компоновки данных формируем стандартный запрос, получающий номенклатуру, ее родителя и ресурс:
    
    ВЫБРАТЬ
        НоменклатураСпр.Ссылка КАК Номенклатура,
        НоменклатураСпр.Родитель КАК Родитель,
        Цены.Цена КАК Цена
    ИЗ
        Справочник.Номенклатура КАК НоменклатураСпр
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены КАК Цены
            ПО Цены.Номенклатура = НоменклатураСпр.Ссылка
    
  2. Переходим на закладку «Ресурсы». Добавляем поле Цена дважды с раздельными условиями расчета:

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

    ЕстьNULL(Цена, 0)

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

    ВычислитьВыражение("Цена", "Номенклатура", "Группировка")
  3. В настройках отчета на закладке «Настройки» добавляем группировку Номенклатура Иерархия. Чтобы исключить повторный вывод родителя внутри его собственной ветки, в детальные записи выводим только те элементы, у которых нет собственных подчиненных, либо накладываем системный отбор на состав записей.

Вариант 2. Настройка рекурсивной связи наборов данных

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

Рассмотрим алгоритм настройки связей:

  1. В наборе данных формируем запрос, объединяющий элементы и их родителей:
    
    ВЫБРАТЬ
        НоменклатураСпр.Ссылка КАК Номенклатура,
        НоменклатураСпр.Родитель КАК Родитель,
        ЕСТЬNULL(Цены.Цена, 0) КАК Цена
    ИЗ
        Справочник.Номенклатура КАК НоменклатураСпр
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены КАК Цены
            ПО Цены.Номенклатура = НоменклатураСпр.Ссылка
    
  2. Переходим на закладку «Связи наборов данных» и добавляем связь набора данных (например, НаборДанных1) с самим собой:
    • Источник связи: НаборДанных1
    • Приемник связи: НаборДанных1
    • Выражение источника: Номенклатура
    • Выражение приемника: Родитель
    • Начальное значение связи: ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
    • Обязательная связь: устанавливаем флаг.
  3. На закладке «Ресурсы» указываем расчет поля Цена по полям Номенклатура и Номенклатура Иерархия.
  4. В структуре отчета выводим только Детальные записи. СКД сама развернет дерево иерархии согласно настроенным связям.

Важное ограничение по производительности: данный метод отлично подходит для небольших справочников и тестовых задач. Однако на больших объемах данных (от десятков тысяч строк) связь набора с самим собой генерирует множество мелких рекурсивных SQL-запросов к СУБД, что может привести к существенному замедлению формирования отчета.

Вариант 3. Подготовка уровней иерархии в тексте запроса

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

Проанализируем этот подход:

  1. В запросе через левые соединения таблицы номенклатуры с вышестоящими родителями формируются поля для конкретных уровней (например, ЦенаУровень1, ЦенаУровень2).
  2. На закладке «Ресурсы» рассчитываем итоговое значение с проверкой текущего уровня группировки:
    
    Выбор
        Когда Уровень() = 1 Тогда ЕстьNULL(ЦенаУровень1, 0)
        Когда Уровень() = 2 Тогда ЕстьNULL(ЦенаУровень2, 0)
        Иначе ЕстьNULL(Цена, 0)
    Конец
    
  3. Исключение дублей родительских строк реализуется фильтрацией на уровне запроса через проверку подчиненности (например, ГДЕ Ссылка НЕ В (ВЫБРАТЬ РАЗЛИЧНЫЕ Родитель ИЗ Справочник.Номенклатура) для выборки только листовых элементов).

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

← На главную