Задача отбора сотрудников или контрагентов, у которых сегодня день рождения, является классической при разработке систем оповещений, отчетов или CRM-механизмов. Несмотря на кажущуюся простоту, реализация этой логики именно в запросе (на языке запросов 1С) имеет свои особенности, связанные с производительностью и корректной обработкой дат. В этой статье мы подробно разберем, как правильно составить такой запрос, какие функции использовать и как избежать типичных ошибок.
Самый простой и очевидный способ — это использование встроенных функций языка запросов 1С, которые позволяют извлечь числовые значения дня и месяца из даты рождения. Проанализируем стандартную конструкцию, которая подходит для большинства повседневных задач.
Для того чтобы получить список именинников на текущую дату, нам необходимо передать в запрос параметры текущего дня и текущего месяца. Рассмотрим пример кода:
ВЫБРАТЬ
Сотрудники.Ссылка КАК Сотрудник,
Сотрудники.ДатаРождения КАК ДатаРождения
ИЗ
Справочник.Сотрудники КАК Сотрудники
ГДЕ
МЕСЯЦ(Сотрудники.ДатаРождения) = &ТекущийМесяц
И ДЕНЬ(Сотрудники.ДатаРождения) = &ТекущийДень
В данном примере мы используем функции МЕСЯЦ() и ДЕНЬ(). Они возвращают числовое значение (от 1 до 12 и от 1 до 31 соответственно). Перед выполнением запроса в коде 1С необходимо установить параметры следующим образом:
Запрос.УстановитьПараметр("ТекущийМесяц", Месяц(ТекущаяДата()));
Запрос.УстановитьПараметр("ТекущийДень", День(ТекущаяДата()));
Этот метод интуитивно понятен и легко читается, однако он имеет свои ограничения, о которых мы поговорим далее.
Разберем ситуацию, когда в вашей базе данных хранятся десятки или сотни тысяч записей (например, в справочнике «Контрагенты»). Использование функций ДЕНЬ() и МЕСЯЦ() непосредственно в условии ГДЕ делает запрос «не-SARGable» (Search Argumentable). Это означает, что СУБД (MS SQL Server, PostgreSQL и др.) не сможет использовать индекс по полю ДатаРождения.
Вместо того чтобы быстро найти нужные записи по индексу, сервер будет вынужден выполнить Index Scan или Table Scan — то есть прочитать все записи таблицы, применить к каждой функцию и сравнить результат. Чтобы оптимизировать работу системы, профессиональные разработчики применяют следующие подходы:
ДеньРождения и МесяцРождения. Они заполняются программно в обработчике ПередЗаписью. В запросе поиск идет по этим полям без использования функций.Если ваша база невелика, стандартного подхода с функциями будет достаточно, но для высоконагруженных систем оптимизация критически важна.
Проанализируем специфический случай, который часто упускают из виду: поздравление людей, родившихся 29 февраля. В обычный год (например, 2023) в календаре просто нет 29-го числа. Если мы будем использовать жесткое сравнение ДЕНЬ(ДатаРождения) = 29, такие люди не попадут в выборку 28 февраля, и их праздник будет проигнорирован системой.
Для корректного решения этой задачи в невисокосный год мы должны считать 28 февраля днем рождения и для тех, кто родился 28-го, и для тех, кто родился 29-го. Выясним, как реализовать это условие в запросе:
ВЫБРАТЬ
Контрагенты.Наименование
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
(МЕСЯЦ(Контрагенты.ДатаРождения) = &ТекущийМесяц И ДЕНЬ(Контрагенты.ДатаРождения) = &ТекущийДень)
ИЛИ (&ЭтоОсобыйСлучай29Февраля
И МЕСЯЦ(Контрагенты.ДатаРождения) = 2
И ДЕНЬ(Контрагенты.ДатаРождения) = 29)
В коде перед запросом нужно вычислить флаг &ЭтоОсобыйСлучай29Февраля. Он должен быть равен Истина только в том случае, если текущая дата — 28 февраля и текущий год является невисокосным.
Рассмотрим более продвинутый метод сравнения дат, который часто используется для поиска именинников в определенном интервале (например, «кто празднует ДР на следующей неделе»). Суть метода заключается в том, чтобы «привести» дату рождения человека к текущему году и сравнить полученную дату с периодом.
Однако здесь кроется ловушка: если мы прибавим разницу лет к дате 29.02.1992, чтобы получить 2023 год, запрос может выдать ошибку или NULL, так как даты 29.02.2023 не существует. Безопаснее всего приводить все даты к гарантированно високосному году, например к 2000-му.
Посмотрим на пример логики:
В языке запросов 1С это реализуется через сложную конструкцию ДОБАВИТЬКДАТЕ, но из-за ограничений функций работы с датами в запросе, этот метод чаще реализуется через формирование временной таблицы дат периода или через СКД.
Если вы создаете отчет на СКД, задача упрощается. В СКД доступны расширения языка запросов и возможность использования выражений. Рассмотрим, как это сделать наиболее эффективно:
В поле «Выражение» вычисляемого поля можно использовать стандартные функции 1С. Однако для фильтрации в настройках отчета лучше всего использовать параметры. СКД позволяет гибко настраивать отбор, используя встроенные функции ДЕНЬ() и МЕСЯЦ() в условиях компоновки. Важный момент: стандартная функция ДЕНЬГОДА() в 1С не подходит для этой задачи, так как порядковый номер дня (например, 60-й день года) будет смещаться в зависимости от того, високосный текущий год или нет.
Подводя итог, можно выделить основные правила отбора по дням рождения в запросе:
МЕСЯЦ(Дата) = &М и ДЕНЬ(Дата) = &Д.Используя эти методы, вы сможете построить надежную и быструю систему учета дней рождения в любой конфигурации на платформе 1С:Предприятие.