Как в СКД применить пользовательский отбор только к результирующей выборке?

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

При разработке отчетов на базе системы компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда в многоэтажном запросе используется несколько обращений к одной и той же таблице (например, к регистру сведений ЗначенияСвойствОбъектов) через левые соединения. Пока отчет формируется без условий, данные выводятся корректно. Но как только пользователь устанавливает отбор по одному из дополнительных реквизитов, из отчета пропадают все остальные строки.

Выясним причину такого поведения и разберем практические способы решения этой проблемы.

Почему СКД ломает связи в запросе

По умолчанию в СКД включен механизм Автозаполнения. Генератор макета компоновки данных анализирует структуру запроса и стремится оптимизировать его исполнение на стороне СУБД. Когда пользователь добавляет отбор в шапке отчета, СКД пытается «протолкнуть» это условие как можно глубже: во вложенные подзапросы, параметры виртуальных таблиц и секции ГДЕ.

Если условие накладывается на поле таблицы, присоединенной через ЛЕВОЕ СОЕДИНЕНИЕ, добавление фильтра в секцию ГДЕ фактически превращает левое соединение во внутреннее. Строки, для которых значение присоединенного свойства равно NULL, отсекаются, и результирующая выборка становится пустой или неполной.

Способ 1. Использование механизма вычисляемых полей

Самый простой способ исключить автоматическую оптимизацию запроса по конкретному полю — вынести его в вычисляемые поля СКД. В этом случае компоновщик рассчитывает значение уже после получения данных из базы и накладывает отбор на уровне процессора компоновки.

Разберем по шагам:

  1. Перейдем на вкладку Наборы данных и найдем исходное поле запроса (например, ЗначениеСвойства1).
  2. В свойствах поля найдем колонку Ограничение поля и установим флаг Запретить отбор (или снимем доступность отбора). Это запретит СКД подставлять условие в текст SQL-запроса.
  3. Перейдем на вкладку Вычисляемые поля и добавим новую строку.
  4. В поле Путь к данным укажем имя, понятное пользователю, а в поле Выражение пропишем имя исходного поля запроса (например, ЗначениеСвойства1).
  5. В настройках отчета предоставим пользователю возможность отбора именно по созданному вычисляемому полю.

Способ 2. Перенос отбора в группировку «Детальные записи»

Поведение фильтрации в СКД напрямую зависит от места, где задан отбор:

  1. Отбор, расположенный в корне отчета (на вкладке Настройки в верхнем элементе Отчет), транслируется в секцию ГДЕ исходного запроса к базе данных.
  2. Отбор, заданный внутри конкретной группировки (например, в строке Детальные записи или группировке верхнего уровня), обрабатывается процессором компоновки данных уже после получения всех записей из базы.

Для реализации этого метода перейдем в настройки структуры отчета, выделим группировку Детальные записи, откроем вкладку Отбор и добавим нужные поля туда. Чтобы пользователю было удобно с ними работать, вынесем эти элементы в Пользовательские настройки с быстрым доступом на форму отчета.

Способ 3. Разрыв связи с полями источника через выражения языка запросов

Генератор макета СКД анализирует структуру запроса и связывает поля выборки с реальными полями таблиц базы данных. Если разорвать прямую связь поля в тексте запроса с помощью функций и выражений, СКД не сможет транслировать пользовательский отбор во вложенные подзапросы.

Посмотрим на примеры таких выражений в секции ВЫБРАТЬ:


ВЫБРАТЬ
    ЗаказКлиента.Ссылка КАК Ссылка,
    ВЫБОР 
        КОГДА ИСТИНА ТОГДА ТаблицаСвойств.Значение 
        ИНАЧЕ NULL 
    КОНЕЦ КАК ЗначениеСвойства,
    ЕСТЬNULL(ТаблицаСвойств.Значение, "") КАК ЗначениеСвойстваСтрока
ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ТаблицаСвойств
        ПО ЗаказКлиента.Ссылка = ТаблицаСвойств.Объект

Обертывание поля в конструкцию ВЫБОР...КОНЕЦ или вызов функции ЕСТЬNULL заставляет СКД рассматривать результирующее поле как расчетное выражение на уровне запроса, что предотвращает некорректное преобразование левого соединения.

Способ 4. Программное заполнение параметров в событии «ПриКомпоновкеРезультата»

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

Рассмотрим структуру запроса:


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

В модуле отчета переопределим стандартный обработчик ПриКомпоновкеРезультата:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Истина;
    
    // Получаем текущие настройки отчета
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    // Считываем пользовательский отбор и при необходимости заполняем параметры
    Для Каждого ЭлементОтбора Из Настройки.Отбор.Элементы Цикл
        Если ТипЗнч(ЭлементОтбора) = Тип("ЭлементОтбораКомпоновкиДанных") 
            И ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ЗаказУслуг") 
            И ЭлементОтбора.Использование Тогда
            
            КомпоновщикНастроек.Настройки.ПараметрыДанных.УстановитьЗначениеПараметра(
                "ОтборЗаказыУслуг", 
                ЭлементОтбора.ПравоеЗначение
            );
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Способ 5. Разделение на несколько наборов данных

Проанализируем еще одну архитектурную альтернативу: отказ от левых соединений в одном монолитном запросе в пользу объединения нескольких наборов данных внутри СКД.

  1. Создаем Набор данных 1 (Запрос): выбираем базовые данные (например, документы или справочники).
  2. Создаем Набор данных 2 (Запрос): выбираем таблицу дополнительных свойств объектов.
  3. Переходим на вкладку Связи наборов данных и связываем Набор 1 и Набор 2 по полю Ссылка = Объект.

В такой схеме СКД выполняет левое соединение на уровне процессора компоновки данных. Пользовательский отбор по реквизитам первого набора данных никак не сможет нарушить логику связывания со вторым набором.

← На главную