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