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

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

В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с задачей фильтрации данных в табличной части. Стандартный механизм ОтборСтрок (RowFilter) отлично работает, когда нужно найти точное совпадение в одной колонке. Однако, когда возникает задача найти значение (например, фамилию водителя), которое может находиться в любой из четырех разных колонок (Водитель 1, Водитель 2 и т.д.), мы упираемся в ограничение платформы: свойство ОтборСтрок принимает ФиксированнуюСтруктуру, которая объединяет условия только по логическому «И». Нам же требуется логическое «ИЛИ».

В этой статье мы подробно разберем три основных способа решения этой задачи, проанализируем их плюсы и минусы, а также научимся писать код для каждого случая.

Метод 1. Использование вспомогательной скрытой колонки для поиска

Рассмотрим самый популярный и производительный метод для обычных табличных частей. Суть его заключается в создании «индекса» — специальной текстовой колонки, в которой будут собраны строковые представления всех водителей из текущей строки.

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

  1. Добавим в табличную часть СоставПлана новый реквизит СтрокаПоиска (тип Строка, длина 500).
  2. На форме для этого поля установим свойство Видимость в значение Ложь.
  3. При заполнении или изменении данных в строке будем «склеивать» значения всех четырех колонок в это поле.

Проанализируем код, который необходимо разместить в модуле формы для обновления этого поля:


&НаСервере
Процедура ОбновитьСтрокуПоиска(СтрокаТаблицы)
    // Собираем наименования всех водителей в одну строку
    ТекстПоиска = "";
    Если ЗначениеЗаполнено(СтрокаТаблицы.Водитель_1) Тогда
        ТекстПоиска = ТекстПоиска + Строка(СтрокаТаблицы.Водитель_1) + " ";
    КонецЕсли;
    Если ЗначениеЗаполнено(СтрокаТаблицы.Водитель_2) Тогда
        ТекстПоиска = ТекстПоиска + Строка(СтрокаТаблицы.Водитель_2) + " ";
    КонецЕсли;
    // ... повторить для всех необходимых колонок
    
    СтрокаТаблицы.СтрокаПоиска = СокрЛП(ТекстПоиска);
КонецПроцедуры

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

Метод 2. Фильтрация через вспомогательный флаг видимости

Если нам нужно искать именно по ссылке (например, когда в поле поиска выбран конкретный элемент справочника Водители), лучше использовать метод с булевым флагом. Этот способ позволяет избежать проблем с одинаковыми фамилиями в текстовом поиске.

Рассмотрим алгоритм действий:

  1. Добавим в табличную часть реквизит ПодходитПодФильтр с типом Булево.
  2. В обработчике события ПриИзменении поля поиска напишем цикл, который проставит этот флаг для каждой строки.
  3. Установим отбор по этому флагу.

Проанализируем пример кода:


&НаКлиенте
Процедура ПоискПоВодителюПриИзменении(Элемент)
    
    ПараметрПоиска = ПоискПоВодителю; // Ссылка на выбранного водителя
    
    Для Каждого СтрокаТЧ Из Объект.СоставПлана Цикл
        // Проверяем наличие водителя в любой из 4 колонок
        Если Не ЗначениеЗаполнено(ПараметрПоиска) Тогда
            СтрокаТЧ.ПодходитПодФильтр = Истина;
        Иначе
            Условие = (СтрокаТЧ.Водитель_1 = ПараметрПоиска) 
                   ИЛИ (СтрокаТЧ.Водитель_2 = ПараметрПоиска)
                   ИЛИ (СтрокаТЧ.Водитель_3 = ПараметрПоиска)
                   ИЛИ (СтрокаТЧ.Водитель_4 = ПараметрПоиска);
            
            СтрокаТЧ.ПодходитПодФильтр = Условие;
        КонецЕсли;
    КонецЦикла;
    
    // Накладываем визуальный отбор
    Если ЗначениеЗаполнено(ПараметрПоиска) Тогда
        Элементы.СоставПлана.ОтборСтрок = Новый ФиксированнаяСтруктура("ПодходитПодФильтр", Истина);
    Иначе
        Элементы.СоставПлана.ОтборСтрок = Неопределено;
    КонецЕсли;
    
КонецПроцедуры

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

Метод 3. Переход на Динамический список

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

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

Посмотрим на пример программного формирования такого отбора в динамическом списке:


&НаСервере
Процедура УстановитьОтборИлиНаСервере(ЗначениеПоиска)
    
    Список = Элементы.СоставПланаДинСписок; // Предположим, мы используем дин. список
    Список.Отбор.Элементы.Очистить();
    
    Если Не ЗначениеЗаполнено(ЗначениеПоиска) Тогда
        Возврат;
    КонецЕсли;
    
    // Создаем группу "ИЛИ"
    ГруппаИЛИ = Список.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
    ГруппаИЛИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
    
    // Добавляем условия для каждой колонки
    Для Номер = 1 По 4 Цикл
        ИмяПоля = "Водитель_" + Номер;
        ЭлементОтбора = ГруппаИЛИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(ИмяПоля);
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
        ЭлементОтбора.ПравоеЗначение = ЗначениеПоиска;
        ЭлементОтбора.Использование = Истина;
    КонецЦикла;
    
КонецПроцедуры

Этот способ является «золотым стандартом» 1С, так как он обеспечивает максимальную производительность и корректность работы интерфейса при больших объемах данных.

Заключение и рекомендации

Подводя итоги нашего анализа, дадим несколько практических советов:

Помните, что при использовании программного изменения данных в табличной части (Методы 1 и 2), необходимо следить за тем, чтобы ОтборСтрок обновлялся каждый раз после модификации данных пользователем. Для этого используйте событие ПриИзменении соответствующих колонок водителей.

← На главную