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