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