Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда стандартный метод поиска по наименованию неожиданно возвращает пустую ссылку, хотя нужный элемент гарантированно присутствует в базе данных. Особенно часто это происходит при автоматическом импорте данных из внешних систем (файлов Excel, XML, JSON или через API), когда в качестве идентификатора используется текстовое наименование объекта.
В этой статье мы подробно разберем, почему возникает эта проблема, изучим внутренние механизмы платформы «1С:Предприятие», СУБД и предложим несколько надежных программных и архитектурных решений, которые позволят навсегда забыть об этой ошибке.
Давайте проанализируем, что происходит на уровне платформы и базы данных, когда мы создаем элемент справочника с длинным названием. Возьмем классический пример строки из реальной практики:
"Средства для защиты кожи при негативном влиянии окружающей среды (например, воздействие пониженных температур, ветра) (от раздражения и повреждения кожи)"
Эта строка состоит из 19 слов и содержит ровно 153 символа (включая пробелы и знаки препинания).
Стандартный реквизит Наименование любого справочника в 1С имеет жесткое ограничение длины, заданное в конфигураторе в свойствах этого справочника. По умолчанию в платформе максимальная длина наименования может составлять до 150 символов (в зависимости от настроек конкретной конфигурации этот лимит часто уменьшают до 100, 80 или даже 50 символов).
Когда мы записываем новый элемент, платформа 1С и СУБД ведут себя следующим образом:
Записать() платформа передает строку СУБД."Средства для защиты кожи при негативном влиянии окружающей среды (например, воздействие пониженных температур, ветра) (от раздражения и повреждения кож" (ровно 150 символов, последние три буквы "и", "ж", "и" и закрывающая скобка были отброшены).Когда в последующем коде мы пытаемся выполнить поиск элемента, используя полную строку из внешнего источника:
НайденнаяСсылка = Справочники.СмывающиеОбезвреживающиеСредства.НайтиПоНаименованию(ИскомаяСтрока, Истина);
Система пытается сопоставить исходные 153 символа с сохраненными в базе 150 символами. Так как мы указали второй параметр поиска как Истина (что означает строгое точное соответствие), СУБД выполняет операцию равенства строк. Очевидно, что строка длиной 153 символа не равна строке длиной 150 символов. Поиск возвращает пустую ссылку.
---
Простейший способ решения проблемы — предварительно обрезать искомую строку перед вызовом поиска. Однако хардкодить числовые значения длины (например, писать функцию Лев(Строка, 150)) крайне не рекомендуется. Если в будущем длину наименования справочника изменят в конфигурации, ваш код снова начнет работать некорректно.
Давайте рассмотрим, как правильно получить ограничение длины динамически из метаданных конфигурации:
// Получаем максимально допустимую длину наименования для конкретного справочника
МаксДлина = Метаданные.Справочники.СмывающиеОбезвреживающиеСредства.ДлинаНаименования;
// Обрезаем исходную строку до размера, который физически может храниться в базе
СтрокаПоиска = СокрЛП(СтрокаТаблицыЗначений.СредствоЗащиты);
СтрокаПоиска = Лев(СтрокаПоиска, МаксДлина);
// Выполняем точный поиск по усеченной строке
НайденнаяСсылка = Справочники.СмывающиеОбезвреживающиеСредства.НайтиПоНаименованию(СтрокаПоиска, Истина);
Этот способ является универсальным, надежным и не зависит от будущих изменений структуры метаданных конфигурации.
---
При загрузке данных из внешних файлов (особенно из таблиц Microsoft Excel) разработчики часто сталкиваются с невидимой проблемой — наличием неразрывных пробелов (символ с кодом 160 или Символы.НПП в терминологии 1С).
Стандартная функция СокрЛП() отлично справляется с обычными пробелами (код 32), символами табуляции и перевода каретки, но она абсолютно «не видит» неразрывные пробелы. Если такой пробел окажется в конце строки или внутри нее, то даже правильно обрезанная строка не будет найдена базой данных.
Давайте разработаем функцию предварительной нормализации поисковой строки, которая очистит её от всех лишних спецсимволов:
// Функция нормализации поискового запроса
Функция ПодготовитьСтрокуДляПоиска(Знач ИсходнаяСтрока, МаксДлина) Экспорт
// Заменяем неразрывные пробелы на стандартные
Результат = СтрЗаменить(ИсходнаяСтрока, Символы.НПП, " ");
// Убираем переводы строк и возвраты каретки
Результат = СтрЗаменить(Результат, Символы.ПС, " ");
Результат = СтрЗаменить(Результат, Символы.ВК, " ");
// Удаляем двойные пробелы, которые могли образоваться после замен
Пока Найти(Результат, " ") > 0 Цикл
Результат = СтрЗаменить(Результат, " ", " ");
КонецЦикла;
// Удаляем пробелы по краям
Результат = СокрЛП(Результат);
// Обрезаем строку по максимальной длине из метаданных
Возврат Лев(Результат, МаксДлина);
КонецФункции
Использование такой функции перед вызовом поиска гарантирует максимальную выверяемость данных и страхует от невидимых символов форматирования.
---
Вторым параметром метода НайтиПоНаименованию() является булево значение ТочноеСоответствие. Если передать туда значение Ложь, платформа выполнит поиск по левой (начальной) части наименования.
Давайте посмотрим на пример такого вызова:
// Ищем элемент без требования точного совпадения всей длины
НайденнаяСсылка = Справочники.СмывающиеОбезвреживающиеСредства. НайтиПоНаименованию(СтрокаТаблицыЗначений.СредствоЗащиты, Ложь);
При таком подходе СУБД найдет элемент, даже если поисковый запрос длиннее, чем значение в базе. Платформа просто сравнит первые 150 символов строки поиска с хранящимся в базе значением.
Важное предупреждение: этот метод таит в себе скрытую опасность. Если в справочнике будут находиться два разных элемента, у которых первые 150 символов полностью совпадают, а различия идут дальше (например, в самом конце строки), система вернет вам первый попавшийся элемент. Это может привести к пересортице и логическим ошибкам в учете. Поэтому использовать поиск по неточному соответствию для длинных строк следует с осторожностью.
---
Иногда более гибким решением является написание классического запроса 1С. Запросы позволяют явно управлять логикой отбора и оптимизировать нагрузку на базу данных. Мы можем использовать оператор ПОДОБНО для поиска элемента по маске.
Давайте разберем пример построения такого запроса:
МаксДлина = Метаданные.Справочники.СмывающиеОбезвреживающиеСредства.ДлинаНаименования;
СтрокаДляПоиска = ПодготовитьСтрокуДляПоиска(СтрокаТаблицыЗначений.СредствоЗащиты, МаксДлина);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 2
| СмывающиеСредства.Ссылка КАК Ссылка,
| СмывающиеСредства.Наименование КАК Наименование
|ИЗ
| Справочник.СмывающиеОбезвреживающиеСредства КАК СмывающиеСредства
|ГДЕ
| СмывающиеСредства.Наименование ПОДОБНО &ШаблонПоиска";
// Добавляем символ процента в конец для поиска по маске начала строки
Запрос.УстановитьПараметр("ШаблонПоиска", СтрокаДляПоиска + "%");
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
// Если найдено более одного элемента, можем зафиксировать неоднозначность
Если Выборка.Количество() > 1 Тогда
ЗаписьЖурналаРегистрации("ИмпортДанных", УровеньЖурналаРегистрации.Предупреждение, , ,
"Найдено несколько соответствий для: " + СтрокаДляПоиска);
КонецЕсли;
НайденнаяСсылка = Выборка.Ссылка;
Иначе
НайденнаяСсылка = Справочники.СмывающиеОбезвреживающиеСредства.ПустаяСсылка();
КонецЕсли;
---
Если специфика вашего бизнеса требует хранения действительно длинных наименований (например, сложные названия химических веществ, лекарств, официальные длинные наименования контрагентов для печатных форм), хранить их в стандартном реквизите Наименование — архитектурная ошибка.
Правильный архитектурный паттерн проектирования справочников в 1С выглядит следующим образом:
НаименованиеПолное (или ОфициальноеНаименование) с типом Строка неограниченной длины (или с большим ограничением, например, 1000 символов).НаименованиеПолное, а его сокращенная и обрезанная копия автоматически помещается в стандартное Наименование.НаименованиеПолное.Давайте посмотрим, как выглядит эффективный поиск по полному наименованию неограниченной длины с помощью запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| СмывающиеСредства.Ссылка КАК Ссылка
|ИЗ
| Справочник.СмывающиеОбезвреживающиеСредства КАК СмывающиеСредства
|ГДЕ
| СмывающиеСредства.НаименованиеПолное = &НаименованиеПолное";
Запрос.УстановитьПараметр("НаименованиеПолное", СокрЛП(СтрокаТаблицыЗначений.СредствоЗащиты));
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
НайденнаяСсылка = Выборка.Ссылка;
Иначе
// Элемент не найден, создаем новый
КонецЕсли;
Этот способ полностью исключает любые проблемы с потерей окончаний слов или символов при поиске и сопоставлении данных.
---
Если вы работаете в типовой конфигурации, находящейся на поддержке (например, «1С:Бухгалтерия» или «1С:ERP»), и не хотите снимать её с замка ради изменения одного свойства, вы можете использовать механизм расширения конфигурации.
Для этого выполните следующие действия:
Длина наименования до необходимого вам размера (например, до максимально возможных 150 символов).Это позволит расширить возможности хранения данных без усложнения процесса последующих обновлений вашей базы данных.