Как реализовать приоритетный поиск по началу строки в 1С 8.3 (Обычные формы)

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Торговля и дистрибуция
← На главную

При работе в устаревших или классических конфигурациях на обычных формах (например, УТ 10.3, УПП 1.3 или КА 1.1) разработчики часто сталкиваются с тем, что стандартные механизмы поиска работают не так удобно, как в современных решениях на управляемых формах (Розница 2.0, УТ 11). Одной из наиболее востребованных функций является «умный» поиск: когда система выдает сначала те позиции, которые начинаются с введенного текста, а затем все остальные, где текст встречается в середине или в конце.

В этой статье мы подробно разберем, как модифицировать стандартный поиск в процедуре ОкончаниеВводаТекста или АвтоПодбор, чтобы добиться идеальной сортировки результатов.

Анализируем проблему стандартного подхода

Рассмотрим типичную ситуацию. Программист пишет запрос, который ищет контрагента по вхождению строки. Обычно это выглядит так:


Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты ГДЕ Наименование ПОДОБНО &Наименование";
Запрос.УстановитьПараметр("Наименование", "%" + Текст + "%");

Проблема такого кода в том, что база данных SQL (или файловая СУБД) возвращает результат либо в порядке создания записей, либо по алфавиту. Если пользователь вводит «Алт», он хочет первым увидеть «Алтай-Сервис», а не «База Алтайская». Но стандартный поиск выдаст их вперемешку. Чтобы решить эту задачу, нам нужно научить запрос определять «вес» или «приоритет» каждой найденной записи.

Решение №1: Использование вычисляемого поля веса (Оптимальный вариант)

Самый быстрый и эффективный способ — добавить в запрос условие, которое будет проверять, начинается ли наименование с искомого текста. Мы присвоим таким записям индекс 0, а всем остальным — индекс 1. Затем просто отсортируем результат по этому индексу.

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

  1. Подготовим два параметра: один для поиска по началу (Текст + "%"), другой для поиска везде ("%" + Текст + "%").
  2. В блоке ВЫБРАТЬ создадим поле с помощью конструкции ВЫБОР.
  3. Добавим сортировку по этому полю.

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


Процедура КонтрагентОкончаниеВводаТекста(Элемент, Текст, Значение, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь; // Отключаем стандартный поиск
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	Контрагенты.Ссылка КАК Ссылка,
    |	ВЫБОР
    |		КОГДА Контрагенты.Наименование ПОДОБНО &ТекстНачало ТОГДА 0
    |		ИНАЧЕ 1
    |	КОНЕЦ КАК Приоритет
    |ИЗ
    |	Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |	Контрагенты.Наименование ПОДОБНО &ТекстЛюбаяЧасть
    |
    |УПОРЯДОЧИТЬ ПО
    |	Приоритет,
    |	Контрагенты.Наименование";

    Запрос.УстановитьПараметр("ТекстНачало", Текст + "%");
    Запрос.УстановитьПараметр("ТекстЛюбаяЧасть", "%" + Текст + "%");

    Результат = Запрос.Выполнить();
    Если Результат.Пустой() Тогда
        Возврат;
    КонецЕсли;

    Выборка = Результат.Выбрать();
    СписокВыборки = Новый СписокЗначений;
    
    Пока Выборка.Следующий() Цикл
        СписокВыборки.Добавить(Выборка.Ссылка);
    КонецЦикла;

    // Выводим список пользователю для выбора
    ВыбранныйЭлемент = ВыбратьИзСписка(СписокВыборки, Элемент);
    Если ВыбранныйЭлемент <> Неопределено Тогда
        Значение = ВыбранныйЭлемент.Значение;
    КонецЕсли;
КонецПроцедуры

Решение №2: Объединение запросов (UNION)

Проанализируем альтернативный подход, который предлагался в обсуждении — использование оператора ОБЪЕДИНИТЬ ВСЕ. Этот способ также рабочий, но требует внимательности при настройке сортировки. Мы делаем два запроса: первый выбирает точные совпадения по началу строки, второй — все остальные вхождения. Чтобы результаты не дублировались, во втором запросе нужно исключить те записи, которые уже попали в первый.

Рассмотрим структуру такого запроса:


Запрос.Текст = 
"ВЫБРАТЬ
|	Контрагенты.Ссылка КАК Ссылка,
|	0 КАК Индекс
|ИЗ
|	Справочник.Контрагенты КАК Контрагенты
|ГДЕ
|	Контрагенты.Наименование ПОДОБНО &ТекстНачало
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|	Контрагенты.Ссылка,
|	1
|ИЗ
|	Справочник.Контрагенты КАК Контрагенты
|ГДЕ
|	Контрагенты.Наименование ПОДОБНО &ТекстЛюбаяЧасть
|	И НЕ Контрагенты.Наименование ПОДОБНО &ТекстНачало
|
|УПОРЯДОЧИТЬ ПО
|	Индекс,
|	Ссылка.Наименование";

Важный момент: использование ОБЪЕДИНИТЬ ВСЕ может работать чуть медленнее на огромных справочниках, так как СУБД приходится обрабатывать два подзапроса. Метод с ВЫБОР (CASE в SQL) обычно считается более элегантным и производительным.

Как сделать поиск по-настоящему «живым»?

Проанализируем ситуацию: событие ОкончаниеВводаТекста срабатывает только тогда, когда пользователь нажал Enter или Tab. Это не совсем то, что мы видим в Рознице 2.0. Чтобы поиск происходил мгновенно, «на лету», рекомендуется использовать событие АвтоПодбор.

Разберем особенности реализации в АвтоПодбор:

Пример реализации для создания ощущения «современного» интерфейса:


Процедура КонтрагентАвтоПодбор(Элемент, Текст, ТекстАвтоПодбора, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    
    // Если введено меньше 3 символов, не мучаем базу данных
    Если СтрДлина(Текст) < 3 Тогда
        Возврат;
    КонецЕсли;

    // Выполняем запрос из Решения №1...
    // ... (код запроса) ...

    Элемент.СписокВыбора.Очистить();
    Пока Выборка.Следующий() Цикл
        Элемент.СписокВыбора.Добавить(Выборка.Ссылка);
    КонецЦикла;
КонецПроцедуры

Дополнительные рекомендации по оптимизации

Чтобы ваш поиск был не только удобным, но и профессиональным, выясним несколько критических нюансов:

1. Ограничение количества результатов. Если пользователь введет букву «А», запрос может вернуть тысячи контрагентов. Это приведет к зависанию интерфейса. Всегда используйте ВЫБРАТЬ ПЕРВЫЕ 30 (или 50). Этого достаточно для выбора из выпадающего списка.

2. Проблема индексов. Помните, что условие ПОДОБНО "%Текст%" (с процентом в начале) принципиально не использует индексы базы данных. Это означает «Full Scan» — полный перебор таблицы. Если в справочнике 100 000 записей, поиск будет медленным. Для таких случаев в 1С предусмотрен Полнотекстовый поиск (ПП). Рассмотрим возможность использования ПолнотекстовыйПоиск.СоздатьСписок(), если быстродействие станет проблемой.

3. Спецсимволы. Если пользователь введет символы % или _, поиск может отработать непредсказуемо. В идеале, перед передачей текста в запрос, его нужно очищать от служебных символов 1С или использовать ключевое слово СПЕЦСИМВОЛ в запросе.

4. Улучшение визуализации. Чтобы пользователю было легче ориентироваться, в СписокВыбора можно добавлять не просто ссылку, а строку-представление, включающую ИНН или Код. Используйте метод СписокВыбора.Добавить(Ссылка, Представление).

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

← На главную