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