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