Как в 1С 7.7 отфильтровать список справочника по нескольким значениям?

Программист 1С v7.7
← На главную

В процессе работы с платформой 1С:Предприятие 7.7 часто возникает задача отобразить в форме списка справочника не все элементы, а только те, что удовлетворяют определенным условиям. Стандартный метод УстановитьОтбор() отлично справляется с фильтрацией по одному значению, но что делать, если нужно отобрать элементы по списку из двух, трех или более значений? Например, показать в справочнике счетов только те, что относятся к типу "Сбер" или "ВТБ".

Рассмотрим, почему прямой отбор по списку значений реквизита не работает и какие существуют правильные и эффективные способы решения этой задачи.

Анализ проблемы: почему не работает простой отбор по списку?

Первая мысль, которая приходит в голову, — это создать СписокЗначений, добавить в него нужные нам значения (например, из перечисления) и как-то применить этот список к форме. Автор в исходной теме попытался сделать именно так:


// Этот код не сработает так, как ожидается
СписОтбора = СоздатьОбъект("СписокЗначений");
СписОтбора.ДобавитьЗначение(Перечисление.ТипСчета.Сбер);
СписОтбора.ДобавитьЗначение(Перечисление.ТипСчета.ВТБ);
ИспользоватьСписокЭлементов(СписОтбора);

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

Следовательно, наша задача сводится к тому, чтобы сначала найти все элементы справочника, которые удовлетворяют нашим условиям (тип счета "Сбер" или "ВТБ"), собрать ссылки на них в СписокЗначений и уже этот итоговый список передать в метод ИспользоватьСписокЭлементов(). Рассмотрим два основных способа, как это сделать.

Решение 1: Перебор всех элементов справочника (простой, но медленный способ)

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

Разберем по шагам:

  1. Создаем пустой объект СписокЗначений. В него мы будем складывать подходящие нам элементы.
  2. Получаем выборку всех элементов нужного справочника (например, "Счета").
  3. В цикле обходим каждый элемент этой выборки.
  4. Внутри цикла проверяем значение реквизита (например, "ТипСчета") текущего элемента. Если оно равно одному из искомых значений (используем оператор ИЛИ), то добавляем в наш список сам элемент — СпрСчета.ТекущийЭлемент().
  5. После завершения цикла применяем к форме метод ИспользоватьСписокЭлементов(), передавая ему наш наполненный список.

Посмотрим на пример кода, к которому пришел автор в обсуждении:


Процедура ПриОткрытии()
    // Итоговый список для ссылок на элементы
    СписОтбора = СоздатьОбъект("СписокЗначений");

    // Создаем объект для работы со справочником
    СпрСчета = СоздатьОбъект("Справочник.Счета");
    СпрСчета.ВыбратьЭлементы();
    
    // Перебираем все элементы справочника
    Пока СпрСчета.ПолучитьЭлемент() = 1 Цикл
        // Проверяем условие для каждого элемента
        Если (СпрСчета.ТипСчета = Перечисление.ТипСчета.Сбер)
        ИЛИ (СпрСчета.ТипСчета = Перечисление.ТипСчета.ВТБ) Тогда
            
            // Если условие выполнено, добавляем ссылку на элемент в список
            СписОтбора.ДобавитьЗначение(СпрСчета.ТекущийЭлемент()); 
            
        КонецЕсли;
    КонецЦикла;

    // Применяем отбор к форме
    ИспользоватьСписокЭлементов(СписОтбора);
КонецПроцедуры

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


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

// ...
Пока СпрСчета.ПолучитьЭлемент() = 1 Цикл
    // Проверяем, входит ли тип счета текущего элемента в наш список
    Если СписТипов.НайтиЗначение(СпрСчета.ТипСчета) <> 0 Тогда
        СписОтбора.ДобавитьЗначение(СпрСчета.ТекущийЭлемент()); 
    КонецЕсли;
КонецЦикла;
// ...

Главный недостаток этого метода — низкая производительность. На справочниках с тысячами и десятками тысяч элементов такой перебор может занимать ощутимое время, так как система вынуждена прочитать и обработать каждый элемент в цикле на языке 1С.

Решение 2: Использование объекта "Запрос" (рекомендуемый и производительный способ)

Наиболее правильный, быстрый и масштабируемый способ выборки данных по сложным условиям — это использование объекта Запрос. Запрос выполняется на уровне внутренних механизмов платформы, что значительно быстрее, чем перебор элементов в цикле.

Алгоритм действий следующий:

  1. Создаем СписокЗначений, в который помещаем значения для условия отбора (наши перечисления "Сбер" и "ВТБ").
  2. Формируем текст запроса. В запросе мы выбираем элементы справочника, а в секции Условие используем конструкцию (ИмяРеквизита В МойСписокЗначений). Эта конструкция — мощный инструмент, который позволяет фильтровать записи по вхождению значения в список.
  3. Выполняем запрос.
  4. Результат запроса (уже отфильтрованные элементы) выгружаем в итоговый СписокЗначений.
  5. Применяем этот список к форме через ИспользоватьСписокЭлементов().

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


Процедура ПриОткрытии()
    // 1. Список значений для условия в запросе
    СписТипов = СоздатьОбъект("СписокЗначений");
    СписТипов.ДобавитьЗначение(Перечисление.ТипСчета.Сбер);
    СписТипов.ДобавитьЗначение(Перечисление.ТипСчета.ВТБ);
    
    // Итоговый список, куда поместим найденные элементы
    СписОтбора = СоздатьОбъект("СписокЗначений");
    
    // 2. Формирование текста запроса
    Запрос = СоздатьОбъект("Запрос");
    ТекстЗапроса = "
    |Счет = Справочник.Счета.ТекущийЭлемент;
    |ТипСчета = Справочник.Счета.ТипСчета;
    |// Здесь происходит магия: отбираем только те элементы,
    |// у которых реквизит ТипСчета есть в списке СписТипов
    |Условие (ТипСчета В СписТипов);
    |Группировка Счет;
    |";
    
    // 3. Выполнение запроса
    Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
        // 4. Выгрузка результата в список
        Пока Запрос.Группировка("Счет") = 1 Цикл
            СписОтбора.ДобавитьЗначение(Запрос.Счет);
        КонецЦикла;
    КонецЕсли;
    
    // 5. Применение фильтра к форме
    ИспользоватьСписокЭлементов(СписОтбора);
КонецПроцедуры

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

Дополнительные рекомендации

← На главную