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

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

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

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

Типичные ошибки при работе со списком значений в запросе

Прежде чем перейти к правильным вариантам, проанализируем ошибки, которые часто допускают разработчики при передаче списков в параметры запроса:

  1. Вложение коллекции внутрь другой коллекции: попытка создать новый список и добавить в него существующий реквизит формы через метод Список.Добавить(РеквизитСписка) приводит к тому, что создается список значений из одного элемента, значением которого является исходный список. В результате проверка Количество() > 0 всегда возвращает Истина, а в запрос передается вложенная коллекция, вызывающая ошибки сравнения типов.
  2. Попытка прямого сравнения списка со ссылкой в тексте запроса: конструкция вида КОГДА &Список = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка) приводит к исключению: «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов», поскольку объект СписокЗначений нельзя сравнивать со ссылочным типом.
  3. Ошибки приоритета логических операций: при объединении условий через ИЛИ и И без явных скобок логическое умножение И выполняется раньше, чем логическое сложение ИЛИ, что приводит к некорректной фильтрации.

Способ 1. Условная конструкция ВЫБОР внутри запроса

Рассмотрим вариант, при котором текст запроса остается статичным, а признак наличия отбора передается в виде отдельного булевого параметра. Это позволяет использовать конструктор запросов без динамической сборки текста.

Посмотрим на пример кода запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	СписаниеТоваровТовары.Номенклатура КАК Номенклатура,
    |	СписаниеТоваровТовары.Количество КАК Количество
    |ИЗ
    |	Документ.СписаниеТоваров.Товары КАК СписаниеТоваровТовары
    |ГДЕ
    |	ВЫБОР
    |		КОГДА &ЕстьОтборПоНоменклатуре
    |			ТОГДА СписаниеТоваровТовары.Номенклатура В (&СписокТоваров)
    |		ИНАЧЕ ИСТИНА
    |	КОНЕЦ";

// Проверяем, заполнен ли список пользователем
ЕстьОтбор = (СписокТоваров.Количество() > 0);

Запрос.УстановитьПараметр("ЕстьОтборПоНоменклатуре", ЕстьОтбор);
Запрос.УстановитьПараметр("СписокТоваров", СписокТоваров);

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

Разберем логику: если параметр &ЕстьОтборПоНоменклатуре равен Истина, проверяется вхождение в переданный список. Если флаг равен Ложь, выражение ВЫБОР возвращает ИСТИНА, и условие не отсекает строки.

Способ 2. Логическое условие с оператором ИЛИ

Похожий подход без использования конструкции ВЫБОР — использование логической связки с флагом пустого списка. При использовании нескольких таких отборов важно обязательно группировать условия скобками.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	СписаниеТоваровТовары.Номенклатура КАК Номенклатура,
    |	СписаниеТоваровТовары.Ссылка.Подразделение КАК Подразделение,
    |	СписаниеТоваровТовары.Количество КАК Количество
    |ИЗ
    |	Документ.СписаниеТоваров.Товары КАК СписаниеТоваровТовары
    |ГДЕ
    |	СписаниеТоваровТовары.Ссылка.Проведен = ИСТИНА
    |	И (СписаниеТоваровТовары.Номенклатура В (&СписокТоваров) ИЛИ &СписокТоваровПуст)
    |	И (СписаниеТоваровТовары.Ссылка.Подразделение В (&СписокПодразделений) ИЛИ &СписокПодразделенийПуст)";

Запрос.УстановитьПараметр("СписокТоваров", ТоварыСп);
Запрос.УстановитьПараметр("СписокТоваровПуст", ТоварыСп.Количество() = 0);

Запрос.УстановитьПараметр("СписокПодразделений", ПодразделениеСп);
Запрос.УстановитьПараметр("СписокПодразделенийПуст", ПодразделениеСп.Количество() = 0);

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

Способ 3. Динамическое формирование текста запроса

С точки зрения производительности СУБД конструкции с ИЛИ и ВЫБОР в секции ГДЕ могут приводить к неоптимальным планам выполнения (сканированию таблиц вместо поиска по индексам). Поэтому для больших объемов данных рекомендуется динамически добавлять условие в текст запроса только при наличии заполненного отбора.

Разберем пример динамической сборки текста:


ТекстЗапроса = 
    "ВЫБРАТЬ
    |	РеализацияТоваровТовары.Номенклатура КАК Номенклатура,
    |	РеализацияТоваровТовары.Количество КАК Количество,
    |	РеализацияТоваровТовары.Цена КАК Цена
    |ИЗ
    |	Документ.РеализацияТоваров.Товары КАК РеализацияТоваровТовары
    |ГДЕ
    |	РеализацияТоваровТовары.Ссылка.Проведен = ИСТИНА";

Если СписокТоваров.Количество() > 0 Тогда
    ТекстЗапроса = ТекстЗапроса + "
    |	И РеализацияТоваровТовары.Номенклатура В (&СпНоменклатуры)";
КонецЕсли;

Запрос = Новый Запрос(ТекстЗапроса);

Если СписокТоваров.Количество() > 0 Тогда
    Запрос.УстановитьПараметр("СпНоменклатуры", СписокТоваров);
КонецЕсли;

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

Особенности проверки заполненности списков

Выясним важные нюансы, с которыми можно столкнуться при проверке заполненности реквизитов формы:

  1. Элементы с отметками: если в интерфейсе используется список значений с возможностью установки флажков, метод Количество() вернет общее число строк в списке, даже если ни одна галочка не проставлена. В таком случае перед формированием запроса необходимо обойти список и отобрать только те элементы, у которых свойство Отметка равно Истина.
  2. Функция ЗначениеЗаполнено(): для объекта типа СписокЗначений данная функция возвращает Истина, если в списке есть хотя бы один элемент, даже если само значение этого элемента пустое (например, пустая ссылка).
  3. Большие списки значений: если передаваемый список содержит тысячи элементов, конструкция В (&Параметр) может вызвать превышение допустимого лимита параметров СУБД. В таких случаях рекомендуется выгружать список во временную таблицу и соединять ее с основной выборкой через ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
← На главную