Как найти элемент справочника 1С по полному коду?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции данных и пакетной обработке часто возникает задача поиска элемента справочника по его полному иерархическому коду вида КодГруппы1/КодГруппы2/КодЭлемента. Начинающие разработчики часто пытаются напрямую обратиться к несуществующему полю в запросе (например, Справочник.ПолныйКод), что приводит к синтаксической ошибке СУБД. Разберем особенности хранения иерархии в платформе 1С и рассмотрим все способы решения этой задачи — от штатных платформенных методов до составления оптимизированных запросов.

Почему полный код недоступен в языке запросов напрямую

В физических таблицах базы данных 1С иерархия организуется через ссылки на родителей: в каждой строке хранится только собственный Код и ссылка Родитель (поле ParentIDRRef). Реквизита «Полный код» в базе данных нет — платформа формирует его динамически на клиенте или сервере при обращении к соответствующему свойству объекта или ссылки.

Кроме того, потребность в поиске по всей цепочке кодов возникает преимущественно тогда, когда в конфигураторе для справочника задана настройка «Серии кодов: В пределах подчинения» (или «В пределах подчинения владельцу»). Если установлено свойство «Во всем справочнике», код уникален сам по себе, и поиск родительских цепочек не требуется — достаточно искать просто по коду элемента.

Способ 1. Использование штатного метода «НайтиПоКоду»

Если поиск выполняется для единичного элемента в программном коде, нет необходимости составлять сложные запросы. В платформе 1С метод менеджера справочника НайтиПоКоду уже содержит встроенный механизм поиска по полному коду.

Синтаксис метода:

Справочники.<ИмяСправочника>.НайтиПоКоду(Код, ПоПолномуКоду, Владелец, На стыке)

Рассмотрим пример использования штатного метода:


// В качестве разделителя уровней иерархии обязательно используется прямой слэш "/"
ПолныйИскомыйКод = "00001/00042/00105";

НайденнаяСсылка = Справочники.Номенклатура.НайтиПоКоду(ПолныйИскомыйКод, Истина);

Если ЗначениеЗаполнено(НайденнаяСсылка) Тогда
    // Элемент успешно найден
    Сообщить("Найден элемент: " + Строка(НайденнаяСсылка));
Иначе
    Сообщить("Элемент с полным кодом " + ПолныйИскомыйКод + " не найден!");
КонецЕсли;

Важные нюансы метода:

  1. Разделителем уровней должен быть исключительно прямой слэш (/). Если во входных данных используется обратный слэш (\), его необходимо предварительно заменить функцией СтрЗаменить.
  2. Каждый сегмент кода должен строго соответствовать заданной в метаданных длине кода (включая лидирующие нули при строковом коде с автонумерацией).

Способ 2. Поиск по цепочке родителей через встроенный язык

Если разделители кодов отличаются от стандартных или требуется дополнительная валидация на каждом уровне иерархии, можно разбить строку на массив и выполнить последовательный поиск от корня к конечному элементу:


&НаСервере
Функция НайтиЭлементПоПолномуКоду(Знач ПолныйКод, Знач Разделитель = "\") Экспорт

    МассивКодов = СтрРазделить(ПолныйКод, Разделитель, Ложь);
    ТекущийРодитель = Справочники.Номенклатура.ПустаяСсылка();
    ТекущийЭлемент = ТекущийРодитель;

    Для Каждого СтрКод Из МассивКодов Цикл
        КодСегмента = СокрЛП(СтрКод);
        ТекущийЭлемент = Справочники.Номенклатура.НайтиПоКоду(КодСегмента, Ложь, , ТекущийРодитель);
        
        Если НЕ ЗначениеЗаполнено(ТекущийЭлемент) Тогда
            // На одном из уровней элемент или группа не найдены
            Возврат Справочники.Номенклатура.ПустаяСсылка();
        КонецЕсли;
        
        ТекущийРодитель = ТекущийЭлемент;
    КонецЦикла;

    Возврат ТекущийЭлемент;

КонецФункции

Способ 3. Динамическое построение текста запроса

Когда требуется выполнить поиск через объект Запрос (например, внутри универсальных алгоритмов выгрузки/загрузки), можно динамически сформировать текст запроса с условиями по полям Родитель:


Функция НайтиЭлементЗапросомПоПолномуКоду(ИмяСправочника, ПолныйКод) Экспорт

    МассивКодов = СтрРазделить(ПолныйКод, "/", Ложь);
    ВсегоСегментов = МассивКодов.Количество();
    
    Если ВсегоСегментов = 0 Тогда
        Возврат Неопределено;
    КонецЕсли;

    Запрос = Новый Запрос;
    ТекстУсловий = "";
    
    // Перебираем сегменты справа налево (от самого элемента к верхним родителям)
    Для Сч = 0 По ВсегоСегментов - 1 Цикл
        
        ИндексСправа = ВсегоСегментов - 1 - Сч;
        ИмяПараметра = "КодУровня_" + Строка(Сч);
        
        ПутьКПолю = "Справочник";
        Для Уровень = 1 По Сч Цикл
            ПутьКПолю = ПутьКПолю + ".Родитель";
        КонецЦикла;
        
        Если Сч > 0 Тогда
            ТекстУсловий = ТекстУсловий + " И " + Символы.ПС;
        КонецЕсли;
        
        ТекстУсловий = ТекстУсловий + ПутьКПолю + ".Код = &" + ИмяПараметра;
        Запрос.УстановитьПараметр(ИмяПараметра, СокрЛП(МассивКодов[ИндексСправа]));
        
    КонецЦикла;

    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Справочник.Ссылка КАК Ссылка
        |ИЗ
        |    Справочник." + ИмяСправочника + " КАК Справочник
        |ГДЕ
        |    " + ТекстУсловий;

    РезультатЗапроса = Запрос.Выполнить();
    Если РезультатЗапроса.Пустой() Тогда
        Возврат Неопределено;
    КонецЕсли;

    Выборка = РезультатЗапроса.Выбрать();
    Выборка.Следующий();
    Возврат Выборка.Ссылка;

КонецФункции

Внимание к производительности: разыменование полей через точку (Справочник.Родитель.Родитель...) транслируется СУБД в цепочку внешних соединений (LEFT JOIN). Для справочников с большой глубиной вложенности такой запрос создает дополнительную нагрузку на сервер СУБД.

Способ 4. Архитектурная оптимизация для массовой обработки

Если в системе регулярно сопоставляются таблицы значений с тысячами строк по полным кодам, вызов НайтиПоКоду или динамических запросов в цикле приведет к проблеме N+1 запросов и сильному замедлению работы. В таких сценариях рекомендуются следующие подходы:

  1. Добавление индексированного реквизита: добавить в справочник реквизит ПолныйКод (с признаком «Индексировать») и автоматически заполнять его в событии ПередЗаписью модуля объекта. В этом случае поиск по таблице в запросе превращается в быстрое соединение по индексу.
  2. Использование регистра сведений: создать непериодический независимый регистр сведений с измерениями ПолныйКод и Ссылка, обновляемый при записи справочника через подписку на событие.
  3. Сопоставление через временные таблицы: разобрать входящие полные коды в таблицу значений по колонкам фиксированных уровней (КодУровня1, КодУровня2, КодЭлемента), поместить её во временную таблицу и выполнить одно пакетное соединение с таблицей справочника по всем уровням сразу.
← На главную