Как правильно использовать оператор МЕЖДУ в запросах 1С при работе с необязательными датами?

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

В практике разработки на платформе 1С:Предприятие часто возникает задача отбора данных по периоду. Казалось бы, оператор МЕЖДУ идеально подходит для этих целей. Однако начинающие разработчики сталкиваются с проблемой: если пользователь не заполнил даты начала или конца периода на форме, запрос либо выдает пустой результат, либо, напротив, выбирает документы с пустой датой. Давайте подробно разберем, как реализовать гибкий отбор, который будет работать корректно в любых ситуациях.

Анализ проблемы: почему оператор МЕЖДУ ведет себя именно так?

Рассмотрим стандартную ситуацию. У нас есть запрос, в котором прописано условие:


ГДЕ
    Договоры.ДатаПодписания МЕЖДУ &ДатаНачала И &ДатаКонца

Когда параметры &ДатаНачала и &ДатаКонца не заполнены, в запрос передается значение 01.01.0001 00:00:00. В результате запрос пытается найти записи, у которых дата подписания попадает в диапазон от "пустого" значения до "пустого". Если в базе есть документы с незаполненной датой, они попадут в выборку. Если мы хотим, чтобы при незаполненных датах на форме отбор просто игнорировался, нам нужно изменить подход к написанию условия ГДЕ.

Способ 1: Универсальное условие внутри запроса

Проанализируем наиболее профессиональный и "чистый" метод. Вместо того чтобы менять текст запроса в коде 1С, мы можем прописать логику обработки пустых параметров непосредственно в языке запросов. Это позволяет избежать использования функций типа СтрЗаменить и делает код более читаемым.

Суть метода заключается в том, чтобы проверить параметр на равенство пустой дате (литерал ДАТАВРЕМЯ(1, 1, 1)). Рассмотрим пример реализации:


ВЫБРАТЬ
    пто_Договоры.Ссылка КАК Ссылка,
    пто_Договоры.ДатаПодписанияЗаявителем КАК ДатаПодписанияЗаявителем
ИЗ
    Справочник.пто_Договоры КАК пто_Договоры
ГДЕ
    (&ДатаНачалаЗаявитель = ДАТАВРЕМЯ(1, 1, 1) ИЛИ пто_Договоры.ДатаПодписанияЗаявителем >= &ДатаНачалаЗаявитель)
    И (&ДатаКонцаЗаявитель = ДАТАВРЕМЯ(1, 1, 1) ИЛИ пто_Договоры.ДатаПодписанияЗаявителем <= &ДатаКонцаЗаявитель)
    И пто_Договоры.Наименование ПОДОБНО &ПараметрСтрокаПоиска

Разберем, как это работает. Если пользователь не заполнил дату начала, условие &ДатаНачалаЗаявитель = ДАТАВРЕМЯ(1, 1, 1) вернет Истина. Благодаря оператору ИЛИ, вся скобка превратится в истину, и фильтр по нижней границе даты применяться не будет. Аналогично это работает и для верхней границы. Это самый надежный способ для обычных запросов.

Способ 2: Использование фигурных скобок (Система компоновки данных)

Если ваша задача выполняется в контексте динамического списка или отчета на СКД, оптимальным решением будет использование необязательных условий в фигурных скобках. В этом случае 1С сама проанализирует, заполнен ли параметр, и если нет — полностью исключит условие из итогового SQL-запроса.

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


ИЗ
    Справочник.пто_Договоры КАК пто_Договоры
ГДЕ
    {пто_Договоры.ДатаПодписанияЗаявителем МЕЖДУ &ДатаНачалаЗаявитель И &ДатаКонцаЗаявитель}

Важно помнить, что фигурные скобки работают только там, где запрос обрабатывается процессором компоновки данных. В обычном объекте Запрос встроенного языка этот синтаксис не сработает.

Способ 3: Динамическое формирование текста запроса

Иногда требуется программно управлять текстом запроса. Разберем пример, когда мы добавляем условие ГДЕ только в том случае, если даты заполнены. Это позволяет избежать лишних проверок внутри самого SQL-запроса, что может быть полезно для производительности на огромных таблицах.

Посмотрим на программную реализацию:


ТекстУсловия = "";
Если ЗначениеЗаполнено(Объект.ДатаНачалаЗаявитель) И ЗначениеЗаполнено(Объект.ДатаКонцаЗаявитель) Тогда
    ТекстУсловия = "И пто_Договоры.ДатаПодписанияЗаявителем МЕЖДУ &ДатаНачалаЗаявитель И &ДатаКонцаЗаявитель";
КонецЕсли;

Запрос.Текст = СтрЗаменить(Запрос.Текст, "//_УсловиеПоДате", ТекстУсловия);

В данном примере мы используем заранее подготовленный комментарий-маркер //_УсловиеПоДате в тексте запроса, который заменяем на реальное условие только при необходимости.

Важные нюансы: О чем часто забывают программисты

Рассмотрим несколько критических моментов, которые могут привести к неверным результатам даже при правильном синтаксисе запроса.

  1. Граница конца дня. Это самая распространенная ошибка. Если пользователь выбрал дату конца 31.12.2023, то в параметр передастся 31.12.2023 00:00:00. Любой документ, созданный в полдень этого дня, не попадет в выборку МЕЖДУ. Решение: всегда используйте КонецДня() при передаче параметра.
  2. Производительность индексов. Никогда не используйте функции преобразования (например, НАЧАЛОПЕРИОДА) над полями базы данных в условии ГДЕ. Это приведет к тому, что СУБД не сможет использовать индекс по дате, и запрос будет "тормозить". Обрабатывайте только параметры.
  3. Синтаксис пустой даты. В языке запросов 1С пустая дата записывается как ДАТАВРЕМЯ(1, 1, 1) или ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0). Ошибка синтаксиса часто возникает, если вы путаете язык запросов и встроенный язык (где пустая дата — это '00010101').

Давайте проанализируем пример правильной передачи параметров из кода:


Запрос.УстановитьПараметр("ДатаНачалаЗаявитель", НачалоДня(Объект.ДатаНачалаЗаявитель));
// Используем КонецДня, чтобы захватить весь последний день периода
Запрос.УстановитьПараметр("ДатаКонцаЗаявитель", КонецДня(Объект.ДатаКонцаЗаявитель));

Работа со значениями NULL

Выясним еще одну важную деталь. Если ваш запрос использует ЛЕВОЕ СОЕДИНЕНИЕ, то в поле ДатаПодписания может оказаться значение NULL. Оператор МЕЖДУ (как и простое сравнение) всегда возвращает Ложь при сравнении с NULL. Если вам нужно учитывать такие записи при пустых фильтрах, обязательно добавьте проверку ЕСТЬNULL(Поле, ДАТАВРЕМЯ(1,1,1)) или явную проверку на ЕСТЬ NULL.

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

← На главную