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