Почему не работает условие исключения нескольких значений в запросе 1С

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

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

Анализ ошибки: почему не работает условие с оператором ИЛИ

Рассмотрим условие, которое изначально написал разработчик в секции ГДЕ:


ГДЕ (омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе) 
ИЛИ омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании))

На первый взгляд кажется, что данный отбор должен исключить из выборки задачи со статусами «ВРаботе» и «НаТестировании». Однако на практике запрос возвращает абсолютно все строки. Проанализируем, почему так происходит с точки зрения логики:

  1. Пусть статус записи равен «ВРаботе». Первое выражение Статус <> ВРаботе даёт ЛОЖЬ. Но второе выражение Статус <> НаТестировании даёт ИСТИНА. Так как между ними стоит оператор ИЛИ, итоговое значение условия ЛОЖЬ ИЛИ ИСТИНА равно ИСТИНА. Запись попадает в выборку.
  2. Пусть статус записи равен «НаТестировании». Первое выражение Статус <> ВРаботе даёт ИСТИНА, второе — ЛОЖЬ. Выражение ИСТИНА ИЛИ ЛОЖЬ снова даёт ИСТИНА. Запись опять попадает в выборку.
  3. Для любого другого статуса оба выражения дают ИСТИНА, и строка также выводится.

Таким образом, логическое условие вида (А <> 1) ИЛИ (А <> 2) истинно абсолютно для любого значения А.

Как правильно составить логическое условие

Для корректного исключения нескольких значений из выборки разберем два правильных подхода.

Вариант 1: Использование логического оператора И

Чтобы запись была исключена, статус должен быть одновременно не равен первому значению И не равен второму значению:


ГДЕ (омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе) 
И омСтатусыЗаданийСрезПоследних.Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании))

Вариант 2: Использование оператора НЕ с объединением условий через ИЛИ

Согласно законам булевой алгебры (законам Де Моргана), отрицание конъюнкции равно дизъюнкции отрицаний (НЕ (А ИЛИ Б) = НЕ А И НЕ Б). Следовательно, мы можем записать условие так:


ГДЕ НЕ (омСтатусыЗаданийСрезПоследних.Статус = ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе) 
ИЛИ омСтатусыЗаданийСрезПоследних.Статус = ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании))

Вариант 3: Использование оператора НЕ ... В (наиболее читаемый вариант)

Самым лаконичным и устойчивым к ошибкам способом является проверка на вхождение в список значений с помощью конструкции НЕ ... В:


ГДЕ НЕ омСтатусыЗаданийСрезПоследних.Статус В (
    ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе), 
    ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании)
)

Если список исключаемых статусов динамический или может расширяться, рекомендуется передавать его через параметр запроса &ИсключаемыеСтатусы:

ГДЕ НЕ омСтатусыЗаданийСрезПоследних.Статус В (&ИсключаемыеСтатусы)

Ошибка наложения условий на ресурсы виртуальных таблиц

В ходе обсуждения было предложено перенести условие фильтрации напрямую в параметры виртуальной таблицы СрезПоследних:


ИЗ РегистрСведений.омСтатусыЗаданий.СрезПоследних(, 
    Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.ВРаботе) 
    И Статус <> ЗНАЧЕНИЕ(Справочник.омСтатусы.НаТестировании)
) КАК омСтатусыЗаданийСрезПоследних

Это классическая и опасная ошибка! Посмотрим, почему нельзя накладывать отбор на ресурсы в параметрах виртуальных таблиц срезов:

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

Правило: В параметрах виртуальных таблиц срезов отборы можно накладывать только на измерения. Отборы по ресурсам и реквизитам следует выполнять строго в секции ГДЕ основного запроса или во временных таблицах.

Важные технические нюансы: NULL и ЛЕВОЕ СОЕДИНЕНИЕ

1. Обработка значений NULL в условиях сравнения

В языке запросов 1С любое сравнение с NULL (включая =, <>, В, НЕ В) возвращает логическое значение NULL (неопределенность), которое в секции ГДЕ интерпретируется как ЛОЖЬ. Если в результате соединения или пустых данных статус окажется равен NULL, условие НЕ Статус В (...) исключит такую строку из результата.

Чтобы сохранить строки с NULL, используем функцию ЕСТЬNULL:


ГДЕ ЕСТЬNULL(омСтатусыЗаданийСрезПоследних.Статус, ЗНАЧЕНИЕ(Справочник.омСтатусы.ПустаяСсылка)) 
    НЕ В (&ИсключаемыеСтатусы)

2. Влияние условий в секции ГДЕ при ЛЕВОМ СОЕДИНЕНИИ

Наложение условия в секции ГДЕ на поля присоединяемой (правой) таблицы превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если требуется сохранить все записи из левой таблицы даже при отсутствии соответствия в правой, отбор необходимо переносить непосредственно в условие связи ПО:


ПО омСтатусыЗаданийСрезПоследних.Задание = омСтатусыЗаданий.Задание
   И НЕ омСтатусыЗаданийСрезПоследних.Статус В (&ИсключаемыеСтатусы)
← На главную