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