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