Как реализовать условие с булевым параметром в запросе 1С и исправить ошибки в конструкции ВЫБОР?

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

При разработке отчетов и обработок в 1С часто требуется сделать условие фильтрации опциональным. Например, если пользователь установил флаг ТолькоАктуальные на форме, в результат запроса должны попасть только те строки, где дата документа не заполнена (пустая дата), а если флаг снят — выводятся все строки без ограничений. При попытке реализовать такую логику разработчики нередко сталкиваются с ошибками синтаксиса языка запросов 1С.

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

Анализ причин синтаксических ошибок

Проанализируем распространенные ошибки, которые возникают при попытке написать условие фильтрации:

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

Вариант 1. Использование конструкции ВЫБОР в секции ГДЕ

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


ГДЕ
    ВЫБОР
        КОГДА &ТолькоАктуальные = ИСТИНА И НЕ ПоступлениеТоваровУслугТовары.Ссылка.Дата = ДАТАВРЕМЯ(1, 1, 1)
            ТОГДА ЛОЖЬ
        ИНАЧЕ ИСТИНА
    КОНЕЦ

Разберем логику работы этой конструкции: если параметр передан как ИСТИНА, но дата заполнена (не равна пустой дате), условие возвращает ЛОЖЬ, и строка исключается из выборки. Во всех остальных случаях возвращается ИСТИНА.

Вариант 2. Прямая булева логика без ВЫБОР (рекомендуемый способ)

Конструкция ВЫБОР в секции ГДЕ делает запрос громоздким. Намного проще и нагляднее выразить ту же логику через стандартные логические операторы НЕ, И, ИЛИ. Рассмотрим этот вариант:


ГДЕ
    (НЕ &ТолькоАктуальные ИЛИ ПоступлениеТоваровУслугТовары.Ссылка.Дата = ДАТАВРЕМЯ(1, 1, 1))

Проанализируем работу данного условия:

  1. Если &ТолькоАктуальные = ЛОЖЬ, первая часть выражения НЕ &ТолькоАктуальные становится равной ИСТИНА. По правилам логического ИЛИ все условие выполняется для любой строки, фильтрация по дате отключается.
  2. Если &ТолькоАктуальные = ИСТИНА, первая часть становится ЛОЖЬ, и СУБД проверяет вторую часть — равенство даты пустой дате ДАТАВРЕМЯ(1, 1, 1).

Важные нюансы: обработка NULL при левых соединениях

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

В языке SQL операция прямого сравнения NULL = ДАТАВРЕМЯ(1, 1, 1) возвращает неопределенный результат (ложь). В итоге строки без документов поступления не попадут в выборку, хотя фактически поступления по ним нет. Чтобы корректно учесть и незаполненные даты, и полное отсутствие документа, используем функцию ЕСТЬNULL:


ГДЕ
    (НЕ &ТолькоАктуальные 
        ИЛИ ЕСТЬNULL(ПоступлениеТоваровУслугТовары.Ссылка.Дата, ДАТАВРЕМЯ(1, 1, 1)) = ДАТАВРЕМЯ(1, 1, 1))

Оптимизация производительности запроса

Рассмотрим два архитектурных момента, влияющих на скорость выполнения запроса:

  1. Разыменование ссылок через точку. Обращение ПоступлениеТоваровУслугТовары.Ссылка.Дата из табличной части к реквизиту шапки документа порождает неявное соединение таблиц на уровне СУБД. Если требуется анализировать реквизиты документа, надежнее присоединять саму таблицу документа или использовать реальные поля без избыточных разыменований.
  2. Динамическое формирование текста запроса. Использование условий вида (НЕ &Флаг ИЛИ Поле = Значение) может препятствовать оптимизатору СУБД использовать индексы по полям. Если запрос выполняется часто или обрабатывает большие массивы данных, оптимально формировать условие в коде модуля перед вызовом запроса:

Если ТолькоАктуальные Тогда
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "//&УсловиеПоДате", 
        "И ЕСТЬNULL(ПоступлениеТоваровУслугТовары.Ссылка.Дата, ДАТАВРЕМЯ(1, 1, 1)) = ДАТАВРЕМЯ(1, 1, 1)");
Иначе
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "//&УсловиеПоДате", "");
КонецЕсли;

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

← На главную