При составлении запросов в платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда отбор по периоду перестает корректно отсекать записи, если в секцию ГДЕ добавляется несколько условий со связкой ИЛИ. В выборку начинают попадать документы за пределами указанного интервала дат. Разберем детально, почему так происходит, как работают логические приоритеты в языке запросов и какими способами можно правильно решить эту проблему.
Проанализируем исходный фрагмент условия в секции ГДЕ, который привел к ошибке:
ГДЕ
ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки
ИЛИ ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще
В языке запросов 1С и в стандарте SQL логический оператор И (конъюнкция) обладает более высоким приоритетом по сравнению с оператором ИЛИ (дизъюнкция). Это означает, что при отсутствии явной группировки выражений скобками СУБД сгруппирует условия следующим образом:
ГДЕ
(ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки)
ИЛИ (ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще)
В результате СУБД возвращает строки, которые удовлетворяют хотя бы одной из двух независимых веток логики:
&ДатаНач и &ДатаКон И имеет статус &СтатусОбработки.&СтатусОбработкиЕще — за абсолютно любой период времени, даже за прошлые годы.Именно поэтому в результирующую выборку попали документы, созданные задолго до начала указанного периода.
Самое быстрое исправление ошибки без изменения логики передачи параметров — явное выделение приоритета условий с помощью круглых скобок. Сгруппируем проверки статусов в единый логический блок:
ГДЕ
ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
И (ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки
ИЛИ ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще)
В таком варианте СУБД сначала вычисляет выражение внутри скобок (проверяет, равен ли статус первому или второму значению), а затем применяет результат через оператор И к условию попадания даты в заданный интервал.
Иногда разработчики пытаются решить задачу дублированием проверки периода для каждого статуса:
ГДЕ
(ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки)
ИЛИ (ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
И ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще)
Хотя такой вариант возвращает корректные данные, он избыточен, усложняет чтение текста запроса и увеличивает вероятность ошибки при будущих доработках.
Наиболее лаконичным, производительным и гибким подходом в платформе 1С является замена цепочки операторов ИЛИ на конструкцию проверки вхождения в массив или список с помощью оператора В.
Посмотрим на пример измененного запроса:
ВЫБРАТЬ
ТТНЕГАИС.Ссылка.Грузополучатель КАК Подразделение,
ТТНЕГАИС.АлкогольнаяПродукция.Код КАК КодАП,
ТТНЕГАИС.АлкогольнаяПродукция.ВидПродукции.КодВидаПродукции КАК КодАПП,
ТТНЕГАИС.АлкогольнаяПродукция.Импортер КАК Импортер,
ТТНЕГАИС.АлкогольнаяПродукция.Производитель КАК Производитель,
ВЫБОР
КОГДА ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработки
ТОГДА ТТНЕГАИС.Количество * ТТНЕГАИС.АлкогольнаяПродукция.Объем / 10
КОГДА ТТНЕГАИС.Ссылка.СтатусОбработки = &СтатусОбработкиЕще
ТОГДА ТТНЕГАИС.КоличествоФакт * ТТНЕГАИС.АлкогольнаяПродукция.Объем / 10
КОНЕЦ КАК КолвоПоступления
ИЗ
Документ.ТТНВходящаяЕГАИС.Товары КАК ТТНЕГАИС
ГДЕ
ТТНЕГАИС.Ссылка.ДатаТТН МЕЖДУ &ДатаНач И &ДатаКон
И ТТНЕГАИС.Ссылка.СтатусОбработки В (&МассивСтатусов)
При вызове запроса во встроенном языке передаем список или массив требуемых статусов:
МассивСтатусов = Новый Массив;
МассивСтатусов.Добавить(СтатусОбработки);
МассивСтатусов.Добавить(СтатусОбработкиЕще);
Запрос.УстановитьПараметр("МассивСтатусов", МассивСтатусов);
Такой подход полностью исключает ошибки с приоритетом операторов, делает секцию ГДЕ компактной и позволяет легко добавлять новые статусы без изменения текста самого запроса.
При написании запросов к табличным частям документов важно учитывать архитектурные особенности платформы 1С:
ТТНЕГАИС.Ссылка.ДатаТТН или ТТНЕГАИС.Ссылка.СтатусОбработки) из таблицы его табличной части Документ.ТТНВходящаяЕГАИС.Товары транслируется платформой в неявное левое соединение (LEFT JOIN). Двойные разыменования вида ТТНЕГАИС.АлкогольнаяПродукция.ВидПродукции.КодВидаПродукции порождают цепочку соединений для каждой строки. На больших базах данных это может приводить к существенным просадкам производительности. В высоконагруженных отчетах рекомендуется сначала отбирать документы с фильтром по дате и статусу во временную таблицу, а затем соединять их с табличной частью.Дата МЕЖДУ &ДатаНач И &ДатаКон включает граничные значения (эквивалентно >= &ДатаНач И <= &ДатаКон). Если в параметре &ДатаКон передана дата без указания времени (например, 31.03.2017 00:00:00), то документы за 31 марта, проведенные в течение дня, не попадут в выборку. Чтобы избежать потери данных, в параметр конца периода необходимо передавать дату с помощью функции КонецДня(&ДатаКон) либо в самом запросе использовать функцию КОНЕЦПЕРИОДА(&ДатаКон, ДЕНЬ).ГДЕ проверку: И НЕ ТТНЕГАИС.Ссылка.ПометкаУдаления.