Почему в отчете СКД при отборе Не заполнено по полю из левого соединения строка не удаляется, а поле просто очищается

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

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

Выясним причину: механизм проталкивания фильтров

Для начала проанализируем ситуацию с точки зрения работы оптимизатора СКД. Когда мы выполняем запрос в 1С, система компоновки данных старается сделать его максимально эффективным для СУБД. Этот процесс называется Filter Pushdown (проталкивание фильтра).

Если вы устанавливаете отбор по полю из таблицы, которая присоединена через ЛЕВОЕ СОЕДИНЕНИЕ, СКД может попытаться «засунуть» этот отбор внутрь параметров виртуальной таблицы или в само условие связи ПО (JOIN ON). Рассмотрим, что происходит в этом случае:

  1. СКД формирует условие типа ГДЕ Поле ЕСТЬ NULL.
  2. Если это условие попадает в параметры виртуальной таблицы, то данные из этой таблицы просто перестают выбираться (возвращается NULL).
  3. Так как соединение левое, основная таблица все равно выводит свою строку, просто пристыковывая к ней пустые значения из правой таблицы.

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

Способ 1. Использование расширения языка запросов СКД

Самый надежный способ решить проблему на уровне запроса — использовать фигурные скобки {}. Это позволяет управлять тем, в какую часть итогового SQL-запроса попадет пользовательский отбор. Рассмотрим подробнее, как это реализовать.

В тексте запроса, в самом конце основного раздела (после всех соединений), необходимо добавить секцию ГДЕ, обернутую в расширение компоновки данных. Проанализируем пример кода:


ВЫБРАТЬ
    Постановление.Ссылка КАК Постановление,
    втДатыОплаты.ДатаПлатежа КАК ДатаПлатежа
ИЗ
    Документ.ПостановлениеОНарушенииПДД КАК Постановление
ЛЕВОЕ СОЕДИНЕНИЕ втДатыОплаты КАК втДатыОплаты 
    ПО Постановление.Ссылка = втДатыОплаты.Документ
ГДЕ
    НЕ Постановление.ПометкаУдаления
    {ГДЕ
    (втДатыОплаты.ДатаПлатежа <= &ДатаОкончанияПлатежа),
    втДатыОплаты.ДатаПлатежа КАК ДатаПлатежа}

В данном примере конструкция {ГДЕ ... втДатыОплаты.ДатаПлатежа КАК ДатаПлатежа} указывает СКД, что если пользователь установит отбор по полю ДатаПлатежа, то это условие должно быть добавлено именно в секцию ГДЕ итогового запроса, а не «протолкнуто» в параметры виртуальных таблиц или условий связи. Это заставит СУБД отсечь всю строку целиком, если условие не выполняется.

Способ 2. Отбор на уровне группировки

Если вы не хотите менять текст запроса, можно воспользоваться особенностями иерархии применения отборов в СКД. Разберем этот вариант по шагам.

В СКД есть несколько уровней, где может применяться фильтр:

  1. Отбор в параметрах виртуальных таблиц (самый ранний и быстрый).
  2. Отбор в секции ГДЕ.
  3. Отбор на уровне всего отчета (после получения данных).
  4. Отбор на уровне конкретной группировки.

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

Способ 3. Уникальные псевдонимы полей во временных таблицах

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

Посмотрим на пример структуры запроса:


// Временная таблица 1
ВЫБРАТЬ ... ПОМЕСТИТЬ втДатыОплаты ИЗ ... ГДЕ ... ;

// Итоговый запрос
ВЫБРАТЬ ... ЕСТЬNULL(втДатыОплаты.ДатаПлатежа, ДАТАВРЕМЯ(1,1,1)) КАК ДатаПлатежа ...

Если в схеме компоновки данных поле ДатаПлатежа из итоговой выборки имеет тот же псевдоним, что и поле во временной таблице, СКД может ошибочно привязать пользовательский отбор к первой встретившейся временной таблице. Чтобы этого избежать, рекомендуется давать уникальные имена полям в промежуточных таблицах, а окончательный псевдоним назначать только в последнем ВЫБРАТЬ. Это исключит путаницу в логике оптимизатора.

Способ 4. Работа с функцией ЕСТЬNULL в полях отбора

Важно помнить, как СКД интерпретирует значение «Не заполнено». Для типа Дата — это либо NULL, либо пустая дата (01.01.0001). Если в вашем запросе используется конструкция ЕСТЬNULL(Поле, ДАТАВРЕМЯ(1, 1, 1)), то для СКД это вычисляемое выражение.

Проанализируем последствия: когда вы накладываете отбор «Не заполнено» на такое поле, СКД может запутаться — нужно ли искать NULL в исходной таблице или сравнивать результат функции ЕСТЬNULL с пустой датой. В таких случаях лучше всего:

Резюме

Для корректной работы отбора «Не заполнено» по полям из левого соединения в СКД, мы рекомендуем придерживаться следующего алгоритма:

  1. Используйте расширение языка запросов {ГДЕ ...} для явного указания места фильтрации.
  2. Если запрос сложный, проверьте, не дублируются ли псевдонимы полей во временных таблицах.
  3. В крайнем случае перенесите отбор из общих настроек отчета в настройки конкретной группировки.

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

← На главную