При разработке алгоритмов импорта данных из внешних источников (например, из файлов Excel) одной из частых задач является поиск существующих элементов справочника по текстовому представлению и получение ссылки на найденный объект. Рассмотрим типичные ошибки, возникающие при написании таких функций, разберем правильные варианты реализации запросов и альтернативные методы поиска платформы 1С.
Проанализируем исходную проблему. При реализации поиска через запрос разработчики часто допускают ошибку несовпадения полей в секции ВЫБРАТЬ и при обращении к результату выборки. Посмотрим на ошибочный фрагмент кода:
// ОШИБКА: в секции ВЫБРАТЬ указано только Наименование,
// а при обращении к выборке запрашивается поле Номенклатура или Ссылка
Запрос.Текст = "ВЫБРАТЬ
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО &Наименование";
...
Если ВыборкаДетальныеЗаписи.Следующий() Тогда
Возврат ВыборкаДетальныеЗаписи.Номенклатура; // Вызовет ошибку "Поле объекта не обнаружено"
КонецЕсли;
Выборка из результата запроса содержит исключительно те поля, которые были явно перечислены в секции ВЫБРАТЬ. Если поле Ссылка не указано в запросе, обратиться к нему через ВыборкаДетальныеЗаписи.Ссылка невозможно. Поля с именем таблицы (Номенклатура) в выборке также не существует, если только для него не был задан соответствующий псевдоним.
Разберем корректную реализацию функции поиска через объект Запрос. Для получения ссылки необходимо явно указать поле Номенклатура.Ссылка в запросе. Для предотвращения неоднозначности, если найдено несколько совпадений, рекомендуется использовать конструкцию ВЫБРАТЬ ПЕРВЫЕ 1.
Функция ПолучитьСсылкуНоменклатуры(ТекстПоиска) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО &Наименование
|УПОРЯДОЧИТЬ ПО
| Номенклатура.ЭтоГруппа,
| Номенклатура.Наименование";
// При использовании ПОДОБНО можно искать как точное совпадение, так и подстроку:
Запрос.УстановитьПараметр("Наименование", ТекстПоиска);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Ссылка;
Иначе
Возврат Справочники.Номенклатура.ПустаяСсылка();
КонецЕсли;
КонецФункции
Рассмотрим важные особенности работы с оператором ПОДОБНО в запросах:
"%" + СокрЛП(ТекстПоиска) + "%".ПОДОБНО "%..." отключает использование индекса по наименованию в СУБД, что приводит к полному сканированию таблицы (Table Scan). Для быстрого и точного поиска по индексу следует использовать прямое равенство ГДЕ Номенклатура.Наименование = &Наименование.%, _, [ или ], они воспринимаются оператором ПОДОБНО как спецсимволы шаблона. В таких случаях необходимо использовать ключевое слово СПЕЦСИМВОЛ для их экранирования.Если требуется выполнить быстрый поиск одиночного элемента без написания текста запроса, воспользуемся методом менеджера справочника НайтиПоНаименованию(). Этот метод оптимизирован платформой и работает быстрее простого запроса.
Функция НайтиНоменклатуруПоИмени(ТекстПоиска) Экспорт
// Синтаксис: НайтиПоНаименованию(Наименование, ТочноеСоответствие, Родитель, Владелец)
// Второй параметр (ТочноеСоответствие) по умолчанию равен Ложь (поиск по началу строки).
// Для строгого поиска обязательно передаем Истина:
СсылкаНоменклатуры = Справочники.Номенклатура.НайтиПоНаименованию(СокрЛП(ТекстПоиска), Истина);
Возврат СсылкаНоменклатуры; // Если элемент не найден, вернется ПустаяСсылка
КонецФункции
Обратим внимание: если параметр ТочноеСоответствие не указан или равен Ложь, платформа выполнит поиск по вхождению строки с начала наименования, что может привести к выбору не того элемента (например, при поиске «Болт» будет найден «Болт М8х20»).
При обработке строк, полученных из табличных документов и файлов Excel, часто возникают скрытые проблемы сопоставления. Разберем основные нюансы подготовки текста перед поиском:
Символы.НПП). Функция СокрЛП() удаляет только стандартные пробелы (код 32). Перед поиском обязательно очистим строку:
ТекстОчищенный = СтрЗаменить(ТекстЯчейки, Символы.НПП, " ");
ТекстОчищенный = СокрЛП(ТекстОчищенный);
Наименование имеет фиксированную максимальную длину (например, 100 или 150 символов). Если наименование в файле длиннее, точное сравнение вернет ложный результат. Рекомендуется обрезать входящую строку с помощью функции Лев() под длину реквизита.Вызов запроса или метода НайтиПоНаименованию() в цикле для каждой строки загружаемого файла существенно замедляет процесс импорта. Разберем правильный подход к массовой загрузке:
ГДЕ Номенклатура.Наименование В (&МассивНаименований).Соответствие, где ключом будет наименование, а значением — найденная ссылка.Если в процессе проверки функция вернула пустую ссылку, создадим новый элемент справочника программно. Проанализируем пример корректного заполнения обязательных реквизитов:
Если Не ЗначениеЗаполнено(СсылкаНоменклатуры) Тогда
НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
НовыйОбъект.Наименование = ТекстОчищенный;
// Обязательно заполняем системные и учетные реквизиты,
// необходимые для проведения документов:
НовыйОбъект.ВидНоменклатуры = Справочники.ВидыНоменклатуры.Товар;
НовыйОбъект.БазоваяЕдиницаИзмерения = Справочники.КлассификаторЕдиницИзмерения.Штука;
НовыйОбъект.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
Попытка
НовыйОбъект.Записать();
СсылкаНоменклатуры = НовыйОбъект.Ссылка;
Исключение
Сообщить("Не удалось записать номенклатуру: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
Таким образом, для надежного поиска и сопоставления номенклатуры важно корректно формировать поля выборки в запросах, предварительно очищать входящие текстовые данные от скрытых символов и учитывать особенности индексации строковых полей в платформе 1С.