Получение данных справочника в 1С: от НайтиПоКоду до запроса

· 8 мин

Содержание

Чтобы получить значение из справочника 1С, найдите элемент и прочитайте его реквизит. Для поиска служат методы менеджера: Справочники.Номенклатура.НайтиПоКоду("00-000123") или НайтиПоНаименованию("Стол офисный", Истина). Они возвращают ссылку, а нужное поле берётся через точку: Ссылка.Артикул.

Это быстрый путь для одного элемента. Если элементов много или условие сложное, нужен запрос с выборкой. Ниже все способы по порядку: от простого к тяжёлому, с кодом и подводными камнями.

Выбор способа: какой метод взять

Сначала определитесь, сколько элементов нужно и что вы про них знаете.

Что естьЧто нужноСпособ
Код элементаодин элементНайтиПоКоду
Точное наименованиеодин элементНайтиПоНаименованию
Значение любого реквизитаодин элементНайтиПоРеквизиту
Уже есть ссылкаодин реквизитобращение через точку
Условие по нескольким полямсписок элементовзапрос + выборка
Предопределённый элементссылка на негоСправочники.Имя.ИмяЭлемента

Правило для практики: один элемент по известному ключу — методы менеджера. Всё, что похоже на «выбрать все, где…», — запрос.

Поиск одного элемента по коду, наименованию и реквизиту

Менеджер справочника умеет искать сам. Каждый метод возвращает ссылку на найденный элемент. Если ничего нет, вернётся пустая ссылка.

По коду

Ссылка = Справочники.Контрагенты.НайтиПоКоду("000000145");
Если Не Ссылка.Пустая() Тогда
    ИНН = Ссылка.ИНН;
КонецЕсли;

Код в справочнике может быть строкой или числом, это зависит от настроек вида справочника. Строковый код с ведущими нулями передавайте строкой целиком: "000000145", а не 145.

У метода есть второй параметр, ПоискПоПолномуКоду. Он нужен для иерархических справочников с кодом, который уникален только внутри родителя.

По наименованию

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

Второй параметр Истина включает точное соответствие. Без него метод ищет по началу строки и вернёт первый подходящий элемент. Для надёжной логики всегда ставьте Истина.

Наименование в большинстве справочников не уникально. Метод вернёт один элемент из нескольких одноимённых, и какой именно, заранее неизвестно. Для ключевых данных так искать нельзя.

По произвольному реквизиту

Ссылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", "СТ-2040");

Метод берёт имя реквизита и искомое значение. Работает быстро, если по реквизиту настроен индекс. Если реквизит не индексируется, на большом справочнике поиск будет заметно медленнее.

Поиск внутри группы или по владельцу

Для иерархии и подчинённых справочников добавляйте параметры родителя и владельца. Так вы исключите совпадения из чужих веток:

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

Здесь последний параметр — владелец. Пропущенный параметр оставляйте пустым через запятую.

Чтение реквизитов через ссылку

Ссылка на элемент — это не копия данных, а указатель. Любой реквизит читается через точку:

Наименование = Ссылка.Наименование;
Артикул      = Ссылка.Артикул;
ЕдиницаИзм   = Ссылка.ЕдиницаИзмерения.Наименование;

Цепочка через точку проходит по связанным справочникам. Платформа сама подтянет данные из базы.

Есть цена вопроса. Каждое обращение через точку к ссылке — отдельное чтение объекта из базы, и в цикле на тысячи элементов это даёт ощутимую задержку. Если нужно прочитать много полей у многих элементов, берите запрос.

Один реквизит без загрузки всего объекта

В типовых конфигурациях на базе библиотеки стандартных подсистем есть готовая функция:

Артикул = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Артикул");

Она читает только указанное поле и не тянет объект целиком. Если у вас нестандартная конфигурация без этой библиотеки, функции не будет. Тогда пишите небольшой запрос сами.

Предопределённые элементы

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

Ссылка = Справочники.ВидыКонтактнойИнформации.ИмяПредопределенного;

Имя берётся из конфигуратора, из списка предопределённых данных справочника. Это самый быстрый и надёжный способ: он не зависит ни от кода, ни от наименования, которые пользователь мог изменить.

