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