Как реализовать фильтр в динамическом списке для поиска элементов, содержащих все значения из списка

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

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

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

Метод 1. Сравнение количества различных вхождений (Группировка и условие ИМЕЮЩИЕ)

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

Проанализируем шаги реализации:

  1. Передаем в запрос массив (или список значений) и отдельно — количество элементов в этом массиве.
  2. В тексте запроса соединяем основную таблицу справочника с его табличной частью.
  3. Устанавливаем фильтр ГДЕ, чтобы оставить только те строки табличной части, которые входят в наш список.
  4. Группируем результат по ссылке и проверяем условие через ИМЕЮЩИЕ.

Рассмотрим пример кода запроса для динамического списка:


ВЫБРАТЬ
    СправочникОбъект.Ссылка КАК Ссылка,
    СправочникОбъект.Наименование КАК Наименование
ИЗ
    Справочник.МойСправочник КАК СправочникОбъект
ГДЕ
    СправочникОбъект.Ссылка В (
        ВЫБРАТЬ
            ТЧ.Ссылка
        ИЗ
            Справочник.МойСправочник.МояТабличнаяЧасть КАК ТЧ
        ГДЕ
            ТЧ.РеквизитВТЧ В (&СписокПараметр)
        ГРУППИРОВАТЬ ПО
            ТЧ.Ссылка
        ИМЕЮЩИЕ
            КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТЧ.РеквизитВТЧ) = &КоличествоЭлементов
    )

Важный нюанс: Перед передачей параметров обязательно убедитесь, что в исходном списке значений (массиве) нет дублей, иначе условие КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) может не совпасть с числом элементов в параметре &КоличествоЭлементов.

Метод 2. Реализация через двойное отрицание (Реляционное деление)

Если необходимо реализовать проверку без использования группировок (что иногда может быть быстрее на очень больших объемах данных), можно применить логику реляционного деления. Мы формулируем условие так: «Выбрать те элементы, для которых не существует такого значения в искомом списке, которого нет в табличной части этого элемента».

Посмотрим, как это выглядит в структуре запроса:


ВЫБРАТЬ
    СправочникОбъект.Ссылка
ИЗ
    Справочник.МойСправочник КАК СправочникОбъект
ГДЕ
    НЕ СправочникОбъект.Ссылка В (
        ВЫБРАТЬ
            ВспомогательнаяТаблица.Ссылка
        ИЗ
            (ВЫБРАТЬ &СписокПараметр КАК Значение) КАК ВТ_Параметры
            ЛЕВОЕ СОЕДИНЕНИЕ Справочник.МойСправочник.МояТабличнаяЧасть КАК ТЧ
            ПО ВТ_Параметры.Значение = ТЧ.РеквизитВТЧ
        ГДЕ
            ТЧ.Ссылка ЕСТЬ NULL
    )

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

Метод 3. Программная установка отбора через массив ссылок

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

Разберем пример процедуры для модуля формы:


&НаСервере
Процедура ОбновитьОтборНаСервере()
    
    // 1. Формируем запрос для получения списка подходящих ссылок
    ТекстЗапроса = 
    "ВЫБРАТЬ
    |	ТЧ.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.МойСправочник.МояТабличнаяЧасть КАК ТЧ
    |ГДЕ
    |	ТЧ.РеквизитВТЧ В (&Список)
    |ГРУППИРОВАТЬ ПО
    |	ТЧ.Ссылка
    |ИМЕЮЩИЕ
    |	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТЧ.РеквизитВТЧ) = &Колво";
    
    Запрос = Новый Запрос(ТекстЗапроса);
    Запрос.УстановитьПараметр("Список", Объект.НужныйСписокЗначений);
    Запрос.УстановитьПараметр("Колво", Объект.НужныйСписокЗначений.Количество());
    
    Результат = Запрос.Выполнить().Выгрузить();
    МассивСсылок = Результат.ВыгрузитьКолонку("Ссылка");
    
    // 2. Устанавливаем отбор в динамический список
    ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбора(
        Список.КомпоновщикНастроек.Настройки.Отбор, 
        "Ссылка", 
        МассивСсылок, 
        ВидСравненияКомпоновкиДанных.ВСписке, 
        "Фильтр по набору", 
        Истина
    );
    
КонецПроцедуры

Внимание: У этого способа есть ограничение. Если подходящих элементов будет слишком много (более нескольких тысяч), платформа может выдать ошибку СУБД из-за слишком длинного списка значений в условии IN. В таких случаях используйте Метод 1.

Нюансы работы с динамическим списком

При работе с произвольными запросами в динамических списках важно помнить о производительности. Проанализируем несколько советов:

Рассмотрим, как правильно передать параметры в форму:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    КоличествоЭлементов = СписокЗначенийФильтра.Количество();
    
    Список.Параметры.УстановитьЗначениеПараметра("СписокПараметр", СписокЗначенийФильтра);
    Список.Параметры.УстановитьЗначениеПараметра("КоличествоЭлементов", КоличествоЭлементов);
    
КонецПроцедуры

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

← На главную