При работе с системой компоновки данных (СКД) часто возникает задача построения рекурсивных отчетов, например, для разузлования спецификаций продукции, когда набор данных связывается сам с собой. Стандартный реквизит Уровень в СКД корректно работает только при выводе данных в виде дерева. Однако, если в данных присутствуют зацикливания или бизнес-логика требует вывода в виде плоской таблицы, этот реквизит всегда возвращает "1". Рассмотрим, как решить эту проблему и корректно рассчитать уровень вложенности на каждом этапе рекурсии.
Проанализируем самый эффективный способ получения уровня иерархии непосредственно в механизмах СКД без изменения структуры запроса. Этот метод основан на передаче текущего значения уровня в следующий шаг рекурсии через параметры связи.
Разберем алгоритм по шагам:
П_Уровень.П_Уровень.Уровень + 1.Таким образом, на каждой итерации СКД будет брать текущее значение поля Уровень из родительской записи, прибавлять к нему единицу и передавать это значение в запрос для получения дочерних записей. В самом тексте запроса этот параметр можно выбрать как обычное поле. Это значение будет корректно отображаться в плоской таблице, так как оно рассчитывается логически, а не визуально.
Выясним причину, по которой рекурсия может стать бесконечной. В производстве это часто случается при использовании аналогов: когда материал А входит в состав полуфабриката Б, а полуфабрикат Б может быть заменен на материал А. Чтобы отчет не «завис», необходимо программно или запросно ограничивать глубину рекурсии.
Посмотрим на пример реализации контроля зацикливания через формирование пути. Мы можем в каждом шаге рекурсии собирать строковое поле, содержащее идентификаторы всех «родителей»:
// Пример логики формирования пути в запросе
ВЫБРАТЬ
Номенклатура,
Количество,
&ПутьРодителя + "/" + ПРЕДСТАВЛЕНИЕ(Номенклатура) КАК Путь
ИЗ
Справочник.Спецификации.Состав
ГДЕ
НЕ &ПутьРодителя ПОДОБНО "%" + ПРЕДСТАВЛЕНИЕ(Номенклатура) + "%"
Проанализируем эту ситуацию: если наименование текущей номенклатуры уже встречается в строке пути, значит, мы попали в цикл. Условие ГДЕ отсечет такие записи, предотвращая бесконечную выборку. Это гораздо надежнее, чем просто ограничивать количество итераций (например, до 400), так как позволяет точно видеть проблемную ветку спецификации.
Рассмотрим более продвинутый математический подход, который упоминался как решение через СЛАУ (Систему линейных алгебраических уравнений). Этот метод особенно актуален для пищевой промышленности (например, мясопереработки), где количество связей и аналогов огромно.
Суть метода заключается в представлении состава изделия в виде матрицы прямых затрат. Если у нас есть 100 видов номенклатуры, мы создаем матрицу 100х100, где в ячейках указано количество вхождения одного элемента в другой. Решение такой системы позволяет получить полные затраты материалов на единицу продукции за один проход, полностью избегая рекурсивных запросов ВЫБРАТЬ внутри циклов. В 1С это реализуется через временные таблицы и итерационный пересчет коэффициентов вхождения в пакетном запросе.
Если отчет должен работать очень быстро у оператора на производстве, выполнение сложных рекурсивных расчетов «на лету» может быть неприемлемым. Рассмотрим вариант с предварительным расчетом состава.
Создадим специализированный регистр сведений «Состав изделия по модификациям». При записи или утверждении спецификации будем программно разузловывать её и записывать в регистр уже готовую «плоскую» структуру со следующими реквизитами:
В этом случае отчет в СКД превращается в простейшую выборку из одного регистра. Это гарантирует мгновенную работу отчета и позволяет легко выводить Уровень, так как он уже физически записан в базе данных. Проблема зацикливания здесь решается один раз в момент записи спецификации программным кодом, который легче отладить, чем сложный запрос.
Проанализируем ситуацию с аналогами. Когда у каждого компонента есть 3-5 аналогов, количество веток дерева растет в геометрической прогрессии. Чтобы упростить задачу, можно использовать метод спецификаций-шаблонов:
Таким образом, мы не множим ветки иерархии в процессе рекурсии, а накладываем возможные замены уже на финальный результат или на конкретные уровни. Это значительно снижает нагрузку на сервер 1С и делает отчет читаемым.
Важный момент: если вы решите использовать вычисляемые поля СКД для расчета уровня, помните, что обращение к функциям общих модулей из СКД замедляет выполнение отчета, так как функция будет вызываться для каждой строки результата. Использование параметров связи (Метод 1) является наиболее «нативным» и быстрым способом для платформы 1С:Предприятие 8.3.