При написании запросов в платформе 1С разработчики часто сталкиваются со ситуацией, когда условие отбора, предназначенное для исключения нескольких конкретных значений, возвращает все записи или работает некорректно. Рассмотрим подробнее причинно-следственную связь такой ошибки, разберем правила логических операций в языке запросов и проанализируем нюансы работы с виртуальными таблицами.
Рассмотрим условие, которое изначально написал разработчик в секции ГДЕ:
ГДЕ (омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе)
ИЛИ омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании))
На первый взгляд кажется, что данный отбор должен исключить из выборки задачи со статусами «ВРаботе» и «НаТестировании». Однако на практике запрос возвращает абсолютно все строки. Проанализируем, почему так происходит с точки зрения логики:
Статус <> ВРаботе даёт ЛОЖЬ. Но второе выражение Статус <> НаТестировании даёт ИСТИНА. Так как между ними стоит оператор ИЛИ, итоговое значение условия ЛОЖЬ ИЛИ ИСТИНА равно ИСТИНА. Запись попадает в выборку.Статус <> ВРаботе даёт ИСТИНА, второе — ЛОЖЬ. Выражение ИСТИНА ИЛИ ЛОЖЬ снова даёт ИСТИНА. Запись опять попадает в выборку.Таким образом, логическое условие вида (А <> 1) ИЛИ (А <> 2) истинно абсолютно для любого значения А.
Для корректного исключения нескольких значений из выборки разберем два правильных подхода.
Вариант 1: Использование логического оператора И
Чтобы запись была исключена, статус должен быть одновременно не равен первому значению И не равен второму значению:
ГДЕ (омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе)
И омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании))
Вариант 2: Использование оператора НЕ с объединением условий через ИЛИ
Согласно законам булевой алгебры (законам Де Моргана), отрицание конъюнкции равно дизъюнкции отрицаний (НЕ (А ИЛИ Б) = НЕ А И НЕ Б). Следовательно, мы можем записать условие так:
ГДЕ НЕ (омСтатусыЗаданийСрезПоследних.Статус = ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе)
ИЛИ омСтатусыЗаданийСрезПоследних.Статус = ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании))
Вариант 3: Использование оператора НЕ ... В (наиболее читаемый вариант)
Самым лаконичным и устойчивым к ошибкам способом является проверка на вхождение в список значений с помощью конструкции НЕ ... В:
ГДЕ НЕ омСтатусыЗаданийСрезПоследних.Статус В (
ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе),
ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании)
)
Если список исключаемых статусов динамический или может расширяться, рекомендуется передавать его через параметр запроса &ИсключаемыеСтатусы:
ГДЕ НЕ омСтатусыЗаданийСрезПоследних.Статус В (&ИсключаемыеСтатусы)
В ходе обсуждения было предложено перенести условие фильтрации напрямую в параметры виртуальной таблицы СрезПоследних:
ИЗ РегистрСведений.омСтатусыЗаданий.СрезПоследних(,
Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе)
И Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании)
) КАК омСтатусыЗаданийСрезПоследних
Это классическая и опасная ошибка! Посмотрим, почему нельзя накладывать отбор на ресурсы в параметрах виртуальных таблиц срезов:
СрезПоследних предназначены для фильтрации исходных записей регистра до расчета среза.Статус является ресурсом регистра, то наложив отбор по ресурсу в параметрах таблицы, мы исключим из истории смены статусов все записи с исключаемыми значениями.Правило: В параметрах виртуальных таблиц срезов отборы можно накладывать только на измерения. Отборы по ресурсам и реквизитам следует выполнять строго в секции ГДЕ основного запроса или во временных таблицах.
1. Обработка значений NULL в условиях сравнения
В языке запросов 1С любое сравнение с NULL (включая =, <>, В, НЕ В) возвращает логическое значение NULL (неопределенность), которое в секции ГДЕ интерпретируется как ЛОЖЬ. Если в результате соединения или пустых данных статус окажется равен NULL, условие НЕ Статус В (...) исключит такую строку из результата.
Чтобы сохранить строки с NULL, используем функцию ЕСТЬNULL:
ГДЕ ЕСТЬNULL(омСтатусыЗаданийСрезПоследних.Статус, ЗНАЧЕНИЕ(Справочник.омСтатусы.ПустаяСсылка))
НЕ В (&ИсключаемыеСтатусы)
2. Влияние условий в секции ГДЕ при ЛЕВОМ СОЕДИНЕНИИ
Наложение условия в секции ГДЕ на поля присоединяемой (правой) таблицы превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если требуется сохранить все записи из левой таблицы даже при отсутствии соответствия в правой, отбор необходимо переносить непосредственно в условие связи ПО:
ПО омСтатусыЗаданийСрезПоследних.Задание = омСтатусыЗаданий.Задание
И НЕ омСтатусыЗаданийСрезПоследних.Статус В (&ИсключаемыеСтатусы)