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