Запрос к справочнику: сложная фильтрация

Когда условий несколько или результат — это список, нужен запрос. Он работает на уровне СУБД и справляется с тысячами строк.

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    Н.Ссылка КАК Ссылка,
    |    Н.Наименование КАК Наименование,
    |    Н.Артикул КАК Артикул
    |ИЗ
    |    Справочник.Номенклатура КАК Н
    |ГДЕ
    |    Н.Артикул ПОДОБНО &Маска
    |    И НЕ Н.ПометкаУдаления";
Запрос.УстановитьПараметр("Маска", "СТ-%");
Результат = Запрос.Выполнить();

Три момента, на которых чаще всего спотыкаются:

  • 🔹 значения подставляйте параметрами (&Маска), а не склеивайте в строку запроса;
  • 🔹 условие НЕ Н.ПометкаУдаления отсекает удалённые элементы, если они не нужны;
  • 🔹 для одного элемента добавьте ПЕРВЫЕ 1, чтобы база не искала дальше.

Получить значение одного поля из запроса

Если запрос вернёт ровно одну строку, её удобно прочитать так:

Выборка = Результат.Выбрать();
Если Выборка.Следующий() Тогда
    Артикул = Выборка.Артикул;
КонецЕсли;

Выгрузка в таблицу значений подойдёт, когда данные нужны целиком и с ними будут работать дальше:

Таблица = Результат.Выгрузить();

Объект Выборка: обход результата

Выбрать() возвращает курсор. Он стоит перед первой строкой, поэтому без Следующий() читать нечего.

Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Наименование + " — " + Выборка.Артикул);
КонецЦикла;

Сравним два способа получить данные из результата запроса:

ВыборкаВыгрузка в таблицу
Памятьчитает по строкедержит всё в памяти
Скорость обходабыстраябыстрая, но с затратой на выгрузку
Когда братьодин проход по даннымнужны сортировка, поиск, несколько проходов

Для справочника в сотни тысяч элементов выбирайте выборку. Таблицу значений грузите, только если без неё не обойтись.

Если в запросе есть иерархия, у Выбрать() есть параметр обхода: Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам). Для простого справочника без группировок он не нужен.

Ссылки и уникальные идентификаторы

Ссылка однозначно указывает на элемент внутри информационной базы. Но ссылку нельзя просто «унести» в другую базу: там у того же элемента будет другая ссылка. Для обмена между базами используют идентификатор.

Идентификатор = Ссылка.УникальныйИдентификатор();

И обратная операция, из идентификатора в ссылку:

Ссылка = Справочники.Контрагенты.ПолучитьСсылку(Идентификатор);

Нюанс: ПолучитьСсылку всегда вернёт ссылку, даже если элемента с таким идентификатором в базе нет. Проверяйте результат:

Объект = Ссылка.ПолучитьОбъект();
Если Объект = Неопределено Тогда
    // элемента в базе нет
КонецЕсли;

Не сохраняйте внутреннее строковое представление ссылки для долгого хранения или передачи в другую базу. Для этого есть уникальный идентификатор или естественный ключ вроде ИНН и КПП.

Пустая ссылка и проверка

Не нашлось — значит, пустая ссылка. Проверять нужно именно её:

Если Ссылка.Пустая() Тогда
    // не нашли
КонецЕсли;

Сравнение с Неопределено для результата НайтиПоКоду ничего не даст: метод возвращает пустую ссылку нужного типа, а не Неопределено.

Работа в управляемой форме: клиент и сервер

В управляемой форме код разделён между клиентом и сервером. Справочники.Имя — это серверный объект, поэтому на клиенте его просто нет. Вызов вида Справочники.Номенклатура.НайтиПоКоду(...) из клиентской процедуры вызовет ошибку компиляции.

Схема такая: клиент вызывает серверную процедуру или функцию, сервер читает данные и возвращает результат.

&НаСервереБезКонтекста
Функция ПолучитьАртикул(Код)
    Ссылка = Справочники.Номенклатура.НайтиПоКоду(Код);
    Если Ссылка.Пустая() Тогда
        Возврат "";
    КонецЕсли;
    Возврат Ссылка.Артикул;
КонецФункции

