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

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

В процессе разработки отчетов или обработок на платформе 1С:Предприятие программисты часто сталкиваются с двумя классическими проблемами при работе с запросами. Первая — запрос перестает возвращать данные, если в параметр-список (например, список складов) не передано ни одного значения. Вторая — необходимость динамически менять логику запроса в зависимости от выбранного пользователем вида сравнения (например, «Равно» или «В списке»).

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

Проблема пустого списка в условии запроса

Рассмотрим стандартную ситуацию: в тексте запроса используется конструкция Поле В (&Список). Если в параметр &Список передать пустой объект СписокЗначений, то результат выполнения условия всегда будет ЛОЖЬ. Система интерпретирует это так: «выбрать записи, где Склад равен ничему из пустоты». В итоге весь запрос может не вернуть ни одной строки, даже если другие фильтры заполнены корректно.

Проанализируем первый способ решения — использование логического флага в условии запроса. Мы добавим дополнительный параметр, который будет «отключать» фильтр, если список пуст.


Запросост = Новый Запрос;
// Предположим, ВыбСклад — это СписокЗначений с формы
Запросост.УстановитьПараметр("Склад", ВыбСклад);
// Устанавливаем флаг: Истина, если список пуст
Запросост.УстановитьПараметр("НеФильтроватьСклады", ВыбСклад.Количество() = 0);

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

В данном примере, если список складов пуст, параметр &НеФильтроватьСклады примет значение Истина. Благодаря логическому оператору ИЛИ, всё условие целиком станет истинным для каждой строки, и фильтрация по складу просто проигнорируется.

Динамическое изменение вида сравнения

Часто требуется, чтобы один и тот же реквизит на форме мог работать в разных режимах: как одиночная ссылка (вид сравнения «Равно») или как список (вид сравнения «В списке»). Хотя современные версии платформы 8.3 позволяют оператору В корректно обрабатывать одиночную ссылку как список из одного элемента, иногда требуется жестко менять текст запроса для оптимизации или специфической логики.

Рассмотрим вариант с использованием метода СтрЗаменить для подстановки нужного оператора сравнения. Проанализируем пример кода:


// В тексте запроса используем шаблон-заполнитель [УсловиеСклада]
ТекстЗапроса = 
"ВЫБРАТЬ * ИЗ РегистрНакопления.ТоварыОрганизаций.Остатки 
|ГДЕ Склад [УсловиеСклада]";

Если ВидСравненияНаФорме = ВидСравнения.Равно Тогда
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "[УсловиеСклада]", "= &Склад");
    Запрос.УстановитьПараметр("Склад", ВыбСкладСсылка); // Одиночная ссылка
ИначеЕсли ВидСравненияНаФорме = ВидСравнения.ВСписке Тогда
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "[УсловиеСклада]", "В (&Склад)");
    Запрос.УстановитьПараметр("Склад", ВыбСкладСписок); // Список значений
КонецЕсли;

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

Оптимизация: параметры виртуальных таблиц

Важно помнить о производительности. Проанализируем критическую ошибку: установка фильтров в секции ГДЕ для виртуальных таблиц остатков или оборотов. Если мы накладываем условие в ГДЕ, 1С сначала получит остатки по всем складам и номенклатурам из базы, и только потом начнет их фильтровать. На больших базах это приведет к серьезным тормозам.

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


ТекстУсловия = "";
Если ЗначениеЗаполнено(ВыбСклад) Тогда
    // Если это список значений и он не пуст, или если это заполненная ссылка
    ТекстУсловия = "Склад В (&Склад)";
КонецЕсли;

// Подставляем условие непосредственно в параметры виртуальной таблицы
Запрос.Текст = СтрЗаменить(
    "ВЫБРАТЬ * ИЗ РегистрНакопления.ТоварыОрганизаций.Остатки(&Период, &Условие)",
    "&Условие",
    ?(ТекстУсловия = "", "", ТекстУсловия)
);

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

Использование построителя запроса и СКД

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

  1. СКД автоматически управляет всеми видами сравнения, правильно обрабатывает пустые значения и, что самое важное, оптимально подставляет условия в параметры виртуальных таблиц.
  2. ПостроительЗапроса позволяет программно добавлять отборы, которые автоматически преобразуются в корректный SQL-код. Это избавляет разработчика от ручного манипулирования строками через СтрЗаменить.

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


Если ЗначениеЗаполнено(ВыбСклад) Тогда
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "//ВСТАВИТЬ_УСЛОВИЕ", "И Склад В (&Склад)");
    Запрос.УстановитьПараметр("Склад", ВыбСклад);
Иначе
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "//ВСТАВИТЬ_УСЛОВИЕ", "");
КонецЕсли;

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

← На главную