При разработке запросов в платформе 1С:Предприятие нередко возникает задача сопоставить дату документа с тем же самым календарным днем, но с жестко заданным временем (например, отобрать документы, проведенные строго после 22:00 текущего дня). Попытка собрать такую дату через конструкцию ДАТАВРЕМЯ с вызовом функций извлечения года, месяца и дня приводит к синтаксической ошибке. Разберем причины этой ошибки и рассмотрим корректные способы решения данной задачи.
Проанализируем частую ошибку начинающих разработчиков. Конструкция ДАТАВРЕМЯ в языке запросов 1С является литералом (константой, определяемой на этапе компиляции запроса), а не встроенной функцией СУБД. Это накладывает строгое ограничение: в качестве аргументов литерал принимает исключительно фиксированные целочисленные значения.
Внутри конструкции ДАТАВРЕМЯ запрещено использовать:
ГОД, МЕСЯЦ, ДЕНЬ, ВЫРАЗИТЬ).Документ.Дата).&ТекущаяДата).Поэтому попытка передать вычисляемые выражения приведет к ошибке Неверные параметры "ДАТАВРЕМЯ".
Рассмотрим наиболее простое, универсальное и читаемое решение. Чтобы получить дату документа с фиксированным временем (например, 22:00:00), сначала обнулим время до начала суток с помощью функции НАЧАЛОПЕРИОДА, а затем прибавим нужное количество часов функцией ДОБАВИТЬКДАТЕ.
Посмотрим на пример условия в секции ГДЕ:
ГДЕ
Документ.Дата >= ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ), ЧАС, 22)
Разберем по шагам, как работает это выражение:
НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ) сбрасывает временную часть поля на 00:00:00.ДОБАВИТЬКДАТЕ(..., ЧАС, 22) прибавляет к полученному значению 22 часа, формируя дату вида ГГГГ-ММ-ДД 22:00:00.Документ.Дата.Если требуется указать не только часы, но также минуты и секунды (например, 22:30:15), смещение можно задать в секундах, рассчитав общее смещение от начала дня:
ГДЕ
Документ.Дата >= ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ), СЕКУНДА, 81015)
Проанализируем альтернативный вариант — сравнение времени через функцию РАЗНОСТЬДАТ. С ее помощью можно вычислить, сколько секунд прошло от начала дня документа до момента его проведения.
Посмотрим на пример запроса для проверки равенства времени 22:00:00:
ГДЕ
РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ), Документ.Дата, СЕКУНДА) = 22 * 3600
Важное замечание: при использовании функции РАЗНОСТЬДАТ необходимо учитывать порядок аргументов. Функция вычитает первый параметр из второго. Кроме того, для точного сравнения времени следует указывать периодичность СЕКУНДА, так как при использовании периодичности ЧАС функция вычисляет количество пересечений границ часа, а не точный интервал времени.
Если задача заключается только в проверке времени без сложных манипуляций с границами интервалов, рассмотрим применение стандартных функций извлечения составляющих времени:
ГДЕ
ЧАС(Документ.Дата) >= 22
Этот способ компактен, если точность до минут и секунд не требуется.
Выясним причину возможного падения производительности при использовании таких конструкций. Поле Дата в таблицах документов и регистров 1С обычно входит в состав индексов базы данных. Применение любых функций (НАЧАЛОПЕРИОДА, ДОБАВИТЬКДАТЕ, РАЗНОСТЬДАТ, ЧАС) к полю таблицы в блоке ГДЕ делает условие неселективным (non-sargable) для СУБД. В результате СУБД не сможет применить поиск по индексу (Index Seek) и будет выполнять полное сканирование таблицы или индекса (Table/Index Scan).
Если запрос выполняется по конкретному дню или периоду, оптимальнее рассчитать граничные значения даты и времени в коде 1С перед выполнением запроса и передать их в виде параметров:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Дата КАК Дата
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Док.Дата >= &ГраницаВремени";
Запрос.УстановитьПараметр("ГраницаВремени", КонецДня(ТекущаяДата()) - 2 * 3600); // 22:00:00
Результат = Запрос.Выполнить();
При использовании ДОБАВИТЬКДАТЕ и НАЧАЛОПЕРИОДА в запросах с левыми соединениями или потенциально пустыми датами помните: применение функций к значению NULL или пустой дате 0001-01-01 00:00:00 (особенно при вычитании интервалов) может вызвать ошибки выхода за допустимый диапазон дат СУБД. В таких ситуациях используйте предварительную проверку через ЕСТЬNULL или конструкцию ВЫБОР КОГДА.