&НаКлиенте
Процедура ПоказатьАртикул(Команда)
    Артикул = ПолучитьАртикул(Элементы.Код.ТекстРедактирования);
    Предупреждение(Артикул);
КонецПроцедуры

Директива &НаСервереБезКонтекста не передаёт на сервер данные формы целиком, поэтому вызов получается легче, чем с &НаСервере. Если серверному коду нужны реквизиты формы, берите &НаСервере.

Если у вас уже есть ссылка, пришедшая на клиент как реквизит формы, прочитать её свойства на клиенте всё равно нельзя. Любой реквизит вне данных формы читается на сервере.

Не вызывайте сервер в цикле на клиенте. Соберите все нужные ссылки в массив, передайте за один вызов и верните готовую структуру или таблицу.

Ошибки и пустые результаты

Типичные ситуации и что с ними делать:

Диагностика: выберите симптом

Чтобы код не падал на пустых данных, держите шаблон: поиск, проверка на пустоту, только потом чтение.

Ссылка = Справочники.Контрагенты.НайтиПоКоду(Код);
Если Ссылка.Пустая() Тогда
    ВызватьИсключение "Контрагент с кодом " + Код + " не найден";
КонецЕсли;

Если ошибка может прийти из недоступных данных, например из-за прав пользователя, оберните вызов в Попытка … Исключение … КонецПопытки и вернитесь к понятному сообщению через ОписаниеОшибки(). Права тоже влияют: пользователь без доступа к справочнику не прочитает его ни запросом, ни через точку.

Массовое чтение и производительность

Несколько привычек, которые экономят время на живых базах:

  • ⚡ не вызывайте НайтиПо… внутри цикла по большому массиву, если есть возможность выбрать всё запросом;
  • ⚡ кэшируйте повторяющиеся ссылки в соответствии (ключ — код или артикул, значение — ссылка);
  • ⚡ в запросе выбирайте только нужные поля, а не весь объект;
  • ⚡ в условии используйте индексируемые реквизиты, а поиск по подстроке оставьте для редких случаев;
  • ⚡ не используйте ПолучитьОбъект() только ради чтения: объект грузится целиком и блокирует лишнее, если его потом записывать.

Простой кэш на соответствии выглядит так:

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

Если справочник могут менять параллельно другие пользователи, кэш живёт только внутри одной операции. Не храните его между сеансами.

Пример: ссылка по артикулу из файла

Типичная задача: на входе артикул из файла, на выходе нужна ссылка на номенклатуру и её единица измерения. Решение в три шага.

  1. Ищем элемент по реквизиту, а не по наименованию:
Ссылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", ТекущийАртикул);
  1. Если ссылка пустая, пишем строку в протокол загрузки и идём дальше, не прерывая весь файл.
  2. Если нашли, читаем нужные поля и складываем в структуру:
Данные = Новый Структура;
Данные.Вставить("Ссылка", Ссылка);
Данные.Вставить("Единица", Ссылка.ЕдиницаИзмерения);

Для файла на пять строк этого хватит. Для файла на пятьдесят тысяч картина другая: 50 000 вызовов поиска и столько же обращений через точку дадут минуты работы. Загрузите артикулы во временную таблицу запроса и соедините её со справочником одним запросом. Время падает с минут до секунд, потому что база делает одну операцию вместо десятков тысяч.

Частые вопросы

Чем НайтиПоКоду отличается от запроса по коду?
Метод проще и короче, возвращает ссылку. Запрос нужен, когда за один проход надо получить несколько полей или список элементов.
Можно ли получить значение справочника на клиенте без обращения к серверу?
Нет. Менеджеры справочников доступны только на сервере. Вынесите чтение в серверную функцию и вызовите её из клиентского кода.
Что вернёт поиск, если элемент не найден?
Пустую ссылку соответствующего типа. Проверяйте её через Пустая().
Как быстро получить один реквизит, не загружая весь объект?
В типовых конфигурациях есть функция ОбщегоНазначения.ЗначениеРеквизитаОбъекта. В нестандартных пишите короткий запрос с ПЕРВЫЕ 1.
Почему обращение к реквизиту в цикле тормозит?
Каждое обращение через точку может читать объект из базы. На больших объёмах заменяйте цикл одним запросом.