Как найти элемент справочника в 1С по наименованию из Excel, если не совпадают пробелы?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При загрузке данных из внешних источников (в частности, таблиц Excel) разработчики часто сталкиваются с ситуацией, когда одно и то же значение в базе данных и в файле записано с разным количеством пробелов. Например, в базе 1С наименование хранится в виде 123 АБВ, а во входном файле строка представлена слитно: 123АБВ (или наоборот). Разберем, почему стандартный запрос не находит такие строки напрямую, и рассмотрим эффективные варианты решения этой задачи.

Ограничения языка запросов 1С и специфика строковых данных

Прежде чем выбирать алгоритм, проанализируем технические особенности работы со строками в платформе 1С:

  1. Отсутствие функции замены в запросах: В языке запросов 1С нет прямого аналога функции СтрЗаменить или SQL-оператора REPLACE. Доступны только функции ПОДСТРОКА, ДЛИНА, СОКРЛ, СОКРП, СОКРЛП и операция объединения строк (конкатенация). Вырезать пробелы из середины поля динамически внутри тела запроса невозможно.
  2. Проблемы с неразрывными пробелами: При чтении данных из файлов Excel в строках нередко присутствуют неразрывные пробелы (символ с кодом 160 / Символы.НПП). Обычная замена стандартного пробела " " их не удаляет, поэтому сопоставление завершается ошибкой.
  3. Смешение раскладок (гомоглифы): Если сопоставляются артикулы, серийные или государственные регистрационные номера, символы кириллицы и латиницы с одинаковым начертанием (А, В, Е, К, М, Н, О, Р, С, Т, У, Х) могут быть введены в разных раскладках.

Вариант 1. Нормализация входных данных и приведение к формату

Если сопоставляемые данные имеют регламентированный формат (например, государственные регистрационные номера автомобилей или фиксированные артикулы), оптимальный путь — программно привести строку из Excel к эталонному виду перед выполнением запроса.

Рассмотрим пример функции очистки и приведения строки к заданному шаблону:


Функция ОчиститьСтрокуОтПробелов(ВходнаяСтрока)
    // Удаляем стандартные и неразрывные пробелы
    Результат = СтрЗаменить(ВходнаяСтрока, " ", "");
    Результат = СтрЗаменить(Результат, Символы.НПП, "");
    Результат = СокрЛП(Результат);
    Возврат Результат;
КонецФункции

После получения очищенной строки можно сформировать эталонное представление с пробелами в нужных позициях и выполнить быстрый точный поиск по индексу:


СтрокаИзExcel = "123АБВ";
СтрокаБезПробелов = ОчиститьСтрокуОтПробелов(СтрокаИзExcel);

// Если известно, что пробел должен быть после третьего символа
ИскомоеНаименование = Лев(СтрокаБезПробелов, 3) + " " + Сред(СтрокаБезПробелов, 4);

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Спр.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.ТранспортныеСредства КАК Спр
    |ГДЕ
    |    Спр.Наименование = &Наименование";

Запрос.УстановитьПараметр("Наименование", ИскомоеНаименование);
РезультатЗапроса = Запрос.Выполнить();

Вариант 2. Использование дополнительного реквизита для поиска

Когда позиция пробелов в наименованиях базы произвольна, а объем справочника велик, наиболее производительным решением является добавление отдельного реквизита для поиска.

Разберем по шагам реализацию этого подхода:

  1. В метаданных справочника добавляем реквизит, например, НаименованиеДляПоиска (тип Строка, свойство Индексировать установлено в значение Индексировать).
  2. В модуле объекта в событии ПередЗаписью прописываем автоматическое заполнение реквизита:
    
    Процедура ПередЗаписью(Отказ)
        НаименованиеДляПоиска = СтрЗаменить(СтрЗаменить(Наименование, " ", ""), Символы.НПП, "");
    КонецПроцедуры
    
  3. При поиске загружаемую строку из Excel очищаем от пробелов и выполняем запрос по индексированному полю НаименованиеДляПоиска:
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Спр.Ссылка КАК Ссылка
        |ИЗ
        |    Справочник.Номенклатура КАК Спр
        |ГДЕ
        |    Спр.НаименованиеДляПоиска = &ПоисковаяСтрока";
    
    Запрос.УстановитьПараметр("ПоисковаяСтрока", ОчиститьСтрокуОтПробелов(СтрокаИзExcel));
    Выборка = Запрос.Выполнить().Выбрать();
    

Этот способ обеспечивает максимальную скорость работы за счет использования индексов базы данных и не требует перебора элементов в цикле.

Вариант 3. Пакетное сопоставление через временные таблицы

