Почему в запросе 1С не работает отбор по дате при добавлении условия ИЛИ и как это исправить

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

При составлении запросов в платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда отбор по периоду перестает корректно отсекать записи, если в секцию ГДЕ добавляется несколько условий со связкой ИЛИ. В выборку начинают попадать документы за пределами указанного интервала дат. Разберем детально, почему так происходит, как работают логические приоритеты в языке запросов и какими способами можно правильно решить эту проблему.

Причина некорректного поведения: приоритет логических операторов

Проанализируем исходный фрагмент условия в секции ГДЕ, который привел к ошибке:


ГДЕ
    ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
    И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки
    ИЛИ ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще

В языке запросов 1С и в стандарте SQL логический оператор И (конъюнкция) обладает более высоким приоритетом по сравнению с оператором ИЛИ (дизъюнкция). Это означает, что при отсутствии явной группировки выражений скобками СУБД сгруппирует условия следующим образом:


ГДЕ
    (ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
    И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки)
    ИЛИ (ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще)

В результате СУБД возвращает строки, которые удовлетворяют хотя бы одной из двух независимых веток логики:

  1. Документ попал в интервал между &ДатаНач и &ДатаКон И имеет статус &СтатусОбработки.
  2. Документ имеет статус &СтатусОбработкиЕще — за абсолютно любой период времени, даже за прошлые годы.

Именно поэтому в результирующую выборку попали документы, созданные задолго до начала указанного периода.

Способ 1. Использование круглых скобок для группировки условий

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


ГДЕ
    ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
    И (ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки
        ИЛИ ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще)

В таком варианте СУБД сначала вычисляет выражение внутри скобок (проверяет, равен ли статус первому или второму значению), а затем применяет результат через оператор И к условию попадания даты в заданный интервал.

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


ГДЕ
    (ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
    И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки)
    ИЛИ (ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
    И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще)

Хотя такой вариант возвращает корректные данные, он избыточен, усложняет чтение текста запроса и увеличивает вероятность ошибки при будущих доработках.

Способ 2. Использование оператора В со списком значений (рекомендуемый)

Наиболее лаконичным, производительным и гибким подходом в платформе 1С является замена цепочки операторов ИЛИ на конструкцию проверки вхождения в массив или список с помощью оператора В.

Посмотрим на пример измененного запроса:


ВЫБРАТЬ
    ТТНЕГАИС.Ссылка.Грузополучатель КАК Подразделение,
    ТТНЕГАИС.АлкогольнаяПродукция.Код КАК КодАП,
    ТТНЕГАИС.АлкогольнаяПродукция.ВидПродукции.КодВидаПродукции КАК КодАПП,
    ТТНЕГАИС.АлкогольнаяПродукция.Импортер КАК Импортер,
    ТТНЕГАИС.АлкогольнаяПродукция.Производитель КАК Производитель,
    ВЫБОР
        КОГДА ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки
            ТОГДА ТТНЕГАИС.Количество * ТТНЕГАИС.АлкогольнаяПродукция.Объем / 10
        КОГДА ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще
            ТОГДА ТТНЕГАИС.КоличествоФакт * ТТНЕГАИС.АлкогольнаяПродукция.Объем / 10
    КОНЕЦ КАК КолвоПоступления
ИЗ
    Документ.ТТНВходящаяЕГАИС.Товары КАК ТТНЕГАИС
ГДЕ
    ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
    И ТТНЕГАИС.Ссылка.СтатусОбработки В (&МассивСтатусов)

При вызове запроса во встроенном языке передаем список или массив требуемых статусов:


МассивСтатусов = Новый Массив;
МассивСтатусов.Добавить(СтатусОбработки);
МассивСтатусов.Добавить(СтатусОбработкиЕще);

Запрос.УстановитьПараметр("МассивСтатусов", МассивСтатусов);

Такой подход полностью исключает ошибки с приоритетом операторов, делает секцию ГДЕ компактной и позволяет легко добавлять новые статусы без изменения текста самого запроса.

Важные нюансы и оптимизация подобных запросов

При написании запросов к табличным частям документов важно учитывать архитектурные особенности платформы 1С:

  1. Разыменование ссылок через точку (неявные соединения): обращение к полям шапки документа (например, ТТНЕГАИС.Ссылка.ДатаТТН или ТТНЕГАИС.Ссылка.СтатусОбработки) из таблицы его табличной части Документ.ТТНВходящаяЕГАИС.Товары транслируется платформой в неявное левое соединение (LEFT JOIN). Двойные разыменования вида ТТНЕГАИС.АлкогольнаяПродукция.ВидПродукции.КодВидаПродукции порождают цепочку соединений для каждой строки. На больших базах данных это может приводить к существенным просадкам производительности. В высоконагруженных отчетах рекомендуется сначала отбирать документы с фильтром по дате и статусу во временную таблицу, а затем соединять их с табличной частью.
  2. Границы времени при использовании оператора МЕЖДУ: конструкция Дата МЕЖДУ &ДатаНач И &ДатаКон включает граничные значения (эквивалентно >= &ДатаНач И <= &ДатаКон). Если в параметре &ДатаКон передана дата без указания времени (например, 31.03.2017 00:00:00), то документы за 31 марта, проведенные в течение дня, не попадут в выборку. Чтобы избежать потери данных, в параметр конца периода необходимо передавать дату с помощью функции КонецДня(&ДатаКон) либо в самом запросе использовать функцию КОНЕЦПЕРИОДА(&ДатаКон, ДЕНЬ).
  3. Помеченные на удаление документы: при выборке строк непосредственно из табличной части в результат попадают строки всех документов, включая помеченные на удаление. Если такие строки выводить не требуется, обязательно добавляем в секцию ГДЕ проверку: И НЕ ТТНЕГАИС.Ссылка.ПометкаУдаления.
← На главную