Разработка и Конфигурация · 11 мин
1С: как программно выбрать элемент справочника
Как программно выбрать элемент справочника в 1С: поиск по коду, наименованию и реквизиту, выборка запросом, проверка результата и типичные ошибки разработчика.
Программный выбор элемента справочника — одна из базовых операций при разработке на платформе 1С:Предприятие. Она нужна при обмене данными, загрузке прайсов, автоматическом заполнении документов, синхронизации справочников между базами и в любых обработках, где по известному значению (коду, наименованию, ИНН) требуется получить ссылку на объект.
В языке 1С есть несколько способов получить ссылку на элемент справочника: методы НайтиПоКоду(), НайтиПоНаименованию(), НайтиПоРеквизиту(), а также произвольный поиск запросом. Каждый способ имеет свои условия применения, ограничения по производительности и типичные ошибки. Ниже разобраны все основные сценарии с примерами кода, проверкой результата и рекомендациями по выбору метода.
Базовый механизм: менеджер справочника и ссылки
Вся работа со справочником в коде идёт через его менеджер: Справочники.Номенклатура, Справочники.Контрагенты и так далее. Менеджер предоставляет методы поиска, которые возвращают ссылку на элемент — легковесный объект, содержащий только уникальный идентификатор. Чтобы прочитать реквизиты элемента, ссылку нужно разыменовать (обратиться к полю через точку) или получить объект методом ПолучитьОбъект().
Важно понимать разницу: ссылка не загружает данные из базы целиком, а обращение к реквизиту ссылки выполняет неявное обращение к базе. Поэтому поиск элемента и чтение его данных — разные по стоимости операции. Для массовых операций правильнее получать данные запросом, а не перебирать ссылки с разыменованием.
Поиск элемента по коду
Метод НайтиПоКоду() — самый быстрый способ, потому что код справочника проиндексирован. Он подходит, когда код известен точно, например при синхронизации по артикулу или коду из внешней системы.
НайденныйЭлемент = Справочники.Номенклатура.НайтиПоКоду("000000123");
Если НайденныйЭлемент = Справочники.Номенклатура.ПустаяСсылка() Тогда
// Элемент не найден — создаём или сообщаем об ошибке
КонецЕсли;
У метода есть нюансы. Если у справочника включена иерархия, поиск по умолчанию идёт среди всех элементов, включая группы. Дополнительные параметры позволяют ограничить поиск родителем и владельцем: НайтиПоКоду(Код, Ложь, Родитель, Владелец). Второй параметр отвечает за поиск по полному коду с учётом иерархии (в подчинённых и иерархических справочниках).
Типичная ловушка — коды с лидирующими нулями: строка "123" не совпадёт с кодом "000000123". Если код числовой, платформа приведёт тип сама, но при строковом коде нужно передавать значение точно в том формате, в котором оно хранится.
Поиск по наименованию
Метод НайтиПоНаименованию() ищет точное совпадение строки с полем наименования. Он удобен, когда внешняя система передаёт именно название, но надёжность ниже: наименования могут дублироваться, отличаться пробелами или регистром.
НайденныйКонтрагент = Справочники.Контрагенты.НайтиПоНаименованию("ООО Ромашка");
Если Не ЗначениеЗаполнено(НайденныйКонтрагент) Тогда
ВызватьИсключение "Контрагент не найден";
КонецЕсли;
Поиск по наименованию возвращает первый найденный элемент. Если в справочнике несколько элементов с одинаковым наименованием, результат фактически случайный — опираться на него в обмене данными нельзя. Для таких случаев нужен поиск по уникальному реквизиту (ИНН, артикул) или запрос с дополнительными условиями.
Поиск по реквизиту: ИНН, артикул и другие поля
Метод НайтиПоРеквизиту() позволяет искать по любому реквизиту справочника, если для него в конфигураторе включено индексирование (свойство «Индексировать»). Это стандартный способ поиска контрагента по ИНН или товара по артикулу.
Найденный = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7701234567");
Если Найденный = Справочники.Контрагенты.ПустаяСсылка() Тогда
// Создаём нового контрагента
НовыйКонтрагент = Справочники.Контрагенты.СоздатьЭлемент();
НовыйКонтрагент.Наименование = "Новый контрагент";
НовыйКонтрагент.ИНН = "7701234567";
НовыйКонтрагент.Записать();
КонецЕсли;
Если реквизит не проиндексирован, поиск будет выполняться полным перебором таблицы, что на больших справочниках (сотни тысяч элементов) заметно тормозит обработку. Перед использованием метода на «тяжёлом» справочнике проверьте в конфигураторе, что у реквизита стоит признак индексирования, либо добавьте его, если это допустимо по архитектуре конфигурации.
Выборка элементов запросом
Когда условия поиска сложнее одного равенства — отбор по нескольким полям, по подстроке, по пометке удаления, выбор всех подходящих элементов — используется язык запросов 1С. Запрос также единственный правильный способ массовой обработки: он возвращает данные одним обращением к базе.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО &Наименование
| И НЕ Номенклатура.ПометкаУдаления";
Запрос.УстановитьПараметр("Наименование", "%" + СтрокаПоиска + "%");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Работа с Выборка.Ссылка
КонецЦикла;
Обратите внимание на оператор ПОДОБНО с символом % — это аналог поиска по подстроке. Условие НЕ ПометкаУдаления отсекает помеченные на удаление элементы: методы НайтиПоКоду и НайтиПоНаименованию по умолчанию находят и помеченные элементы, что часто становится источником ошибок при обменах.
Сравнение вариантов
Самый быстрый, код проиндексирован. Подходит для обменов по коду. Ограничение: нужно знать точный формат кода с ведущими нулями.
Простой, но ненадёжный при дублях. Подходит для интерактивных обработок, не для интеграций.
Универсальный поиск по уникальному полю (ИНН, артикул). Требует индексирования реквизита, иначе медленный на больших справочниках.
Любые условия, выбор множества элементов, массовая обработка. Требует большего кода и аккуратности с параметрами.
Проверка результата и получение объекта для изменения
Любой метод поиска возвращает пустую ссылку, если элемент не найден. Работа с пустой ссылкой без проверки приводит к ошибкам «Объект не найден» или к записи данных не туда. Стандартная проверка — сравнение с пустой ссылкой или функция ЗначениеЗаполнено().
Если элемент нужно не только прочитать, но и изменить, по ссылке получают объект методом ПолучитьОбъект(), изменяют реквизиты и вызывают Записать(). Помните: ПолучитьОбъект() загружает весь объект в память и блокирует его на время изменения, поэтому в массовых циклах это дорогая операция.
СсылкаНаЭлемент = Справочники.Номенклатура.НайтиПоКоду("000000123");
Если ЗначениеЗаполнено(СсылкаНаЭлемент) Тогда
ЭлементОбъект = СсылкаНаЭлемент.ПолучитьОбъект();
ЭлементОбъект.Наименование = "Новое наименование";
ЭлементОбъект.Записать();
КонецЕсли;
- 1Определите, по какому полю искать: код, наименование или уникальный реквизит
- 2Выберите метод поиска: НайтиПоКоду, НайтиПоНаименованию, НайтиПоРеквизиту или запрос
- 3Выполните поиск и проверьте результат на пустую ссылку
- 4Если элемент не найден — создайте новый или зафиксируйте ошибку в журнале
- 5Для изменения получите объект через ПолучитьОбъект(), измените и запишите
- 6Проверьте результат: реквизиты обновились, дублей не возникло
Типичные ошибки и ограничения
Большинство проблем при программном выборе элементов связано не с самими методами, а с данными и контекстом выполнения. Ниже — ошибки, которые встречаются чаще всего.
Отдельное ограничение — права доступа. Если код выполняется под пользователем без прав на чтение справочника, поиск вернёт пустую ссылку или вызовет исключение. В фоновых заданиях и обменах проверяйте, под каким пользователем выполняется сеанс. В привилегированном модуле (УстановитьПривилегированныйРежим(Истина)) проверка прав отключается — это удобно для служебных обработок, но опасно при работе с данными, вводимыми пользователем.
Ещё одна граница безопасной работы — транзакции. Если обработка ищет элемент и при его отсутствии создаёт новый, при параллельных сеансах возможны дубли. В критичных интеграциях такую логику оборачивают в транзакцию или используют уникальные индексы, чтобы база сама не допустила дублирования.
Как проверить результат
ЗначениеЗаполнено(Ссылка) возвращает Истина
Пустая ссылка — элемент не найден или нет прав на чтение
Реквизиты соответствуют ожидаемым, пометка удаления снята
Найден помеченный на удаление или дубль элемента
Поиск по индексированным полям, массовые операции — запросом
Обработка «зависает» на больших справочниках — вероятен полный перебор
Часто задаваемые вопросы
Чем НайтиПоКоду отличается от НайтиПоНаименованию по скорости?
Оба метода используют индексы, но код справочника всегда проиндексирован и уникален в рамках серии нумерации, поэтому поиск по коду предсказуемо быстрый. Наименование тоже индексируется, однако по нему возможны дубли, и для точной идентификации наименование не подходит.
Как найти элемент справочника по GUID?
Используйте метод ПолучитьСсылку() с объектом УникальныйИдентификатор: Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаGUID)). Чтобы проверить, существует ли элемент с таким идентификатором, получите объект и проверьте его на Неопределено или выполните запрос по ссылке.
Как выбрать все элементы справочника без условий?
Двумя способами: методом Выбрать() менеджера справочника (Выборка = Справочники.Номенклатура.Выбрать() с обходом через Следующий()) или запросом без предложения ГДЕ. Запрос предпочтительнее, если нужны только отдельные поля или дополнительная фильтрация.
Почему НайтиПоНаименованию не находит элемент, который точно есть?
Чаще всего причина в неразличимых различиях строк: лишние пробелы, неразрывные пробелы, разный регистр букв «е» и «ё». Сравнение выполняется точное. Очистите искомую строку функцией СокрЛП() и проверьте фактическое наименование в базе через запрос с ПОДОБНО.
Можно ли искать по нескольким реквизитам одновременно?
Метод НайтиПоРеквизиту принимает только одно поле. Для составного условия используйте запрос с несколькими условиями в разделе ГДЕ, объединёнными оператором И. Это также исключает ситуацию, когда по одному реквизиту находится не тот элемент.
Что делать, если поиск выполняется в цикле и работает медленно?
Вынесите поиск из цикла: сначала одним запросом получите соответствие «ключ поиска → ссылка» для всех нужных значений, затем в цикле обращайтесь к этому соответствию. Это сокращает число обращений к базе с тысяч до одного и ускоряет обработку на порядки.
Что важно запомнить
Для точного поиска используйте НайтиПоКоду или НайтиПоРеквизиту по уникальному проиндексированному полю, для сложных условий и массовой обработки — запрос. Результат всегда проверяйте на пустую ссылку, учитывайте пометку удаления, формат кода и права доступа. Изменение элемента выполняется только через ПолучитьОбъект() с последующим Записать().