Если дорабатывать структуру метаданных нельзя, а строк в Excel много, перебор каждой строки в цикле запросами приведет к сильному падению производительности. В этом случае выгрузим данные справочника и файла в таблицы значений, очистим пробелы на стороне 1С-кода и соединим их одним запросом через временные таблицы.

Посмотрим на пример пакетной обработки:


// Шаг 1. Получаем все элементы справочника
ЗапросБазы = Новый Запрос;
ЗапросБазы.Текст = 
    "ВЫБРАТЬ
    |    Спр.Ссылка КАК Ссылка,
    |    Спр.Наименование КАК Наименование
    |ИЗ
    |    Справочник.Номенклатура КАК Спр
    |ГДЕ
    |    НЕ Спр.ПометкаУдаления";

ТаблицаБазы = ЗапросБазы.Выполнить().Выгрузить();
ТаблицаБазы.Колонки.Добавить("КлючПоиска", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));

// Шаг 2. Нормализуем наименования из базы
Для Каждого СтрБазы Из ТаблицаБазы Цикл
    СтрБазы.КлючПоиска = ОчиститьСтрокуОтПробелов(СтрБазы.Наименование);
КонецЦикла;

// Шаг 3. Подготавливаем таблицу из Excel
ТаблицаExcel = Новый ТаблицаЗначений;
ТаблицаExcel.Колонки.Добавить("ИсходныйНомер", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));
ТаблицаExcel.Колонки.Добавить("КлючПоиска", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));

Для Каждого СтрExcel Из МассивДанныхExcel Цикл
    НоваяСтрока = ТаблицаExcel.Добавить();
    НоваяСтрока.ИсходныйНомер = СтрExcel;
    НоваяСтрока.КлючПоиска = ОчиститьСтрокуОтПробелов(СтрExcel);
КонецЦикла;

// Шаг 4. Соединяем нормализованные таблицы запросом
МенеджерВТ = Новый МенеджерВременныхТаблиц;
ЗапросСопоставления = Новый Запрос;
ЗапросСопоставления.МенеджерВременныхТаблиц = МенеджерВТ;

ЗапросСопоставления.Текст = 
    "ВЫБРАТЬ
    |    Т_Excel.ИсходныйНомер КАК ИсходныйНомер,
    |    Т_База.Ссылка КАК Ссылка
    |ИЗ
    |    &ТаблицаExcel КАК Т_Excel
    |    ЛЕВОЕ СОЕДИНЕНИЕ &ТаблицаБазы КАК Т_База
    |        ПО Т_Excel.КлючПоиска = Т_База.КлючПоиска";

ЗапросСопоставления.УстановитьПараметр("ТаблицаExcel", ТаблицаExcel);
ЗапросСопоставления.УстановитьПараметр("ТаблицаБазы", ТаблицаБазы);

РезультатСопоставления = ЗапросСопоставления.Выполнить().Выгрузить();

Вариант 4. Поиск с помощью оператора ПОДОБНО

Если точное расположение пробелов неизвестно, а добавление реквизитов невозможно, можно применить оператор ПОДОБНО. Для этого входная строка разбивается символами-масками %:


// Преобразуем строку "123АБВ" в маску "%1%2%3%А%Б%В%"
СтрокаМаски = "%";
Для Индекс = 1 По СтрДлина(СтрокаИзExcel) Цикл
    Символ = Сред(СтрокаИзExcel, Индекс, 1);
    Если Символ <> " " И Символ <> Символы.НПП Тогда
        СтрокаМаски = СтрокаМаски + Символ + "%";
    КонецЕсли;
КонецЦикла;

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Спр.Ссылка КАК Ссылка,
    |    Спр.Наименование КАК Наименование
    |ИЗ
    |    Справочник.Номенклатура КАК Спр
    |ГДЕ
    |    Спр.Наименование ПОДОБНО &ШаблонПоиска";

Запрос.УстановитьПараметр("ШаблонПоиска", СтрокаМаски);
Результат = Запрос.Выполнить();

Важно: При использовании маски, начинающейся с символа %, СУБД не сможет использовать индекс по наименованию. Это вызовет полное сканирование таблицы (Full Scan), поэтому данный способ подходит только для небольших справочников.

Вариант 5. Использование полнотекстового поиска

Если для справочника включено полнотекстовое индексирование, можно использовать объект ПолнотекстовыйПоиск. Механизм платформы автоматически разделяет текст на слова и основы, позволяя находить элементы независимо от разделителей между значащими частями строки.

Для конфигураций на базе Библиотеки стандартных подсистем (БСП) также рекомендуется использовать функции общего модуля СтроковыеФункцииКлиентСервер для корректного разделения и очистки строк от непечатаемых символов.

← На главную