При разработке отчетов в системе 1С:Предприятие часто возникает задача реализации гибкого фильтра. Классический сценарий: пользователь может выбрать несколько подразделений (или других элементов справочника) для детализации отчета, но если он оставляет поле выбора пустым, система должна автоматически сформировать отчет по всем доступным подразделениям. В старых конфигурациях на обычных формах эта задача требует особого внимания к коду и тексту запроса.
Рассмотрим подробно несколько способов решения этой задачи, проанализируем их плюсы и минусы, а также разберем типичные ошибки начинающих разработчиков.
Разберем типичную ситуацию. В запросе используется условие: ГДЕ Док.Подразделение В (&Подразделение). Если в параметр &Подразделение передать пустой СписокЗначений, то с точки зрения SQL это условие превратится в нечто, что всегда возвращает ЛОЖЬ. В итоге отчет получится пустым, даже если данные в базе есть. Наша задача — сделать условие "прозрачным", если фильтр не задан.
Этот метод считается наиболее изящным, так как он позволяет не менять текст запроса динамически в коде. Мы добавляем в условие проверку на "пустоту" параметра. Рассмотрим, как это выглядит в тексте запроса:
|ГДЕ
| (&НеИспользоватьОтбор ИЛИ Док.Подразделение В (&Подразделение))
Теперь проанализируем, как подготовить параметры перед выполнением запроса в модуле отчета:
// Предположим, что "ПодразделенияНаФорме" — это СписокЗначений с формы
Запрос.УстановитьПараметр("Подразделение", ПодразделенияНаФорме);
// Если список пуст, флаг устанавливается в ИСТИНА, и условие "В" игнорируется
Запрос.УстановитьПараметр("НеИспользоватьОтбор", ПодразделенияНаФорме.Количество() = 0);
В этом случае, если пользователь ничего не выбрал, выражение &НеИспользоватьОтбор станет равным ИСТИНА. Согласно правилам логики, все условие в скобках станет истинным для каждой строки таблицы, и фильтрация не произойдет.
Если вы хотите избежать лишних проверок внутри самого SQL-запроса (что актуально для высоконагруженных систем), можно прибегнуть к модификации текста запроса непосредственно перед его выполнением. Этот метод позволяет полностью удалить секцию ГДЕ, если отбор не требуется.
Рассмотрим пример реализации:
ТекстЗапроса =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.СуммаДокумента КАК Сумма
|ИЗ
| Документ.ЗаказПокупателя КАК Док
|{ГДЕ_ОТБОР}"; // Метка-заполнитель
Если ПодразделенияНаФорме.Количество() > 0 Тогда
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{ГДЕ_ОТБОР}", "ГДЕ Док.Подразделение В (&Подразделение)");
Запрос.УстановитьПараметр("Подразделение", ПодразделенияНаФорме);
Иначе
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{ГДЕ_ОТБОР}", "");
КонецЕсли;
Запрос.Текст = ТекстЗапроса;
Такой подход делает запрос максимально эффективным для СУБД, так как в план выполнения не попадают лишние логические условия.
В обычных формах (особенно в "древних" конфигурациях) часто используется ПостроительОтчетов. Он обладает встроенным механизмом работы с отборами, который сам умеет обрабатывать пустые значения. Проанализируем, как это работает:
Подразделение в текст запроса построителя.
ПостроительОтчетов.Текст = "ВЫБРАТЬ ... ИЗ Документ.Продажи КАК Продажи {ГДЕ Продажи.Подразделение.*}";
ПостроительОтчетов.ЗаполнитьНастройки();
// Если пользователь выбрал значения в списке на форме:
Если ПодразделенияНаФорме.Количество() > 0 Тогда
Отбор = ПостроительОтчетов.Отбор.Подразделение;
Отбор.Использование = Истина;
Отбор.ВидСравнения = ВидСравнения.ВСписке;
Отбор.Значение = ПодразделенияНаФорме;
КонецЕсли;
Если условие ПодразделенияНаФорме.Количество() > 0 не выполнится, построитель просто не включит фильтр по подразделениям в итоговый SQL-запрос. Это самый "правильный" путь для старых конфигураций 1С.
Разберем ситуацию, упомянутую в обсуждении, когда разработчик пытается вручную перебрать справочник и загрузить все элементы в список, если пользователь ничего не выбрал:
// ОШИБОЧНЫЙ ПОДХОД
Спр = Справочники.Подразделения.Выбрать();
Пока Спр.Следующий() Цикл
Подразделение.ЗагрузитьЗначения(Спр.Ссылка); // Ошибка: метод ЗагрузитьЗначения очищает список на каждой итерации!
КонецЦикла;
Почему это плохо:
ЗагрузитьЗначения ожидает массив или другой список, он не предназначен для добавления одиночных элементов в цикле.IN (...).Проанализируем работу с виртуальными таблицами. Если ваш отчет строится по регистрам накопления, крайне важно устанавливать параметры отбора в параметрах виртуальной таблицы, а не в секции ГДЕ.
Сравним два варианта:
Вариант А (Медленный):
ИЗ РегистрНакопления.Продажи.Обороты() КАК Продажи ГДЕ Продажи.Подразделение В (&Список)
Вариант Б (Быстрый):
ИЗ РегистрНакопления.Продажи.Обороты(,,, Подразделение В (&Список)) КАК Продажи
Во втором случае платформа передаст фильтр напрямую в SQL-запрос к итогам, что значительно ускорит формирование отчета, особенно если данных в базе много. Помните: правильная работа с типами данных и структурой запроса — залог стабильной работы вашей конфигурации.