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