В языке запросов платформы 1С:Предприятие работа с датами является одной из фундаментальных задач для разработчиков и аналитиков. Часто возникает необходимость отфильтровать данные не по полному диапазону времени, а по конкретному значению дня месяца, игнорируя год или месяц. Например, вам может потребоваться найти все документы, созданные 25-го числа любого месяца, или сгруппировать продажи по дням месяца для анализа сезонных всплесков.
Для решения таких задач система предоставляет встроенную функцию День(), которая извлекает числовое значение дня из поля типа Дата. Понимание принципов работы этой функции и особенностей её использования в условиях отбора критически важно для написания производительных запросов. Неправильное применение может привести к тому, что оптимизатор запросов не сможет использовать индексы, что существенно замедлит работу базы данных.
В данной статье мы подробно разберем синтаксис функции, рассмотрим примеры её использования в различных сценариях и обсудим нюансы, которые необходимо учитывать при проектировании отчетов в 1С:Предприятие. Мы также затронем вопросы альтернативных подходов и типичных ошибок, допускаемых при манипуляциях с временными метками.
Синтаксис и принцип работы функции День()
Функция День() является стандартной функцией языка запросов и принимает в качестве единственного аргумента выражение типа Дата. Результатом выполнения функции всегда является целое число от 1 до 31, соответствующее дню месяца указанной даты. Важно понимать, что тип возвращаемого значения — Число, что позволяет использовать его в арифметических операциях или сравнивать с другими числовыми константами.
При использовании в условии отбора функция вычисляется для каждой строки выборки. Это означает, что если вы напишете условие День(ДатаДокумента) = 15, система пройдет по всем записям, извлечет день из даты и сравнит его с пятнадцатым числом. Синтаксически вызов выглядит следующим образом:
ВЫБРАТЬ
Документ.РеализацияТоваровУслуг.Ссылка КАК Ссылка,
Документ.РеализацияТоваровУслуг.Дата КАК Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ.РеализацияТоваровУслуг
ГДЕ
День(Документ.РеализацияТоваровУслуг.Дата) = 25
Стоит отметить, что функция корректно обрабатывает даты високосных лет и разные длины месяцев. Если в месяце нет 31 числа, то условие День(Дата) = 31 просто не вернет записей за февраль, апрель, июнь и другие месяцы с меньшим количеством дней. Это поведение является ожидаемым и логичным для календарных расчетов.
Используйте функцию День() только когда вам действительно нужно игнорировать месяц и год. Для выборки за конкретный период эффективнее использовать обычные сравнения дат (БольшеИлиРавно, Меньше).
Фильтрация данных по конкретному дню месяца
Одним из самых распространенных сценариев использования является поиск событий, привязанных к определенной дате календаря, независимо от периода их возникновения. Например, бухгалтерия может искать все акты сверки, сформированные в день зарплаты — 10-го и 25-го числа каждого месяца. В таких случаях оператор ИЛИ в сочетании с функцией День() становится незаменимым инструментом.
Рассмотрим пример запроса, который выбирает все документы поступления товаров, созданные 1-го числа любого месяца. Такая выборка часто нужна для анализа документов, сформированных в первый день отчетного периода:
- 📅 Фильтрация по первому дню месяца позволяет быстро собрать документы открытия периода.
- 📊 Анализ кассовой дисциплины часто требует выборки операций по конкретным дням выплаты авансов.
- 🔍 Поиск ошибок в данных может быть упрощен, если известно, что сбой происходил всегда в определенное число.
Однако следует быть осторожным при использовании таких условий в больших базах данных с миллионами записей. Если таблица не содержит специализированных индексов, поддерживающих функциональные зависимости, запрос может привести к полному сканированию таблицы (Full Table Scan). Это может заблокировать работу других пользователей в моменты пиковой нагрузки.
⚠️ Внимание: Использование функций от полей в условии ГДЕ часто отключает использование обычных индексов по дате. Перед внедрением такого запроса в высоконагруженную систему обязательно протестируйте его план выполнения.
Группировка и агрегация по дням месяца
Функция День() также широко применяется в блоке ВЫБРАТЬ и СГРУППИРОВАТЬ ПО для формирования аналитических отчетов. Это позволяет увидеть распределение активности внутри месяца. Например, менеджеру может быть интересно, в какие дни месяца клиенты чаще всего делают заказы, чтобы оптимизировать график работы склада или колл-центра.
При группировке все записи с одинаковым номером дня (например, все 5-е числа за год) будут объединены в одну строку итога. Это дает возможность рассчитать суммарные показатели: оборот, количество документов или средний чек для каждого дня месяца. Ниже приведен пример структуры такого запроса:
ВЫБРАТЬ
День(ЗаказКлиента.Дата) КАК ДеньМесяца,
СУММА(ЗаказКлиента.Сумма) КАК ОбщийОборот,
КОЛИЧЕСТВО(ЗаказКлиента.Ссылка) КАК КоличествоЗаказов
ИЗ
Документ.ЗаказКлиента КАК ЗаказКлиента
ГДЕ
ЗаказКлиента.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
День(ЗаказКлиента.Дата)
УПОРЯДОЧИТЬ ПО
ДеньМесяца
Результатом такого запроса станет таблица из 31 строки (или меньше, если в периоде нет некоторых дней), где каждая строка соответствует дню месяца. Это наглядно демонстрирует, как 1С:Предприятие позволяет трансформировать временные ряды в удобную для анализа форму без необходимости выгрузки данных во внешние системы.
Группировка по функции День() идеально подходит для выявления внутримесячной сезонности, но скрывает межмесячную динамику.
Сравнение производительности и альтернативные методы
Хотя функция День() удобна в написании, с точки зрения производительности она не всегда является оптимальным решением. Когда вы применяете функцию к полю в условии отбора, серверу 1С приходится вычислять значение функции для каждой записи перед сравнением. В отличие от этого, сравнение диапазонов дат может использовать индекс по полю даты напрямую, что работает на порядки быстрее.
Если ваша задача состоит не в поиске "всех 15-х чисел за 10 лет", а в выборке данных за конкретный месяц с фильтром по дням, эффективнее использовать явное указание диапазона. Например, вместо День(Дата) >= 10 И День(Дата) <= 20, лучше написать условие на полную дату:
- 🚀 Использование диапазонов дат (
МЕЖДУ) позволяет задействовать индексы базы данных. - ⚙️ Функции от полей увеличивают нагрузку на процессор сервера 1С.
- 💾 При работе с архивными данными разница во времени выполнения может составлять минуты и часы.
Для сложных случаев, когда требуется частая фильтрация по дню месяца, некоторые архитекторы баз данных рекомендуют вводить в регистры дополнительные реквизиты, например, ДеньМесяца, и хранить там вычисленное значение. Это позволяет индексировать само число и выполнять отбор мгновенно, не вычисляя функцию на лету.
⚠️ Внимание: Интерфейс и возможности оптимизации запросов могут различаться в зависимости от используемой СУБД (MS SQL, PostgreSQL, Oracle) и версии платформы 1С. Всегда сверяйте план выполнения запроса в конкретной среде.
Работа с календарями и производственными графиками
В производственных и торговых конфигурациях часто встречается задача сопоставления даты документа с производственным календарем. Функция День() помогает определить, попадает ли дата на выходной или праздничный день, если эти данные хранятся в виде справочника дней месяца. Например, можно проверить, является ли текущий день последним рабочим днем месяца.
Также эта функция полезна при расчете сроков оплаты или доставки, которые привязаны к конкретным датам. Если договор предусматривает оплату "до 10-го числа каждого месяца", то в запросе на выборку просроченной задолженности можно использовать условие День(ДатаОплаты) > 10 в сочетании с проверкой месяца.
| Сценарий использования | Пример условия в запросе | Рекомендация по оптимизации |
|---|---|---|
| Выборка зарплатных ведомостей | День(Дата) = 10 |
Добавить индекс по вычисляемому полю или использовать диапазон дат. |
| Аналитика продаж по дням | СГРУППИРОВАТЬ ПО День(Дата) |
Допустимо для отчетов, выполняемых редко или по небольшим выборкам. |
| Поиск документов за сегодня | День(Дата) = День(&ТекущаяДата) |
Лучше использовать НачалоДня(&ТекущаяДата) и КонецДня. |
При работе с международными компаниями важно помнить о различиях в календарях, однако функция День() в 1С всегда возвращает число согласно григорианскому календарю, что унифицирует обработку данных в распределенных информационных базах.
Как обрабатывать високосные годы?
Функция День() автоматически учитывает високосные годы. 29 февраля будет иметь значение 29, а 1 марта — значение 3. Никаких дополнительных проверок на високосность при извлечении дня не требуется.
Типичные ошибки и рекомендации разработчикам
Одной из частых ошибок является попытка сравнить результат функции День() со строковым значением. Поскольку функция возвращает число, сравнение День(Дата) = "05" может работать некорректно или требовать неявного преобразования типов, что негативно сказывается на производительности. Всегда используйте числовые литералы: День(Дата) = 5.
Еще один нюанс связан с часовыми поясами. Если сервер 1С и клиент находятся в разных часовых поясах, значение даты может сдвинуться, и, следовательно, извлеченный день месяца может отличаться от ожидаемого пользователем. В таких случаях необходимо явно приводить дату к нужному часовому поясу перед применением функции.
- ✅ Всегда проверяйте типы данных в сравнениях (Число с Числом).
- ✅ Учитывайте часовой пояс сервера при критичных к времени расчетах.
- ✅ Избегайте вложенных функций, если это возможно (например,
День(ДатаВремяСтрока(...))).
Разработчикам также рекомендуется документировать сложные запросы с функциями от полей, чтобы при последующей поддержке другие специалисты понимали логику выборки и не пытались "оптимизировать" её, удалив необходимую функцию.
⚠️ Внимание: При обновлении платформы 1С поведение некоторых системных функций может незначительно изменяться. Проверяйте релиз-ноты новой версии перед миграцией критичных отчетов.
☑️ Проверка запроса перед запуском
Можно ли использовать функцию День() в условиях соединения таблиц?
Технически это возможно, но крайне не рекомендуется. Соединение по условию с функцией от поля (ЛЕВОЕ СОЕДИНЕНИЕ ... ПО День(Т1.Дата) = День(Т2.Дата)) практически гарантированно приведет к декартовому произведению или полному перебору, что убьет производительность. Лучше присоединять таблицы по полному ключу или диапазону дат.
Что вернет функция День(), если дата равна NULL?
В языке запросов 1С, если поле Дата содержит значение NULL (незаполнено), то функция День() также вернет NULL. При сравнении NULL = 5 результат будет ложным, и такая запись не попадет в выборку. Для обработки таких случаев используйте функцию ЕСТЬNULL().
Есть ли разница между функциями День() и День(Период)?
Да, есть. Функция День() работает с точной датой (включая время, хотя время игнорируется при извлечении дня). Функция День(Период) предназначена для работы с типом Период в регистрах накопления и срезах, учитывая границы интервалов. В обычных запросах к документам используется классическая функция День().
Как получить последний день месяца с помощью функции День()?
Сама по себе функция День() только извлекает значение. Чтобы получить последний день месяца, нужно использовать функцию КонецМесяца(Дата), а затем, при необходимости, применить к результату функцию День(), чтобы получить числовое значение (28, 29, 30 или 31).
Влияет ли формат отображения даты в конфигураторе на работу функции?
Нет, не влияет. Функция День() работает с внутренним представлением даты в базе данных. Формат отображения (ДД.ММ.ГГГГ или ММ/ДД/ГГГГ), установленный в настройках пользователя или конфигуратора, касается только визуальной части и не меняет логику вычислений на сервере.