При работе с иерархическими справочниками в 1С, такими как ДоговорыКонтрагентов, Номенклатура или Подразделения, часто возникает необходимость найти «корень» иерархии — то есть родителя самого верхнего уровня для конкретного элемента. Сложность заключается в том, что уровень вложенности может быть разным, а стандартный язык запросов 1С не поддерживает рекурсию в чистом виде. В рамках этой статьи мы разберем несколько способов решения этой задачи, начиная от простых конструкций для консоли и заканчивая производительными методами для больших объемов данных.
Типичная ошибка начинающих разработчиков при поиске корня — использование конструкции ЕСТЬ NULL. Важно понимать, что в системе 1С:Предприятие поле Родитель у элементов справочника никогда не содержит значение NULL. Если элемент находится в корне справочника, его родителем является пустая ссылка соответствующего типа.
Проанализируем ситуацию: когда мы пишем запрос и пытаемся проверить ДоговорыКонтрагентов.Родитель ЕСТЬ NULL, условие всегда будет ложным, и мы получим пустой результат или некорректные данные. Правильный подход — сравнение со значением ЗНАЧЕНИЕ(Справочник.ИмяСправочника.ПустаяСсылка).
Если нам заранее известно максимальное количество уровней вложенности (например, не более 5), самым быстрым способом решения задачи в консоли запросов будет использование конструкции ВЫБОР КОГДА... ТОГДА. Рассмотрим этот метод по шагам.
Суть метода заключается в последовательной проверке поля Родитель на каждом уровне. Мы «поднимаемся» вверх, пока не встретим пустую ссылку.
ВЫБРАТЬ
Договоры.Ссылка КАК ИсходныйЭлемент,
ВЫБОР
КОГДА Договоры.Родитель = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
ТОГДА Договоры.Ссылка
КОГДА Договоры.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
ТОГДА Договоры.Родитель
КОГДА Договоры.Родитель.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
ТОГДА Договоры.Родитель.Родитель
КОГДА Договоры.Родитель.Родитель.Родитель.Родитель = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка)
ТОГДА Договоры.Родитель.Родитель.Родитель
ИНАЧЕ Договоры.Родитель.Родитель.Родитель.Родитель
КОНЕЦ КАК РодительПервогоУровня
ИЗ
Справочник.ДоговорыКонтрагентов КАК Договоры
ГДЕ
Договоры.Ссылка В (&СписокСсылок)
Преимущества этого метода:
Недостатки:
LEFT JOIN), которые СУБД генерирует для каждого обращения через точку.Если мы хотим сделать запрос более структурированным и избежать длинных цепочек «через точку», мы можем использовать временные таблицы. Это особенно полезно, когда нужно получить не только корень, но и промежуточные данные.
Разберем логику: мы создаем временную таблицу, куда помещаем ссылку и её непосредственного родителя. Затем в следующей временной таблице мы соединяем первую таблицу саму с собой, заменяя родителя на родителя следующего уровня, если тот не пустой.
// Шаг 1: Получаем исходные данные
ВЫБРАТЬ
Договоры.Ссылка КАК Ссылка,
Договоры.Родитель КАК ТекущийРодитель
ПОМЕСТИТЬ ВТ_Уровень1
ИЗ
Справочник.ДоговорыКонтрагентов КАК Договоры
ГДЕ
Договоры.Ссылка В (&СписокСсылок);
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Поднимаемся на уровень выше
ВЫБРАТЬ
ВТ.Ссылка КАК Ссылка,
ВЫБОР
КОГДА Родители.Родитель ЕСТЬ NULL ТОГДА ВТ.ТекущийРодитель
КОГДА Родители.Родитель = ЗНАЧЕНИЕ(Справочник.ДоговорыКонтрагентов.ПустаяСсылка) ТОГДА ВТ.ТекущийРодитель
ИНАЧЕ Родители.Родитель
КОНЕЦ КАК ТекущийРодитель
ПОМЕСТИТЬ ВТ_Уровень2
ИЗ
ВТ_Уровень1 КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ДоговорыКонтрагентов КАК Родители
ПО ВТ.ТекущийРодитель = Родители.Ссылка;
// И так далее для нужного количества уровней...
Этот метод позволяет контролировать процесс на каждом этапе, но он так же ограничен конечным числом шагов, прописанных в запросе.
Для случаев, когда глубина иерархии неизвестна или очень велика, а запрос должен работать максимально быстро, профессионалы используют метод транзитивного замыкания. Рассмотрим, в чем его суть.
В иерархических структурах 1С штатный поиск «В ИЕРАРХИИ» работает достаточно эффективно, но он направлен «вниз». Чтобы эффективно искать «вверх», иногда создают вспомогательные таблицы (или регистры сведения), где хранятся пары Элемент — Все его предки. Если такого регистра в конфигурации нет, построить его «на лету» в консоли запросов сложно, однако это стандарт индустрии для высоконагруженных систем.
Если же стоит задача найти корень один раз, можно использовать следующий алгоритм в консоли с постобработкой:
В ИЕРАРХИИ).Родитель = ПустаяСсылка.Проанализируем возможности современных инструментов. Если вы используете консоль запросов, встроенную в «Инструменты разработчика» или консоль на базе Системы компоновки данных (СКД), задача упрощается. В СКД можно настроить связь набора данных с самим собой по полю Родитель и Ссылка.
Выясним, как это помогает: СКД умеет автоматически вычислять иерархию и предоставлять функции для работы с ней. Например, можно использовать вычисляемые поля, которые будут обращаться к вышестоящим уровням. Однако для простой выборки в консоли запросов метод из Решения 1 остается самым популярным ввиду своей лаконичности.
При написании запросов к иерархии следует помнить о следующих моментах:
Родитель в 1С индексируется по умолчанию, поэтому поиск по нему работает быстро.Родитель.Родитель — это новое соединение таблиц в SQL-запросе. Цепочка из 10 таких обращений может существенно замедлить выполнение запроса на базе данных с миллионами записей.ВЫБОР могут привести к ошибке переполнения стека парсера SQL-запроса (хотя в 1С это встречается редко, так как платформа сама ограничивает вложенность).Подводя итог, можно сказать, что для оперативной работы в консоли запросов использование конструкции ВЫБОР с проверкой на ЗНАЧЕНИЕ(...ПустаяСсылка) является оптимальным балансом между сложностью написания и результатом.