Как в запросе 1С сделать отбор по числу и месяцу даты рождения?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с реквизитами типа Дата в платформе 1С (например, при получении списка именинников из справочника ИнформационныеКарты или Контрагенты) часто возникает задача сделать отбор по дню и месяцу, полностью проигнорировав год рождения. Рассмотрим основные способы решения этой задачи в языке запросов 1С, а также важные нюансы, связанные с пустыми датами, 29 февраля и производительностью.

Способ 1. Использование встроенных функций ДЕНЬ() и МЕСЯЦ()

Язык запросов 1С предоставляет встроенные функции ДЕНЬ() и МЕСЯЦ(), которые позволяют извлечь числовые значения дня и месяца из поля даты прямо в запросе.

Если необходимо получить список именинников на текущий день (или конкретную дату, переданную в параметр &ТекДата), проанализируем следующий пример запроса:


ВЫБРАТЬ
    ИнформационныеКарты.Ссылка КАК Ссылка,
    ИнформационныеКарты.ДеньРождения КАК ДеньРождения
ИЗ
    Справочник.ИнформационныеКарты КАК ИнформационныеКарты
ГДЕ
    ИнформационныеКарты.ДеньРождения <> ДАТАВРЕМЯ(1, 1, 1)
    И ДЕНЬ(ИнформационныеКарты.ДеньРождения) = ДЕНЬ(&ТекДата)
    И МЕСЯЦ(ИнформационныеКарты.ДеньРождения) = МЕСЯЦ(&ТекДата)

Если требуется выбрать элементы за фиксированный день (например, 21 декабря), условие в секции ГДЕ можно записать через конкретные числа:


ГДЕ
    ИнформационныеКарты.ДеньРождения <> ДАТАВРЕМЯ(1, 1, 1)
    И ДЕНЬ(ИнформационныеКарты.ДеньРождения) = 21
    И МЕСЯЦ(ИнформационныеКарты.ДеньРождения) = 12

Важное замечание о пустых датах: Незаполненная дата в платформе 1С хранится как 01.01.0001 00:00:00. Вызов функций ДЕНЬ() и МЕСЯЦ() для пустой даты возвращает значение 1. Поэтому при поиске именинников на 1 января без проверки ДеньРождения <> ДАТАВРЕМЯ(1, 1, 1) в выборку ошибочно попадут все элементы с незаполненной датой рождения.

Способ 2. Отбор именинников за интервал дат (период)

Если задача усложняется и требуется выбрать дни рождения за произвольный период (например, на ближайшую неделю), простой отбор по номерам дней и месяцев работать не будет. В этом случае дату рождения приводят к текущему году с помощью функции ДОБАВИТЬКДАТЕ().

Рассмотрим пример запроса для отбора контрагентов, чьи дни рождения попадают в интервал от &ДатаНач до &ДатаКон:


ВЫБРАТЬ 
    Контрагенты.Ссылка КАК Контрагент,
    Контрагенты.ДатаРождения КАК ДатаРождения
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    НЕ Контрагенты.ЭтоГруппа
    И Контрагенты.ДатаРождения <> ДАТАВРЕМЯ(1, 1, 1)
    И ВЫБОР
        КОГДА ((ДОБАВИТЬКДАТЕ(Контрагенты.ДатаРождения, ГОД, (ГОД(&ДатаНач) - ГОД(Контрагенты.ДатаРождения))) >= &ДатаНач 
            И ДОБАВИТЬКДАТЕ(Контрагенты.ДатаРождения, ГОД, (ГОД(&ДатаНач) - ГОД(Контрагенты.ДатаРождения))) <= &ДатаКон)
            ИЛИ (ДОБАВИТЬКДАТЕ(Контрагенты.ДатаРождения, ГОД, (ГОД(&ДатаКон) - ГОД(Контрагенты.ДатаРождения))) <= &ДатаКон 
            И ДОБАВИТЬКДАТЕ(Контрагенты.ДатаРождения, ГОД, (ГОД(&ДатаКон) - ГОД(Контрагенты.ДатаРождения))) >= &ДатаНач)) ТОГДА
            ИСТИНА
        ИНАЧЕ
            ЛОЖЬ
    КОНЕЦ

Данный подход корректно обрабатывает даже те периоды, которые пересекают границу года (например, с 25 декабря по 10 января).

Особенности работы с родившимися 29 февраля

При обработке дат рождения следует учитывать високосные года:

  1. При использовании функции ДОБАВИТЬКДАТЕ() для приведения года дата 29.02 в невисокосном году автоматически трансформируется платформой 1С в 28.02.
  2. Если используется прямое сравнение ДЕНЬ = 29 И МЕСЯЦ = 2, то в невисокосные года такие люди не попадут в отчет. Чтобы поздравить их 28 февраля, в условие запроса при проверке на 28 февраля можно добавить логический блок ИЛИ (ДЕНЬ(Дата) = 29 И МЕСЯЦ(Дата) = 2).

Оптимизация производительности на больших справочниках

Использование функций ДЕНЬ(), МЕСЯЦ() или ДОБАВИТЬКДАТЕ() непосредственно в условии ГДЕ делает невозможным использование индексов СУБД по полю даты. В результате СУБД выполняет сканирование всех записей таблицы (Table Scan), что на справочниках объемом в десятки и сотни тысяч записей приводит к падению скорости выполнения запроса.

Для высоконагруженных систем рекомендуется следующий архитектурный подход:

  1. Добавить в справочник два дополнительных числовых реквизита: ДеньРожденияДень и ДеньРожденияМесяц (или один строковый реквизит вида "ММДД").
  2. Установить для этих реквизитов признак Индексировать.
  3. Заполнять их в событии ПередЗаписью модуля объекта при изменении основной даты рождения.
  4. В запросах выполнять фильтрацию прямо по индексированным реквизитам без использования вычисляемых функций.
← На главную