Разберем распространенную ситуацию в системе компоновки данных (СКД), когда при наложении пользовательского отбора по вычисляемому логическому полю отчет начинает выдавать неожиданные результаты. Например, отбор со значением Истина отрабатывает корректно, а отбор со значением Ложь выводит все записи без исключения либо полностью ломает логику левого соединения.
Проанализируем исходный пример пакета запросов, с которым возникла проблема:
ВЫБРАТЬ
5 КАК Оценка
ПОМЕСТИТЬ Оценки
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
4
;
ВЫБРАТЬ
5 КАК Оценка,
ИСТИНА КАК Хорошая
ПОМЕСТИТЬ ХорошиеОценки
;
ВЫБРАТЬ
Оценки.Оценка КАК Оценка,
ЕСТЬNULL(ХорошиеОценки.Хорошая, ЛОЖЬ) КАК Хорошая
ИЗ
Оценки КАК Оценки
ЛЕВОЕ СОЕДИНЕНИЕ ХорошиеОценки КАК ХорошиеОценки
ПО Оценки.Оценка = ХорошиеОценки.Оценка
При установке пользовательского отбора Хорошая = Ложь ожидается получить только записи с оценкой 4. Однако система возвращает все строки (и 4, и 5). Выясним причину такого поведения платформы 1С:Предприятие и рассмотрим несколько вариантов правильного решения задачи.
По умолчанию для набора данных — запроса в схеме компоновки данных включен флаг Автозаполнение. В этом режиме компоновщик макета СКД анализирует имена и псевдонимы полей во всех запросах пакета и автоматически транслирует пользовательские условия в секции ГДЕ.
Поскольку поле Хорошая присутствует как во временной таблице ХорошиеОценки, так и в итоговом запросе выборки, компоновщик модифицирует пакет запросов следующим образом:
ВЫБРАТЬ
5 КАК Оценка,
ИСТИНА КАК Хорошая
ПОМЕСТИТЬ ХорошиеОценки
ГДЕ
ИСТИНА = &П // Сюда подставилось: ИСТИНА = ЛОЖЬ
;
ВЫБРАТЬ
Оценки.Оценка КАК Оценка,
ЕСТЬNULL(ХорошиеОценки.Хорошая, ЛОЖЬ) КАК Хорошая
ИЗ
Оценки КАК Оценки
ЛЕВОЕ СОЕДИНЕНИЕ ХорошиеОценки КАК ХорошиеОценки
ПО Оценки.Оценка = ХорошиеОценки.Оценка
ГДЕ
ЕСТЬNULL(ХорошиеОценки.Хорошая, ЛОЖЬ) = &П
В результате при отборе Хорошая = Ложь секция ГДЕ ИСТИНА = ЛОЖЬ делает временную таблицу ХорошиеОценки полностью пустой. При последующем левом соединении таблица Оценки ни с чем не соединяется, функция ЕСТЬNULL для всех строк возвращает ЛОЖЬ, и второе условие ГДЕ ЛОЖЬ = ЛОЖЬ становится истинным для абсолютно всех строк. Отчет выводит все оценки подряд.
Самое лаконичное и производительное решение — отказаться от создания искусственного логического поля во временной таблице. Достаточно проверить наличие записи через условие НЕ ... ЕСТЬ NULL в итоговой выборке.
Посмотрим на исправленный текст запроса:
ВЫБРАТЬ
5 КАК Оценка
ПОМЕСТИТЬ Оценки
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
4
;
ВЫБРАТЬ
5 КАК Оценка
ПОМЕСТИТЬ ХорошиеОценки
;
ВЫБРАТЬ
Оценки.Оценка КАК Оценка,
НЕ ХорошиеОценки.Оценка ЕСТЬ NULL КАК Хорошая
ИЗ
Оценки КАК Оценки
ЛЕВОЕ СОЕДИНЕНИЕ ХорошиеОценки КАК ХорошиеОценки
ПО Оценки.Оценка = ХорошиеОценки.Оценка
В этом случае поле Хорошая присутствует исключительно в итоговом запросе. СКД не будет вмешиваться в формирование временной таблицы ХорошиеОценки, и условие применится строго к результирующим данным.
Если логическое поле необходимо во временной таблице для промежуточных расчетов, следует задать ему уникальный псевдоним, отличный от имени результирующего поля отчета.
Разберем этот вариант на примере:
ВЫБРАТЬ
5 КАК Оценка,
ИСТИНА КАК ЭтоХорошаяОценкаВТ
ПОМЕСТИТЬ ХорошиеОценки
;
ВЫБРАТЬ
Оценки.Оценка КАК Оценка,
ЕСТЬNULL(ХорошиеОценки.ЭтоХорошаяОценкаВТ, ЛОЖЬ) КАК Хорошая
ИЗ
Оценки КАК Оценки
ЛЕВОЕ СОЕДИНЕНИЕ ХорошиеОценки КАК ХорошиеОценки
ПО Оценки.Оценка = ХорошиеОценки.Оценка
Поскольку имя поля в отборе (Хорошая) больше не совпадает с полем временной таблицы (ЭтоХорошаяОценкаВТ), автозаполнение СКД не добавит фильтрацию в первый запрос пакета.
Для полного контроля над поведением запроса снимем флаг Автозаполнение на вкладке Наборы данных и расставим инструкции расширения языка запросов СКД в фигурных скобках вручную.
Рассмотрим структуру запроса без автозаполнения:
ВЫБРАТЬ
5 КАК Оценка,
ИСТИНА КАК Хорошая
ПОМЕСТИТЬ ХорошиеОценки
;
ВЫБРАТЬ
Оценки.Оценка КАК Оценка,
ЕСТЬNULL(ХорошиеОценки.Хорошая, ЛОЖЬ) КАК Хорошая
{ВЫБРАТЬ
Оценка,
Хорошая}
ИЗ
Оценки КАК Оценки
ЛЕВОЕ СОЕДИНЕНИЕ ХорошиеОценки КАК ХорошиеОценки
ПО Оценки.Оценка = ХорошиеОценки.Оценка
{ГДЕ
(ЕСТЬNULL(ХорошиеОценки.Хорошая, ЛОЖЬ)) КАК Хорошая,
Оценки.Оценка КАК Оценка}
Такой подход гарантирует, что пользовательский отбор по полю Хорошая встроится платформой исключительно в блок {ГДЕ ...} финального запроса.
Еще один надежный способ — вынести вычисление флага на уровень схемы компоновки данных:
Хорошая и выражением: Выбор Когда ОценкаХорошая Есть Не Null Тогда Истина Иначе Ложь Конец.При таком подходе фильтрация выполняется процессором компоновки данных и не искажает структуру промежуточных выборок в пакете запросов.