При разработке сложных отчетов или обработке больших массивов данных в 1С:Предприятие разработчики часто сталкиваются с проблемой фильтрации записей по дате. Казалось бы, задача проста: выбрать все документы за конкретное число. Однако тип ДатаВремя в платформе хранит и календарную дату, и время суток с точностью до секунды. Если вы просто сравните поле базы данных с датой, введенной через интерфейс, выборка может оказаться пустой или неполной.
Это происходит потому, что при вводе даты пользователем время часто устанавливается в текущий момент или остается нулевым, тогда как в базе данных у документов время регистрации может быть любым в течение суток. Чтобы корректно отобрать данные, необходимо программно обнулить время или привести дату к началу дня. Существует несколько штатных функций языка запросов, позволяющих решить эту задачу эффективно.
В этой статье мы подробно разберем три основных способа получения даты без времени: использование функции НАЧАЛОДНЯ(), математическое округление через ОКР() и специфический прием с префиксом. Понимание нюансов каждого метода поможет избежать ошибок производительности и логических неточностей в ваших отчетах.
Проблема сравнения дат в запросах 1С
Основная сложность заключается в природе хранения данных. Поле типа ДатаВремя занимает 8 байт и представляет собой количество тиков с определенной эпохи. Когда вы пишете условие ГДЕ Дата = &ДатаПараметр, система ищет точное совпадение до миллисекунды. Если в параметре &ДатаПараметр передано значение "15.05.2026 12:30:00", а в таблице лежит запись "15.05.2026 10:15:00", условие не выполнится, хотя визуально даты совпадают.
Особенно критично это становится при работе с регистрами накопления или документами, где время проведения играет ключевую роль. Неопытные разработчики часто пытаются решать проблему через преобразование типов в коде 1С перед передачей параметра, но это не всегда удобно и иногда невозможно при использовании динамических списков или СКД.
⚠️ Внимание: Никогда не используйте простое сравнение "Дата = Параметр" для отбора данных за целый день, если вы не уверены на 100%, что время в параметре установлено в 00:00:00. Это гарантированно приведет к потере части записей.
Для корректной работы необходимо использовать специальные функции языка запросов, которые «отрезают» временную составляющую. Эти функции выполняются на стороне СУБД (MS SQL, PostgreSQL или встроенной), что обеспечивает высокую скорость обработки даже на больших объемах данных.
Метод округления через функцию ОКР
Один из самых старых и надежных способов получить дату без времени — использование функции ОКР(). Логика метода основана на том, что одни сутки составляют число 1. Если мы разделим дату на 1 и округлим результат до 0 знаков после запятой, мы получим целое число, соответствующее началу суток.
Синтаксис выглядит следующим образом: ОКР(Дата / 1, 0). Здесь деление на 1 переводит дату в числовой формат (количество дней), а округление отбрасывает дробную часть, которая как раз и отвечает за время. Результатом будет дата с временем 00:00:00.
Этот метод универсален и работает во всех версиях платформы, начиная с самых ранних. Он особенно удобен, когда нужно выполнить арифметические операции с датами, так как результат сразу приводится к числовому контексту, который затем вновь интерпретируется как дата.
Однако у метода есть нюанс: он может быть чуть менее читаемым для новичков по сравнению с более современными функциями. Тем не менее, в сложных вычислениях, где дата участвует в формулах вместе с другими числами, использование ОКР часто бывает оправдано.
ВЫБРАТЬ
РегистрНакопления.Продажи.Период КАК Период,
ОКР(РегистрНакопления.Продажи.Период / 1, 0) КАК ДатаБезВремени
ИЗ
РегистрНакопления.Продажи КАК РегистрНакопления.Продажи
Использование функции НАЧАЛОДНЯ
Начиная с определенных версий платформы 1С:Предприятие 8, появилась более семантически понятная функция НАЧАЛОДНЯ(). Она делает ровно то, что написано в названии: возвращает дату, соответствующую началу дня для переданного значения. Время в результате всегда будет равно 00:00:00.
Этот подход рекомендуется как основной для новой разработки. Код становится самодокументируемым: любой разработчик, читающий запрос, сразу понимает намерение автора, не вникая в математические трюки с делением и округлением. Кроме того, функция оптимизирована движком запросов.
Использование НАЧАЛОДНЯ() особенно удобно при группировке данных. Например, если вам нужно сгруппировать продажи не по точному времени проведения, а по дням, эта функция станет незаменимым инструментом в разделе СГРУППИРОВАТЬ ПО.
Функция НАЧАЛОДНЯ() работает быстрее на некоторых СУБД, чем ручное округление, так как использует нативные функции базы данных для усечения времени.
Если передать ей дату с уже обнуленным временем, она вернет то же самое значение без дополнительных вычислительных затрат. Это делает её безопасной для использования в циклах или рекурсивных запросах.
Сравнение производительности методов
Вопрос производительности при обработке миллионов записей всегда стоит остро. Различия между методами могут быть незаметны на малых выборках, но становятся критичными в высоконагруженных системах. Ниже приведена таблица, сравнивающая основные характеристики подходов.
| Метод | Читаемость кода | Производительность | Совместимость |
|---|---|---|---|
НАЧАЛОДНЯ() |
Высокая | Оптимальная | Современные версии 8.х |
ОКР(Дата/1, 0) |
Средняя | Высокая | Все версии платформы |
| Префикс "Д" | Низкая | Средняя | Только строковые представления |
| Диапазон (Между) | Высокая | Очень высокая | Универсально |
Как видно из таблицы, использование диапазона дат часто оказывается самым быстрым способом, так как позволяет СУБД использовать индексы по полю даты наиболее эффективным образом без применения функций к самому полю в условии ГДЕ.
Применение функций непосредственно к полям таблицы в условиях отбора (например, ГДЕ НАЧАЛОДНЯ(Таблица.Дата) =..) может в некоторых случаях приводить к полному сканированию таблицы (Table Scan), если оптимизатор запросов не сможет использовать индекс. Поэтому для больших таблиц предпочтительнее использовать диапазон.
⚠️ Внимание: Избегайте применения функций к полям, по которым построены индексы, в левой части условия сравнения. Это может отключить использование индекса и замедлить запрос в десятки раз.
Формирование диапазона дат для отбора
Самый правильный с точки зрения архитектуры баз данных способ выбрать данные за день — это задать диапазон от начала дня до начала следующего дня. Это позволяет использовать индекс по дате в режиме поиска по диапазону (Range Seek), что является максимально быстрым вариантом.
Для реализации этого подхода в запросе 1С необходимо сформировать два параметра или вычисляемых поля: НачалоДня и КонецДня (исключая). Условие в запросе будет выглядеть как использование оператора МЕЖДУ или пары условий >= и <.
- 📅 Используйте
НАЧАЛОДНЯ(&Дата)для получения нижней границы диапазона. - ⏭️ Для верхней границы используйте
НАЧАЛОДНЯ(&Дата) + 1, что даст начало следующих суток. - ✅ В условии пишите:
ГДЕ Таблица.Дата >= &Начало И Таблица.Дата < &Конец.
Такой подход гарантирует, что вы захватите все записи от 00:00:00 до 23:59:59 включительно, не теряя данные из-за погрешностей округления или особенностей хранения времени.
☑️ Проверка диапазона дат
Специфические приемы и префиксы
Существует менее известный, но интересный прием, связанный с внутренним представлением даты в строковом формате. В языке запросов 1С дату можно задать строковой константой с префиксом. Например, "Д20260515" будет интерпретировано как дата 15.05.2026 с обнуленным временем.
Этот синтаксис удобен при написании жестко заданных запросов или при формировании динамических строк запроса в коде. Префикс "Д" указывает системе, что строка должна быть преобразована в дату, при этом время по умолчанию считается нулевым.
Секреты строковых констант
Помимо префикса "Д" для даты, существует префикс "В" для времени и "Ч" для числа. Например, "В123000" превратится в время 12:30:00. Это полезно при жестком кодировании значений в запросах.
Однако использовать этот метод в параметризированных запросах, где значение приходит от пользователя, не рекомендуется. Лучше явно преобразовывать типы или использовать функции даты, чтобы избежать ошибок формата и обеспечить локализацию.
Обработка null и неопределенных значений
При работе с датами в 1С важно учитывать возможность наличия пустых значений (NULL или НЕОПРЕДЕЛЕНО). Функции НАЧАЛОДНЯ() и ОКР() корректно обрабатывают неопределенные значения, возвращая НЕОПРЕДЕЛЕНО, но это может повлиять на логику отбора.
Если в условии сравнения участвует неопределенное значение, результат сравнения также станет неопределенным, и запись не попадет в выборку. Это стандартное поведение SQL, о котором стоит помнить при проектировании отчетов, где дата может быть не заполнена.
Для защиты от таких ситуаций можно использовать функцию ЕСТЬNULL() или ВЫБОР внутри запроса, подменяя пустые даты на заведомо некорректные значения или исключая их отдельным условием.
Лучшая практика для выборки за день — использование диапазона [НачалоДня, НачалоДня + 1), так как это обеспечивает максимальную производительность за счет использования индексов.
В чем разница между НАЧАЛОДНЯ() и ОКР()?
Функция НАЧАЛОДНЯ() является семантически более правильной и читаемой. Она явно указывает на намерение получить начало суток. Функция ОКР() — это математический трюк, который дает тот же результат, но требует понимания внутреннего устройства типа ДатаВремя. В современном коде следует предпочитать НАЧАЛОДНЯ().
Почему запрос с функцией в условии работает медленно?
Когда вы применяете функцию к полю таблицы (например, НАЧАЛОДНЯ(Дата)), СУБД часто вынуждена вычислять значение функции для каждой строки таблицы перед сравнением. Это предотвращает использование индекса по полю Дата, приводя к полному перебору всех записей. Использование диапазона значений позволяет СУБД сразу перейти к нужному участку индекса.
Как получить вчерашнюю дату без времени?
Для получения вчерашней даты используйте выражение НАЧАЛОДНЯ(СЕГОДНЯ() - 1). Функция СЕГОДНЯ() возвращает текущую дату с обнуленным временем, вычитание единицы дает вчера, а повторное применение НАЧАЛОДНЯ() гарантирует очистку времени, хотя в случае с СЕГОДНЯ() это часто избыточно, но безопасно.
Можно ли использовать этот метод для регистров сведений?
Да, методы обнуления времени универсальны для всех типов объектов 1С, где используется тип ДатаВремя. Это касается документов, регистров накопления, регистров сведений и любых других табличных частей. Логика работы с датами едина для всей платформы.