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