Как выполнить поиск в таблице значений по неточному наименованию в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом
← На главную

При разработке на платформе 1С часто возникает задача фильтрации данных в таблице значений (ТЗ). Типовой метод НайтиСтроки() отлично работает, когда нам нужно точное совпадение, но он бессилен, если требуется найти вхождение слова или части строки (поиск по подстроке). В этой статье мы подробно разберем, как реализовать неточный поиск, проанализируем типичные ошибки начинающих разработчиков и рассмотрим наиболее эффективные способы решения этой задачи.

Причина проблемы: типы данных и спецсимволы

Прежде всего, проанализируем ситуацию, с которой столкнулся автор темы. Попытка использовать оператор ПОДОБНО в запросе к временной таблице, созданной из ТЗ, часто заканчивается ошибкой "Поле не найдено" или некорректной работой фильтра. Это происходит по двум причинам:

  1. Несоответствие типов: Если колонка таблицы значений содержит не строку, а ссылку (например, СправочникСсылка.Подразделения), то оператор ПОДОБНО не сможет с ней работать. Этот оператор предназначен исключительно для строковых данных.
  2. Спецсимволы в коде 1С: Разработчики часто путают синтаксис языка запросов SQL и встроенного языка 1С. В запросе символ % является маской поиска, а в функции СтрНайти() он воспринимается как обычный символ "процент".

Способ №1: Использование перебора строк (Для каждого)

Это самый простой и интуитивно понятный способ. Рассмотрим его реализацию. Основная ошибка здесь заключается в попытке найти текст внутри самого объекта-ссылки. Для корректного поиска нам нужно обратиться к строковому представлению — наименованию.

Разберем правильный алгоритм поиска в цикле:


// Предположим, у нас есть ТЗ "КадровыеДанныеСотрудников"
ЗначениеПоиска = "продаж";

Для Каждого СтрокаТЗ Из КадровыеДанныеСотрудников Цикл
    // Важно: Обращаемся к Наименованию, так как Подразделение — это ссылка
    // Используем ВРег для исключения влияния регистра
    Если СтрНайти(ВРег(СтрокаТЗ.Подразделение.Наименование), ВРег(ЗначениеПоиска)) > 0 Тогда
        // Нашли нужную строку, выполняем действия
        Сообщить("Сотрудник найден: " + СтрокаТЗ.Сотрудник);
    КонецЕсли;
КонецЦикла;

Важное замечание по производительности: Обращение "через точку" в цикле (СтрокаТЗ.Подразделение.Наименование) является "плохим тоном" в 1С. Если в таблице 10 000 строк, система выполнит 10 000 обращений к базе данных за наименованием. Чтобы этого избежать, при получении данных (например, через КадровыйУчет.КадровыеДанныеСотрудников), сразу добавляйте поле "Подразделение.Наименование" в список запрашиваемых полей. Тогда в вашей ТЗ уже будет строковая колонка, и поиск станет мгновенным.

Способ №2: Использование запроса к таблице значений

Если нам нужно выполнить сложную фильтрацию или соединить полученные данные с другими таблицами базы данных, использование запроса оправдано. Однако здесь есть важный нюанс: платформа 1С не всегда может автоматически определить типы колонок в переданной таблице значений.

Рассмотрим, как правильно подготовить таблицу и выполнить запрос с оператором ПОДОБНО:


// 1. Создаем типизированную таблицу значений
ТЗДляЗапроса = Новый ТаблицаЗначений;
ТЗДляЗапроса.Колонки.Добавить("Сотрудник", Новый ОписаниеТипов("СправочникСсылка.Сотрудники"));
ТЗДляЗапроса.Колонки.Добавить("ПодразделениеНазвание", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(150)));

// 2. Заполняем её данными
Для Каждого ИсходнаяСтрока Из КадровыеДанныеСотрудников Цикл
    НоваяСтрока = ТЗДляЗапроса.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, ИсходнаяСтрока);
    НоваяСтрока.ПодразделениеНазвание = Строка(ИсходнаяСтрока.Подразделение);
КонецЦикла;

// 3. Выполняем запрос
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВремТабл.Сотрудник,
    |   ВремТабл.ПодразделениеНазвание
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |   &МояТаблица КАК ВремТабл
    |;
    |
    |ВЫБРАТЬ
    |   *
    |ИЗ
    |   ВТ_Данные КАК Данные
    |ГДЕ
    |   Данные.ПодразделениеНазвание ПОДОБНО &МаскаПоиска";

Запрос.УстановитьПараметр("МояТаблица", ТЗДляЗапроса);
Запрос.УстановитьПараметр("МаскаПоиска", "%" + "продаж" + "%");

РезультатЗапроса = Запрос.Выполнить().Выгрузить();

В данном примере мы принудительно создали строковую колонку ПодразделениеНазвание. Только в этом случае оператор ПОДОБНО отработает корректно и без ошибок.

Способ №3: Поиск с использованием построителя запроса или СКД

Если задача стоит в интерактивной фильтрации таблицы значений в форме, часто эффективнее использовать Систему Компоновки Данных (СКД) или объект ПостроительЗапроса. Они позволяют накладывать отборы (в том числе "содержит") без написания сложного программного кода для каждой итерации.

Проанализируем пример с построителем запроса:


Построитель = Новый ПостроительЗапроса;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(КадровыеДанныеСотрудников);
// Добавляем условие
Отбор = Построитель.Отбор.Добавить("Подразделение");
Отбор.ВидСравнения = ВидСравнения.Содержит;
Отбор.Значение = "продаж";
Отбор.Использование = Истина;

Построитель.Выполнить();
ТЗ_Результат = Построитель.Результат.Выгрузить();

Этот метод хорош тем, что ПостроительЗапроса автоматически пытается привести типы и корректно обрабатывает нечеткий поиск в памяти сервера приложений, что часто быстрее, чем ручной перебор в цикле на клиенте.

Подведем итоги

Для эффективного поиска по неточному наименованию в ТЗ следует придерживаться следующих правил:

Выбор конкретного метода зависит от объема данных: для небольших таблиц (до пары сотен строк) вполне подойдет простой цикл, а для больших массивов данных лучше использовать методы с предварительной типизацией и запросом или механизмы СКД.

← На главную