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