При разработке отчетов по сотрудникам (например, списков именинников или анализа возрастного состава) программисты часто сталкиваются с проблемой: стандартная группировка по дате рождения учитывает год. Из-за этого сотрудники, родившиеся в один и тот же день, но в разные годы, попадают в разные строки. Нам же требуется собрать их по месяцам или дням, игнорируя год рождения. Рассмотрим, как решить эту задачу эффективно, используя язык запросов и Систему компоновки данных (СКД).
В системе 1С:Предприятие тип Дата всегда содержит год, месяц, день, часы, минуты и секунды. Если мы просто выберем поле ДатаРождения в запросе и попробуем сгруппировать по нему данные в СКД, система создаст отдельную группу для каждой уникальной даты. Например, 15.10.1980 и 15.10.1990 — это разные значения.
Проанализируем ситуацию: нам нужно «обнулить» год, приведя все даты к одному базовому значению, чтобы группировка сработала корректно, но при этом сохранить возможность выводить название месяца ("Октябрь") и сортировать данные хронологически.
Самый надежный способ «убрать» год в запросе — это вычислить разницу между текущим (или базовым) годом и годом рождения, а затем добавить эту разницу к дате рождения. Рассмотрим пример кода, который приводит все даты к 1-му году нашей эры (0001 год):
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(Сотрудники.ДатаРождения, ГОД, 1 - ГОД(Сотрудники.ДатаРождения)) КАК ПериодМесяц,
Сотрудники.Ссылка КАК Сотрудник
ИЗ
Справочник.Сотрудники КАК Сотрудники
Разберем, что происходит в этом выражении: функция ГОД(Сотрудники.ДатаРождения) возвращает число (например, 1985). Мы вычитаем его из единицы (1 - 1985 = -1984) и добавляем полученное количество лет к исходной дате. В результате получаем дату вида 15.10.0001. Теперь все сотрудники, родившиеся 15 октября любого года, будут иметь идентичное значение в поле ПериодМесяц.
При использовании 0001 года как базового можно столкнуться с ошибкой, если сотрудник родился 29 февраля. Так как 0001 год не является високосным, система может выдать пустую дату или ошибку при попытке преобразования. Чтобы избежать этого, рекомендуется использовать в качестве базового любой високосный год, например, 2000-й.
ДОБАВИТЬКДАТЕ(Сотрудники.ДатаРождения, ГОД, 2000 - ГОД(Сотрудники.ДатаРождения))
Это гарантирует, что дата 29.02.1988 превратится в 29.02.2000, а не исчезнет из отчета.
Если нам нужно сгруппировать данные строго по месяцам, мы можем использовать функцию МЕСЯЦ(). Она возвращает число от 1 до 12.
ВЫБРАТЬ
МЕСЯЦ(Сотрудники.ДатаРождения) КАК НомерМесяца,
КОЛИЧЕСТВО(Сотрудники.Ссылка) КАК КоличествоСотрудников
ИЗ
Справочник.Сотрудники КАК Сотрудники
СГРУППИРОВАТЬ ПО
МЕСЯЦ(Сотрудники.ДатаРождения)
Однако у этого метода есть недостаток: в отчете мы увидим цифры (1, 2, 3...) вместо красивых названий ("Январь", "Февраль"). Чтобы это исправить, в СКД придется либо писать сложное выражение ВЫБОР КОГДА..., либо настраивать форматирование через выражения представления.
Для отчета по возрастным диапазонам нам сначала нужно вычислить точный возраст. Простое вычитание лет ГОД(ТекущаяДата) - ГОД(ДатаРождения) не совсем корректно, так как оно не учитывает, наступил ли день рождения в этом году. Выясним правильную формулу для запроса:
ВЫБОР
КОГДА МЕСЯЦ(Сотрудники.ДатаРождения) < МЕСЯЦ(&ТекущаяДата)
ИЛИ (МЕСЯЦ(Сотрудники.ДатаРождения) = МЕСЯЦ(&ТекущаяДата) И ДЕНЬ(Сотрудники.ДатаРождения) <= ДЕНЬ(&ТекущаяДата))
ТОГДА РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД)
ИНАЧЕ РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД) - 1
КОНЕЦ КАК Возраст
После вычисления возраста мы можем распределить сотрудников по диапазонам, используя конструкцию ВЫБОР прямо в запросе или в вычисляемом поле СКД:
ВЫБОР
КОГДА Возраст < 20 ТОГДА "До 20 лет"
КОГДА Возраст МЕЖДУ 20 И 30 ТОГДА "20-30 лет"
КОГДА Возраст МЕЖДУ 31 И 45 ТОГДА "31-45 лет"
ИНАЧЕ "Старше 45 лет"
КОНЕЦ КАК ВозрастнойДиапазон
Вернемся к полю ПериодМесяц, где мы нормализовали дату к виду 15.10.2000. Чтобы в отчете это выглядело профессионально, выполним следующие настройки в компоновке данных:
ПериодМесяц установим тип значения Дата.ДФ=ММММ. Это заставит систему выводить только полное название месяца.ПериодМесяц.Преимущество такого подхода: так как в поле по-прежнему хранится дата (пусть и с измененным годом), СКД автоматически отсортирует колонки в хронологическом порядке: Январь, Февраль, Март и так далее. Если бы мы использовали просто названия месяцев как строки, они бы отсортировались по алфавиту (Август, Апрель, Декабрь...).
Мы рассмотрели, как при помощи функции ДОБАВИТЬКДАТЕ привести дни рождения к одному году, чтобы обеспечить корректную группировку. Посмотрим еще раз на финальную структуру запроса, которая объединяет все приемы:
ВЫБРАТЬ
Сотрудники.Ссылка КАК Сотрудник,
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(Сотрудники.ДатаРождения, ГОД, 2000 - ГОД(Сотрудники.ДатаРождения)), МЕСЯЦ) КАК МесяцДляГруппировки,
ВЫБОР
КОГДА РАЗНОСТЬДАТ(Сотрудники.ДатаРождения, &ТекущаяДата, ГОД) < 25 ТОГДА "Молодежь"
ИНАЧЕ "Основной состав"
КОНЕЦ КАК Категория
ИЗ
Справочник.Сотрудники КАК Сотрудники
Использование функции НАЧАЛОПЕРИОДА дополнительно гарантирует, что все даты месяца будут приведены к его первому числу, что окончательно исключает "размножение" строк в группировках отчета.