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