Почему СКД превращает левое соединение наборов данных во внутреннее при наложении отбора и как это исправить?

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

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

Как работает оптимизатор макета СКД

В настройках СКД нет явного указания типа соединения наборов данных — платформа по умолчанию считает все связи левым внешним соединением (где первый набор — это левая таблица, а зависимый — правая). Однако в процессе формирования отчета вмешивается встроенный оптимизатор СКД.

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

Убедиться в этом можно, если посмотреть финальный запрос. Найдите стандартную обработку «Консоль компоновки данных». В ней схема разбирается на составные части, и в сгенерированном макете четко видно, что без отбора платформа использует левое соединение, а при включенном отборе — внутреннее. Отсюда и возникает потеря данных из главного набора.

Способы решения проблемы

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

  1. Перенос отбора на уровень группировки

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

  2. Использование параметров вместо отборов

    Если требуется отфильтровать данные одного набора данных еще до момента их связывания, лучше отказаться от стандартного пользовательского отбора СКД. Вместо этого заведите параметр (например, &ДатаСсылка) на вкладке «Параметры» и используйте его напрямую в тексте запроса подчиненного набора (в параметрах виртуальной таблицы или в секции ГДЕ). СКД сначала получит ограниченную выборку из базы данных по этому параметру, а затем выполнит классическое левое соединение с главным набором.

  3. Передача отбора через параметры связи наборов данных

    На закладке «Связи наборов данных» есть полезная колонка «Параметр». Вы можете передавать значение поля из главного набора в подчиненный набор в виде параметра. В запросе зависимого набора данных этот параметр можно использовать для ограничения выборки. Это позволяет отсекать лишние данные в дочернем наборе без влияния на тип общей связи между наборами.

  4. Хитрость с обработкой пустых значений (ЕСТЬ NULL)

    Иногда сохранить левое соединение помогает добавление в отбор альтернативного условия. Настройте отбор как группу условий «ИЛИ». Внутри группы проверьте целевое значение поля из правого набора ИЛИ укажите, что это же поле ЕСТЬ NULL. Увидев такое условие, оптимизатор поймет, что ненайденные записи тоже важны для результата, и оставит левое соединение.

Альтернативный подход к проектированию отчета

Посмотрим на ситуацию шире. Как отмечают опытные разработчики, связывание наборов данных в СКД имеет множество нюансов и «подводных камней». Если задача позволяет, гораздо безопаснее не связывать наборы данных вообще, а использовать вид связи «Объединение» или, что еще лучше, выполнять всю логику связывания таблиц в одном большом SQL-запросе в единственном наборе данных.

Когда вы пишете связи таблиц непосредственно в тексте запроса:


ВЫБРАТЬ
    Таблица1.Ссылка КАК Ссылка,
    Таблица2.Дата КАК ДатаСсылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК Таблица1
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг КАК Таблица2
        ПО Таблица1.ДокументОснование = Таблица2.Ссылка

Вы полностью контролируете процесс. В таком случае тип соединения жестко задан разработчиком (ЛЕВОЕ СОЕДИНЕНИЕ), и никакие оптимизаторы СКД не смогут превратить его во внутреннее, даже если пользователь установит глобальный отбор на поле ДатаСсылка.

← На главную