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