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