В процессе разработки отчетов и алгоритмов обработки данных часто возникает задача опциональной фильтрации: если пользователь взводит флаг (булево значение Истина), запрос должен отбирать только строки с незаполненной датой, а если флаг снят (Ложь) — возвращать все строки без ограничений по дате. Разберем по шагам, как правильно задать пустую дату в языке запросов 1С, как составить логическое условие и какие варианты реализации являются наиболее производительными для СУБД.
Проанализируем специфику типа данных Дата в платформе 1С:Предприятие. Пустое значение даты в 1С соответствует значению 01.01.0001 00:00:00. В тексте запроса литерал пустой даты объявляется с помощью конструкции ДАТАВРЕМЯ(1, 1, 1) или ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0). Кроме того, если дата получается в результате левого соединения таблиц или из реквизита составного типа, незаполненное поле может содержать значение NULL, что также необходимо учитывать при построении условий.
Рассмотрим наиболее простой способ, позволяющий передавать логический параметр типа Булево прямо в запрос без изменения текста самого запроса. Логика работы следующая: если параметр равен Ложь (флаг выключен), выражение НЕ &ТолькоПустые возвращает Истина, и условие выполняется для всех строк. Если параметр равен Истина, первая часть выражения становится ложной, и запрос проверяет равенство даты пустой величине.
Посмотрим на пример текста запроса:
ВЫБРАТЬ
Таблица.Ссылка КАК Документ,
Таблица.Дата КАК ДатаДокумента
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Таблица
ГДЕ
НЕ &ТолькоПустые ИЛИ Таблица.Дата = ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0)
Если в выборке могут присутствовать строки со значением NULL (например, при обращении к реквизитам через точку или при использовании левого соединения), обязательно защитим запрос от некорректного сравнения:
ВЫБРАТЬ
Заказ.Ссылка КАК Заказ,
Реализация.Дата КАК ДатаОтгрузки
ИЗ
Документ.ЗаказКлиента КАК Заказ
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация
ПО Заказ.Ссылка = Реализация.ЗаказКлиента
ГДЕ
НЕ &ТолькоПустые
ИЛИ ЕСТЬNULL(Реализация.Дата, ДАТАВРЕМЯ(1, 1, 1)) = ДАТАВРЕМЯ(1, 1, 1)
В коде на встроенном языке передача параметра выглядит стандартно:
Запрос = Новый Запрос;
Запрос.Текст = "..."; // Текст запроса
Запрос.УстановитьПараметр("ТолькоПустые", ФлагТолькоПустые);
Результат = Запрос.Выполнить();
Разберем альтернативную синтаксическую запись условия через оператор ВЫБОР. Этот подход делает логику ветвления явной:
ВЫБРАТЬ
Таблица.Ссылка КАК Ссылка,
Таблица.Дата КАК Дата
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Таблица
ГДЕ
ВЫБОР
КОГДА &ТолькоПустые ТОГДА Таблица.Дата = ДАТАВРЕМЯ(1, 1, 1)
ИНАЧЕ ИСТИНА
КОНЕЦ
Важно: Обратите внимание на типичную ошибку начинающих разработчиков: попытку в ветке ИНАЧЕ возвращать само поле (например, ИНАЧЕ Таблица.Дата). Секция ГДЕ ожидает только логическое выражение (возвращающее ИСТИНА или ЛОЖЬ), поэтому возвращать значение поля даты туда нельзя.
Проанализируем производительность. Конструкции вида ГДЕ НЕ &Параметр ИЛИ Поле = Значение или операторы ВЫБОР в секции ГДЕ могут приводить к деградации плана выполнения в СУБД (MS SQL Server, PostgreSQL). СУБД не может заранее определить, будет ли использоваться индекс по дате, и выполняет полное сканирование таблицы (Table Scan) вместо эффективного поиска по индексу (Index Seek).
Чтобы избежать падения производительности на больших объемах данных, сформируем текст запроса динамически на встроенном языке:
Запрос = Новый Запрос;
ТекстЗапроса =
"ВЫБРАТЬ
| Таблица.Ссылка КАК Документ,
| Таблица.Дата КАК ДатаДокумента
|ИЗ
| Документ.ПоступлениеТоваровУслуг КАК Таблица";
Если ТолькоПустыеДаты Тогда
ТекстЗапроса = ТекстЗапроса + "
|ГДЕ
| Таблица.Дата = ДАТАВРЕМЯ(1, 1, 1)";
КонецЕсли;
Запрос.Текст = ТекстЗапроса;
Результат = Запрос.Выполнить();
При таком подходе СУБД получает идеально оптимизированный запрос: если флаг не установлен, фильтрация отсутствует вовсе; если установлен — используется индекс по полю даты без лишних логических ветвлений.
Если необходимо избежать прямого склеивания строк в коде и работать с запросом как с объектной моделью, воспользуемся встроенным объектом СхемаЗапроса:
СхемаЗапроса и загружаем исходный текст без условий.Выбрать.Условия.ПолучитьТекст().Посмотрим на пример реализации:
Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(
"ВЫБРАТЬ
| Таблица.Ссылка КАК Документ,
| Таблица.Дата КАК ДатаДокумента
|ИЗ
| Документ.ПоступлениеТоваровУслуг КАК Таблица");
ОператорВыбрать = Схема.ПакетЗапросов[0].Операторы[0];
Если ТолькоПустыеДаты Тогда
ОператорВыбрать.Условия.Добавить("Таблица.Дата = ДАТАВРЕМЯ(1, 1, 1)");
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = Схема.ПолучитьТекст();
Выборка = Запрос.Выполнить().Выбрать();
Если разработка ведется в отчете на СКД, писать сложные логические условия в тексте запроса не требуется. Рассмотрим два стандартных механизма СКД:
ВЫБРАТЬ
Таблица.Ссылка КАК Документ,
Таблица.Дата КАК ДатаДокумента
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Таблица
{ГДЕ
Таблица.Дата = ДАТАВРЕМЯ(1, 1, 1)}
Дата и устанавливаем вид сравнения Не заполнено. Механизм компоновки автоматически сформирует корректный отбор как для пустой даты 0001-01-01, так и для значений NULL.