В практике разработчика 1С иногда возникают «спортивные» задачи или требования на собеседованиях, когда необходимо построить отчет по иерархическому справочнику произвольной вложенности, используя исключительно Консоль запросов. Сложность заключается в том, что язык запросов 1С не поддерживает рекурсивные вызовы (конструкции типа WITH RECURSIVE в SQL), а стандартный механизм СКД, который легко справляется с иерархией, по условию задачи использовать нельзя.
В данной статье мы разберем, как обойти эти ограничения, проанализируем математические подходы к имитации рекурсии и построим алгоритм, позволяющий «развернуть» дерево любой глубины в плоскую таблицу одним пакетным запросом.
Основная проблема кроется в архитектуре платформенного транслятора. Когда мы пишем запрос в 1С, он переводится в нативный SQL конкретной СУБД (MS SQL Server, PostgreSQL и т.д.). Несмотря на то, что современные СУБД поддерживают рекурсию, 1С транслирует запросы в «плоский» вид. Оператор В ИЕРАРХИИ отлично работает в секции ГДЕ, когда мы передаем конкретный массив родительских узлов, но он не может быть использован внутри соединения JOIN для динамического связывания предков и потомков.
Рассмотрим ситуацию: у нас есть справочник Подразделения с неограниченным уровнем вложенности. Нам нужно получить данные из регистра, привязанные к нижним узлам, но сгруппировать их по всем вышестоящим родителям. Если уровень вложенности равен 2 или 3, мы можем просто написать несколько левых соединений. Но что делать, если уровней 10, 20 или 99?
Проанализируем наиболее эффективный способ решения — алгоритм бинарного удвоения. Суть метода заключается в том, что каждое последующее соединение таблиц позволяет нам увеличить глубину просматриваемой иерархии в геометрической прогрессии. Если в первом запросе мы соединяем элемент с его непосредственным кратким родителем (1 уровень), то во втором шаге мы можем соединить результат с самим собой, получив уже 2 уровня, затем 4, 8, 16 и так далее.
Разберем по шагам, как это реализовать в пакете запросов:
Т1.Родитель = Т2.Ссылка.Пример структуры такого запроса для первых уровней может выглядеть так:
// Шаг 1: Получаем прямые связи
ВЫБРАТЬ
Подразделения.Ссылка КАК Элемент,
Подразделения.Родитель КАК Родитель
ПОМЕСТИТЬ ТЗ_Связи_L1
ИЗ
Справочник.Подразделения КАК Подразделения;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Удваиваем глубину (до 2-х уровней)
ВЫБРАТЬ
T1.Элемент КАК Элемент,
ЕСТЬNULL(T2.Родитель, T1.Родитель) КАК Родитель
ПОМЕСТИТЬ ТЗ_Связи_L2
ИЗ
ТЗ_Связи_L1 КАК T1
ЛЕВОЕ СОЕДИНЕНИЕ ТЗ_Связи_L1 КАК T2
ПО T1.Родитель = T2.Элемент;
Продолжая эту логику, в таблице ТЗ_Связи_L3 мы получим связи через 4 колена, в L4 — через 8 и так далее. Это позволяет нам «пробить» любую глубину вложенности, не зная её заранее.
Для окончательного решения задачи нам нужно построить таблицу транзитивного замыкания. Это такая таблица, где для каждого элемента указаны все его предки, а не только один самый верхний. Рассмотрим, как применить это для отчета:
Подразделение (из регистра) = Элемент (из нашей иерархической таблицы).Родитель из иерархической таблицы и суммируем ресурсы регистра.Родитель.Таким образом, сумма, относящаяся к самому глубокому подразделению, «поднимется» ко всем его родителям по цепочке связей, сформированной в пакете запросов. Для контроля точности итоговых сумм и поиска расхождений в учете применяется автоматическое сравнение бухгалтерских и налоговых проводок между информационными базами.
Проанализируем технические ограничения, о которых стоит помнить:
Иерархия элементов vs Иерархия групп: Если в справочнике используется иерархия элементов (как в случае с подразделениями), количество записей во временных таблицах будет равно общему количеству элементов. Это может создать нагрузку на tempdb СУБД. Рекомендуется выбирать только те поля, которые необходимы для связи (Ссылка и Родитель), используя тип УникальныйИдентификатор или ссылку.
Ограничение В ИЕРАРХИИ: Помним, что в консоли запросов мы не можем написать ГДЕ Таблица1.Ссылка В ИЕРАРХИИ (Таблица2.Ссылка). Это фундаментальное ограничение. Именно поэтому метод бинарного удвоения через ЛЕВОЕ СОЕДИНЕНИЕ является единственным рабочим способом «честного» решения задачи без использования программного кода.
Если мы будем просто соединять таблицу 100 раз подряд (L1 с L2, L2 с L3...), запрос получится избыточным и крайне медленным. Метод бинарного возведения (L1+L1=L2, L2+L2=L4) работает за логарифмическое время относительно глубины иерархии. Это позволяет сохранять производительность даже на больших справочниках, особенно если настроено автоматическое обслуживание и тестирование баз 1С в тихом режиме.
Подведем итог: Решение задачи «чистым запросом» возможно. Оно требует понимания механизмов работы временных таблиц и основ дискретной математики. Используя пакетный запрос с последовательным удвоением связей, мы можем имитировать рекурсию и строить отчеты по иерархии любой сложности, доказывая коллегам, что для опытного разработчика 1С нет ничего невозможного.