Как в СКД сохранить вывод всех строк ведущей таблицы при отборе по присоединенной внешней таблице?

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

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

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

Причина проблемы: почему СКД отсекает пустые строки

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

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

Способ 1. Использование Менеджера временных таблиц в СКД

Наиболее производительный и гибкий способ — отказаться от набора данных типа «Объект» в пользу передачи внешней таблицы через МенеджерВременныхТаблиц прямо в единый пакетный запрос схемы компоновки данных.

Разберем по шагам, как это реализовать:

  1. В коде модуля создаем МенеджерВременныхТаблиц и помещаем в него таблицу значений обычным запросом.
  2. В СКД создаем единственный Набор данных - Запрос, содержащий пакет запросов с предварительной фильтрацией и нужным порядком соединений.
  3. При инициализации процессора компоновки передаем менеджер временных таблиц в качестве параметра.

Посмотрим на пример текста запроса для схемы компоновки данных:


ВЫБРАТЬ
    ТЗ.Контрагент КАК Контрагент,
    ТЗ.Характеристика КАК Характеристика
ПОМЕСТИТЬ ВТ_Внешняя
ИЗ
    &ВнешняяТаблица КАК ТЗ
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ПродажиОбороты.Контрагент КАК Контрагент,
    ВТ_Внешняя.Характеристика КАК ХарактеристикаКонтрагента
ПОМЕСТИТЬ ВТ_ПродажиСОтбором
ИЗ
    РегистрНакопления.Продажи.Обороты КАК ПродажиОбороты
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Внешняя КАК ВТ_Внешняя
        ПО ПродажиОбороты.Контрагент = ВТ_Внешняя.Контрагент
ГДЕ
    ВТ_Внешняя.Характеристика = &НужнаяХарактеристика
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    спрНоменклатура.Ссылка КАК Номенклатура,
    Продажи.Контрагент КАК Контрагент,
    Продажи.ХарактеристикаКонтрагента КАК ХарактеристикаКонтрагента
ИЗ
    Справочник.Номенклатура КАК спрНоменклатура
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПродажиСОтбором КАК Продажи
        ПО спрНоменклатура.Ссылка = Продажи.Номенклатура

Программная инициализация формирования отчета с передачей менеджера временных таблиц выглядит следующим образом:


МенеджерВТ = Новый МенеджерВременныхТаблиц;

// Помещаем внешнюю ТЗ в менеджер временных таблиц
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = 
"ВЫБРАТЬ * ПОМЕСТИТЬ ВнешняяТаблица ИЗ &ТЗ КАК ТЗ";
Запрос.УстановитьПараметр("ТЗ", ТаблицаЗначенийИсточник);
Запрос.Выполнить();

// Инициализируем процессор компоновки
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки());

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(
    МакетКомпоновки,
    , // Внешние наборы данных не требуются
    ДанныеРасшифровки,
    Истина,
    ,
    МенеджерВТ // Передаем менеджер 6-м параметром
);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Способ 2. Предварительная фильтрация через параметры виртуальной таблицы

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

  1. Набор данных 1 формирует выборку справочника Номенклатура с левым соединением к виртуальной таблице РегистрНакопления.Продажи.Обороты.
  2. В параметрах виртуальной таблицы накладываем условие по списку контрагентов: Контрагент В (&СписокКонтрагентов).
  3. Перед формированием отчета программно анализируем переданную таблицу значений, отбираем только тех контрагентов, которые соответствуют условию, и передаем полученный массив в параметр СписокКонтрагентов.
  4. Набор данных 2 передает саму таблицу значений для сопоставления полей.

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

Способ 3. Перенос отбора в подчиненную группировку СКД

Когда требуется предоставить пользователю возможность самостоятельно управлять отбором в пользовательском режиме без написания дополнительного программного кода, изменим уровень наложения отбора в структуре отчета:

  1. Переходим на вкладку Настройки в схеме компоновки данных.
  2. Создаем группировку верхнего уровня по полю Номенклатура. В свойствах этой группировки отборы не устанавливаем.
  3. Внутрь группировки номенклатуры добавляем детальные записи или подчиненную группировку по контрагентам/продажам.
  4. Устанавливаем пользовательский отбор по полю характеристики только для подчиненной группировки.

При такой структуре компоновщик выведет в отчет абсолютно всю номенклатуру, а блок подчиненных строк отобразится только у тех позиций, для которых выполнилось условие отбора.

Способ 4. Корректная обработка значений NULL в выражении отбора

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

  1. В настройках отчета создаем группу условий ИЛИ.
  2. Первым элементом добавляем нужное условие: ХарактеристикаКонтрагента Равно &ЗначениеОтбора.
  3. Вторым элементом добавляем проверку отсутствия данных: ХарактеристикаКонтрагента Не заполнено.

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

← На главную