Получение данных справочника в 1С: от НайтиПоКоду до запроса
Содержание
Чтобы получить значение из справочника 1С, найдите элемент и прочитайте его реквизит. Для поиска служат методы менеджера: Справочники.Номенклатура.НайтиПоКоду("00-000123") или НайтиПоНаименованию("Стол офисный", Истина). Они возвращают ссылку, а нужное поле берётся через точку: Ссылка.Артикул.
Это быстрый путь для одного элемента. Если элементов много или условие сложное, нужен запрос с выборкой. Ниже все способы по порядку: от простого к тяжёлому, с кодом и подводными камнями.
Выбор способа: какой метод взять
Сначала определитесь, сколько элементов нужно и что вы про них знаете.
| Что есть | Что нужно | Способ |
|---|---|---|
| Код элемента | один элемент | НайтиПоКоду |
| Точное наименование | один элемент | НайтиПоНаименованию |
| Значение любого реквизита | один элемент | НайтиПоРеквизиту |
| Уже есть ссылка | один реквизит | обращение через точку |
| Условие по нескольким полям | список элементов | запрос + выборка |
| Предопределённый элемент | ссылка на него | Справочники.Имя.ИмяЭлемента |
Правило для практики: один элемент по известному ключу — методы менеджера. Всё, что похоже на «выбрать все, где…», — запрос.
Поиск одного элемента по коду, наименованию и реквизиту
Менеджер справочника умеет искать сам. Каждый метод возвращает ссылку на найденный элемент. Если ничего нет, вернётся пустая ссылка.
По коду
Ссылка = Справочники.Контрагенты.НайтиПоКоду("000000145");
Если Не Ссылка.Пустая() Тогда
ИНН = Ссылка.ИНН;
КонецЕсли;
Код в справочнике может быть строкой или числом, это зависит от настроек вида справочника. Строковый код с ведущими нулями передавайте строкой целиком: "000000145", а не 145.
У метода есть второй параметр, ПоискПоПолномуКоду. Он нужен для иерархических справочников с кодом, который уникален только внутри родителя.
По наименованию
Ссылка = Справочники.Номенклатура.НайтиПоНаименованию("Стол офисный", Истина);
Второй параметр Истина включает точное соответствие. Без него метод ищет по началу строки и вернёт первый подходящий элемент. Для надёжной логики всегда ставьте Истина.
Наименование в большинстве справочников не уникально. Метод вернёт один элемент из нескольких одноимённых, и какой именно, заранее неизвестно. Для ключевых данных так искать нельзя.
По произвольному реквизиту
Ссылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", "СТ-2040");
Метод берёт имя реквизита и искомое значение. Работает быстро, если по реквизиту настроен индекс. Если реквизит не индексируется, на большом справочнике поиск будет заметно медленнее.
Поиск внутри группы или по владельцу
Для иерархии и подчинённых справочников добавляйте параметры родителя и владельца. Так вы исключите совпадения из чужих веток:
Ссылка = Справочники.Договоры.НайтиПоНаименованию(
"Основной договор", Истина, , КонтрагентСсылка);
Здесь последний параметр — владелец. Пропущенный параметр оставляйте пустым через запятую.
Чтение реквизитов через ссылку
Ссылка на элемент — это не копия данных, а указатель. Любой реквизит читается через точку:
Наименование = Ссылка.Наименование;
Артикул = Ссылка.Артикул;
ЕдиницаИзм = Ссылка.ЕдиницаИзмерения.Наименование;
Цепочка через точку проходит по связанным справочникам. Платформа сама подтянет данные из базы.
Есть цена вопроса. Каждое обращение через точку к ссылке — отдельное чтение объекта из базы, и в цикле на тысячи элементов это даёт ощутимую задержку. Если нужно прочитать много полей у многих элементов, берите запрос.
Один реквизит без загрузки всего объекта
В типовых конфигурациях на базе библиотеки стандартных подсистем есть готовая функция:
Артикул = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Артикул");
Она читает только указанное поле и не тянет объект целиком. Если у вас нестандартная конфигурация без этой библиотеки, функции не будет. Тогда пишите небольшой запрос сами.
Предопределённые элементы
Если элемент заведён как предопределённый, ссылка получается без поиска:
Ссылка = Справочники.ВидыКонтактнойИнформации.ИмяПредопределенного;
Имя берётся из конфигуратора, из списка предопределённых данных справочника. Это самый быстрый и надёжный способ: он не зависит ни от кода, ни от наименования, которые пользователь мог изменить.
Запрос к справочнику: сложная фильтрация
Когда условий несколько или результат — это список, нужен запрос. Он работает на уровне СУБД и справляется с тысячами строк.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Н.Ссылка КАК Ссылка,
| Н.Наименование КАК Наименование,
| Н.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Н
|ГДЕ
| Н.Артикул ПОДОБНО &Маска
| И НЕ Н.ПометкаУдаления";
Запрос.УстановитьПараметр("Маска", "СТ-%");
Результат = Запрос.Выполнить();
Три момента, на которых чаще всего спотыкаются:
- 🔹 значения подставляйте параметрами (
&Маска), а не склеивайте в строку запроса; - 🔹 условие
НЕ Н.ПометкаУдаленияотсекает удалённые элементы, если они не нужны; - 🔹 для одного элемента добавьте
ПЕРВЫЕ 1, чтобы база не искала дальше.
Получить значение одного поля из запроса
Если запрос вернёт ровно одну строку, её удобно прочитать так:
Выборка = Результат.Выбрать();
Если Выборка.Следующий() Тогда
Артикул = Выборка.Артикул;
КонецЕсли;
Выгрузка в таблицу значений подойдёт, когда данные нужны целиком и с ними будут работать дальше:
Таблица = Результат.Выгрузить();
Объект Выборка: обход результата
Выбрать() возвращает курсор. Он стоит перед первой строкой, поэтому без Следующий() читать нечего.
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Наименование + " — " + Выборка.Артикул);
КонецЦикла;
Сравним два способа получить данные из результата запроса:
| Выборка | Выгрузка в таблицу | |
|---|---|---|
| Память | читает по строке | держит всё в памяти |
| Скорость обхода | быстрая | быстрая, но с затратой на выгрузку |
| Когда брать | один проход по данным | нужны сортировка, поиск, несколько проходов |
Для справочника в сотни тысяч элементов выбирайте выборку. Таблицу значений грузите, только если без неё не обойтись.
Если в запросе есть иерархия, у Выбрать() есть параметр обхода: Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам). Для простого справочника без группировок он не нужен.
Ссылки и уникальные идентификаторы
Ссылка однозначно указывает на элемент внутри информационной базы. Но ссылку нельзя просто «унести» в другую базу: там у того же элемента будет другая ссылка. Для обмена между базами используют идентификатор.
Идентификатор = Ссылка.УникальныйИдентификатор();
И обратная операция, из идентификатора в ссылку:
Ссылка = Справочники.Контрагенты.ПолучитьСсылку(Идентификатор);
Нюанс: ПолучитьСсылку всегда вернёт ссылку, даже если элемента с таким идентификатором в базе нет. Проверяйте результат:
Объект = Ссылка.ПолучитьОбъект();
Если Объект = Неопределено Тогда
// элемента в базе нет
КонецЕсли;
Не сохраняйте внутреннее строковое представление ссылки для долгого хранения или передачи в другую базу. Для этого есть уникальный идентификатор или естественный ключ вроде ИНН и КПП.
Пустая ссылка и проверка
Не нашлось — значит, пустая ссылка. Проверять нужно именно её:
Если Ссылка.Пустая() Тогда
// не нашли
КонецЕсли;
Сравнение с Неопределено для результата НайтиПоКоду ничего не даст: метод возвращает пустую ссылку нужного типа, а не Неопределено.
Работа в управляемой форме: клиент и сервер
В управляемой форме код разделён между клиентом и сервером. Справочники.Имя — это серверный объект, поэтому на клиенте его просто нет. Вызов вида Справочники.Номенклатура.НайтиПоКоду(...) из клиентской процедуры вызовет ошибку компиляции.
Схема такая: клиент вызывает серверную процедуру или функцию, сервер читает данные и возвращает результат.
&НаСервереБезКонтекста
Функция ПолучитьАртикул(Код)
Ссылка = Справочники.Номенклатура.НайтиПоКоду(Код);
Если Ссылка.Пустая() Тогда
Возврат "";
КонецЕсли;
Возврат Ссылка.Артикул;
КонецФункции
&НаКлиенте
Процедура ПоказатьАртикул(Команда)
Артикул = ПолучитьАртикул(Элементы.Код.ТекстРедактирования);
Предупреждение(Артикул);
КонецПроцедуры
Директива &НаСервереБезКонтекста не передаёт на сервер данные формы целиком, поэтому вызов получается легче, чем с &НаСервере. Если серверному коду нужны реквизиты формы, берите &НаСервере.
Если у вас уже есть ссылка, пришедшая на клиент как реквизит формы, прочитать её свойства на клиенте всё равно нельзя. Любой реквизит вне данных формы читается на сервере.
Не вызывайте сервер в цикле на клиенте. Соберите все нужные ссылки в массив, передайте за один вызов и верните готовую структуру или таблицу.
Ошибки и пустые результаты
Типичные ситуации и что с ними делать:
Ссылка.Пустая() или значение на Неопределено.Истина вторым параметром или ищите по уникальному реквизиту.Чтобы код не падал на пустых данных, держите шаблон: поиск, проверка на пустоту, только потом чтение.
Ссылка = Справочники.Контрагенты.НайтиПоКоду(Код);
Если Ссылка.Пустая() Тогда
ВызватьИсключение "Контрагент с кодом " + Код + " не найден";
КонецЕсли;
Если ошибка может прийти из недоступных данных, например из-за прав пользователя, оберните вызов в Попытка … Исключение … КонецПопытки и вернитесь к понятному сообщению через ОписаниеОшибки(). Права тоже влияют: пользователь без доступа к справочнику не прочитает его ни запросом, ни через точку.
Массовое чтение и производительность
Несколько привычек, которые экономят время на живых базах:
- ⚡ не вызывайте
НайтиПо…внутри цикла по большому массиву, если есть возможность выбрать всё запросом; - ⚡ кэшируйте повторяющиеся ссылки в соответствии (ключ — код или артикул, значение — ссылка);
- ⚡ в запросе выбирайте только нужные поля, а не весь объект;
- ⚡ в условии используйте индексируемые реквизиты, а поиск по подстроке оставьте для редких случаев;
- ⚡ не используйте
ПолучитьОбъект()только ради чтения: объект грузится целиком и блокирует лишнее, если его потом записывать.
Простой кэш на соответствии выглядит так:
Кэш = Новый Соответствие;
Для Каждого Строка Из Таблица Цикл
Ссылка = Кэш.Получить(Строка.Артикул);
Если Ссылка = Неопределено Тогда
Ссылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Строка.Артикул);
Кэш.Вставить(Строка.Артикул, Ссылка);
КонецЕсли;
КонецЦикла;
Если справочник могут менять параллельно другие пользователи, кэш живёт только внутри одной операции. Не храните его между сеансами.
Пример: ссылка по артикулу из файла
Типичная задача: на входе артикул из файла, на выходе нужна ссылка на номенклатуру и её единица измерения. Решение в три шага.
- Ищем элемент по реквизиту, а не по наименованию:
Ссылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", ТекущийАртикул);
- Если ссылка пустая, пишем строку в протокол загрузки и идём дальше, не прерывая весь файл.
- Если нашли, читаем нужные поля и складываем в структуру:
Данные = Новый Структура;
Данные.Вставить("Ссылка", Ссылка);
Данные.Вставить("Единица", Ссылка.ЕдиницаИзмерения);
Для файла на пять строк этого хватит. Для файла на пятьдесят тысяч картина другая: 50 000 вызовов поиска и столько же обращений через точку дадут минуты работы. Загрузите артикулы во временную таблицу запроса и соедините её со справочником одним запросом. Время падает с минут до секунд, потому что база делает одну операцию вместо десятков тысяч.
Частые вопросы
Чем НайтиПоКоду отличается от запроса по коду?
Можно ли получить значение справочника на клиенте без обращения к серверу?
Что вернёт поиск, если элемент не найден?
Пустая().Как быстро получить один реквизит, не загружая весь объект?
ОбщегоНазначения.ЗначениеРеквизитаОбъекта. В нестандартных пишите короткий запрос с ПЕРВЫЕ 1.