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