При загрузке данных из внешних источников (в частности, таблиц Excel) разработчики часто сталкиваются с ситуацией, когда одно и то же значение в базе данных и в файле записано с разным количеством пробелов. Например, в базе 1С наименование хранится в виде 123 АБВ, а во входном файле строка представлена слитно: 123АБВ (или наоборот). Разберем, почему стандартный запрос не находит такие строки напрямую, и рассмотрим эффективные варианты решения этой задачи.
Прежде чем выбирать алгоритм, проанализируем технические особенности работы со строками в платформе 1С:
СтрЗаменить или SQL-оператора REPLACE. Доступны только функции ПОДСТРОКА, ДЛИНА, СОКРЛ, СОКРП, СОКРЛП и операция объединения строк (конкатенация). Вырезать пробелы из середины поля динамически внутри тела запроса невозможно.Символы.НПП). Обычная замена стандартного пробела " " их не удаляет, поэтому сопоставление завершается ошибкой.Если сопоставляемые данные имеют регламентированный формат (например, государственные регистрационные номера автомобилей или фиксированные артикулы), оптимальный путь — программно привести строку из Excel к эталонному виду перед выполнением запроса.
Рассмотрим пример функции очистки и приведения строки к заданному шаблону:
Функция ОчиститьСтрокуОтПробелов(ВходнаяСтрока)
// Удаляем стандартные и неразрывные пробелы
Результат = СтрЗаменить(ВходнаяСтрока, " ", "");
Результат = СтрЗаменить(Результат, Символы.НПП, "");
Результат = СокрЛП(Результат);
Возврат Результат;
КонецФункции
После получения очищенной строки можно сформировать эталонное представление с пробелами в нужных позициях и выполнить быстрый точный поиск по индексу:
СтрокаИзExcel = "123АБВ";
СтрокаБезПробелов = ОчиститьСтрокуОтПробелов(СтрокаИзExcel);
// Если известно, что пробел должен быть после третьего символа
ИскомоеНаименование = Лев(СтрокаБезПробелов, 3) + " " + Сред(СтрокаБезПробелов, 4);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка
|ИЗ
| Справочник.ТранспортныеСредства КАК Спр
|ГДЕ
| Спр.Наименование = &Наименование";
Запрос.УстановитьПараметр("Наименование", ИскомоеНаименование);
РезультатЗапроса = Запрос.Выполнить();
Когда позиция пробелов в наименованиях базы произвольна, а объем справочника велик, наиболее производительным решением является добавление отдельного реквизита для поиска.
Разберем по шагам реализацию этого подхода:
НаименованиеДляПоиска (тип Строка, свойство Индексировать установлено в значение Индексировать).ПередЗаписью прописываем автоматическое заполнение реквизита:
Процедура ПередЗаписью(Отказ)
НаименованиеДляПоиска = СтрЗаменить(СтрЗаменить(Наименование, " ", ""), Символы.НПП, "");
КонецПроцедуры
НаименованиеДляПоиска:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Спр
|ГДЕ
| Спр.НаименованиеДляПоиска = &ПоисковаяСтрока";
Запрос.УстановитьПараметр("ПоисковаяСтрока", ОчиститьСтрокуОтПробелов(СтрокаИзExcel));
Выборка = Запрос.Выполнить().Выбрать();
Этот способ обеспечивает максимальную скорость работы за счет использования индексов базы данных и не требует перебора элементов в цикле.
Если дорабатывать структуру метаданных нельзя, а строк в Excel много, перебор каждой строки в цикле запросами приведет к сильному падению производительности. В этом случае выгрузим данные справочника и файла в таблицы значений, очистим пробелы на стороне 1С-кода и соединим их одним запросом через временные таблицы.
Посмотрим на пример пакетной обработки:
// Шаг 1. Получаем все элементы справочника
ЗапросБазы = Новый Запрос;
ЗапросБазы.Текст =
"ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка,
| Спр.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Спр
|ГДЕ
| НЕ Спр.ПометкаУдаления";
ТаблицаБазы = ЗапросБазы.Выполнить().Выгрузить();
ТаблицаБазы.Колонки.Добавить("КлючПоиска", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));
// Шаг 2. Нормализуем наименования из базы
Для Каждого СтрБазы Из ТаблицаБазы Цикл
СтрБазы.КлючПоиска = ОчиститьСтрокуОтПробелов(СтрБазы.Наименование);
КонецЦикла;
// Шаг 3. Подготавливаем таблицу из Excel
ТаблицаExcel = Новый ТаблицаЗначений;
ТаблицаExcel.Колонки.Добавить("ИсходныйНомер", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));
ТаблицаExcel.Колонки.Добавить("КлючПоиска", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));
Для Каждого СтрExcel Из МассивДанныхExcel Цикл
НоваяСтрока = ТаблицаExcel.Добавить();
НоваяСтрока.ИсходныйНомер = СтрExcel;
НоваяСтрока.КлючПоиска = ОчиститьСтрокуОтПробелов(СтрExcel);
КонецЦикла;
// Шаг 4. Соединяем нормализованные таблицы запросом
МенеджерВТ = Новый МенеджерВременныхТаблиц;
ЗапросСопоставления = Новый Запрос;
ЗапросСопоставления.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросСопоставления.Текст =
"ВЫБРАТЬ
| Т_Excel.ИсходныйНомер КАК ИсходныйНомер,
| Т_База.Ссылка КАК Ссылка
|ИЗ
| &ТаблицаExcel КАК Т_Excel
| ЛЕВОЕ СОЕДИНЕНИЕ &ТаблицаБазы КАК Т_База
| ПО Т_Excel.КлючПоиска = Т_База.КлючПоиска";
ЗапросСопоставления.УстановитьПараметр("ТаблицаExcel", ТаблицаExcel);
ЗапросСопоставления.УстановитьПараметр("ТаблицаБазы", ТаблицаБазы);
РезультатСопоставления = ЗапросСопоставления.Выполнить().Выгрузить();
Если точное расположение пробелов неизвестно, а добавление реквизитов невозможно, можно применить оператор ПОДОБНО. Для этого входная строка разбивается символами-масками %:
// Преобразуем строку "123АБВ" в маску "%1%2%3%А%Б%В%"
СтрокаМаски = "%";
Для Индекс = 1 По СтрДлина(СтрокаИзExcel) Цикл
Символ = Сред(СтрокаИзExcel, Индекс, 1);
Если Символ <> " " И Символ <> Символы.НПП Тогда
СтрокаМаски = СтрокаМаски + Символ + "%";
КонецЕсли;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка,
| Спр.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Спр
|ГДЕ
| Спр.Наименование ПОДОБНО &ШаблонПоиска";
Запрос.УстановитьПараметр("ШаблонПоиска", СтрокаМаски);
Результат = Запрос.Выполнить();
Важно: При использовании маски, начинающейся с символа %, СУБД не сможет использовать индекс по наименованию. Это вызовет полное сканирование таблицы (Full Scan), поэтому данный способ подходит только для небольших справочников.
Если для справочника включено полнотекстовое индексирование, можно использовать объект ПолнотекстовыйПоиск. Механизм платформы автоматически разделяет текст на слова и основы, позволяя находить элементы независимо от разделителей между значащими частями строки.
Для конфигураций на базе Библиотеки стандартных подсистем (БСП) также рекомендуется использовать функции общего модуля СтроковыеФункцииКлиентСервер для корректного разделения и очистки строк от непечатаемых символов.