Как в запросе 1С рассчитать среднее количество вакансий за период?

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

При построении аналитических отчетов по кадровому составу и подбору персонала в 1С часто возникает задача расчета средней численности открытых позиций (вакансий) за выбранный интервал времени (месяц, квартал или год). Базовая формула расчета выглядит как отношение суммы действующих вакансий на каждый день интервала к общему количеству календарных дней в периоде.

Рассмотрим структуру типовых данных: в конфигурациях (например, 1С:Зарплата и Управление Персоналом) для вакансий обычно задаются реквизиты ДатаОткрытия и ДатаЗакрытия (или фактическая дата закрытия). Если дата закрытия не заполнена, позиция считается активной по текущий момент. Разберем типичные ошибки при реализации такого алгоритма и рассмотрим два правильных способа построения запроса.

Почему не работает классический «срез последних»

Проанализируем распространенную ошибку: попытку применить методику среза последних на каждую дату через агрегатную функцию МАКСИМУМ(ДатаОткрытия). Срез последних предназначен для периодических регистров сведений, где каждая новая запись полностью вытесняет предыдущую. В случае справочников или документов с интервальными реквизитами каждая вакансия представляет собой независимый интервал действия. Попытка сгруппировать данные по максимальной дате открытия приводит к объединению разных позиций и искажению выборки. Кроме того, простое условие ДатаКалендаря >= Вакансии.ДатаОткрытия без проверки даты закрытия приводит к избыточному соединению, при котором закрытые вакансии продолжают ошибочно учитываться.

Способ 1. Расчет через длительность интервалов (без производственного календаря)

Разберем наиболее производительный и математически точный подход. Вместо деления периода на отдельные дни и создания соединения «многие ко многим», мы можем вычислить фактическое количество дней действия каждой вакансии внутри анализируемого периода.

Алгоритм решения по шагам:

  1. Отбираем вакансии, которые пересекаются с интервалом отчета: дата открытия меньше или равна концу периода, а дата закрытия не заполнена или больше либо равна началу периода.
  2. Ограничиваем границы действия каждой позиции рамками периода: если вакансия открыта раньше начала периода, берем дату начала периода; если дата закрытия пустая или позже конца периода, берем дату конца периода.
  3. Вычисляем количество дней активности позиции с помощью функции РАЗНОСТЬДАТ. Так как обе границы включаются, добавляем 1 день.
  4. Суммируем полученные дни по всем вакансиям и делим на общую продолжительность периода в днях.

Посмотрим на пример текста запроса:


ВЫБРАТЬ
    Вакансии.Подразделение КАК Подразделение,
    Вакансии.Должность КАК Должность,
    СУММА(
        РАЗНОСТЬДАТ(
            ВЫБОР
                КОГДА Вакансии.ДатаОткрытия < &НачалоПериода
                    ТОГДА &НачалоПериода
                ИНАЧЕ НАЧАЛОПЕРИОДА(Вакансии.ДатаОткрытия, ДЕНЬ)
            КОНЕЦ,
            ВЫБОР
                КОГДА Вакансии.ДатаЗакрытия ЕСТЬ NULL
                        ИЛИ Вакансии.ДатаЗакрытия = ДАТАВРЕМЯ(1, 1, 1)
                        ИЛИ Вакансии.ДатаЗакрытия > &КонецПериода
                    ТОГДА &КонецПериода
                ИНАЧЕ НАЧАЛОПЕРИОДА(Вакансии.ДатаЗакрытия, ДЕНЬ)
            КОНЕЦ,
            ДЕНЬ
        ) + 1
    ) / (РАЗНОСТЬДАТ(&НачалоПериода, &КонецПериода, ДЕНЬ) + 1) КАК СреднееКоличествоВакансий
ИЗ
    Справочник.Вакансии КАК Вакансии
ГДЕ
    Вакансии.ДатаОткрытия <= &КонецПериода
    И (Вакансии.ДатаЗакрытия ЕСТЬ NULL
        ИЛИ Вакансии.ДатаЗакрытия = ДАТАВРЕМЯ(1, 1, 1)
        ИЛИ Вакансии.ДатаЗакрытия >= &НачалоПериода)
СГРУППИРОВАТЬ ПО
    Вакансии.Подразделение,
    Вакансии.Должность

Способ 2. Подневный расчет через соединение с календарем

Рассмотрим второй вариант, который применяется, если требуется получить подневную динамику или рассчитывать среднее количество вакансий только с учетом рабочих дней. В этом случае используется регистр сведений РегламентированныйПроизводственныйКалендарь или собственный календарный классификатор.

Разберем реализацию запроса с соединением на каждый день:


ВЫБРАТЬ
    Календарь.ДатаКалендаря КАК ДатаКалендаря,
    Вакансии.Подразделение КАК Подразделение,
    Вакансии.Должность КАК Должность,
    Вакансии.Ссылка КАК Вакансия
ПОМЕСТИТЬ ВТ_ВакансииПоДням
ИЗ
    РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК Календарь
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Вакансии КАК Вакансии
        ПО Календарь.ДатаКалендаря >= НАЧАЛОПЕРИОДА(Вакансии.ДатаОткрытия, ДЕНЬ)
            И Календарь.ДатаКалендаря <= ВЫБОР
                КОГДА Вакансии.ДатаЗакрытия ЕСТЬ NULL
                        ИЛИ Вакансии.ДатаЗакрытия = ДАТАВРЕМЯ(1, 1, 1)
                    ТОГДА ДАТАВРЕМЯ(3999, 12, 31)
                ИНАЧЕ НАЧАЛОПЕРИОДА(Вакансии.ДатаЗакрытия, ДЕНЬ)
            КОНЕЦ
ГДЕ
    Календарь.ДатаКалендаря МЕЖДУ &НачалоПериода И &КонецПериода
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Подразделение КАК Подразделение,
    ВТ.Должность КАК Должность,
    КОЛИЧЕСТВО(ВТ.Вакансия) / (РАЗНОСТЬДАТ(&НачалоПериода, &КонецПериода, ДЕНЬ) + 1) КАК СреднееКоличествоВакансий
ИЗ
    ВТ_ВакансииПоДням КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Подразделение,
    ВТ.Должность

Особенности и тонкости платформы 1С при расчетах

Проанализируем важные нюансы, которые необходимо учитывать при составлении запросов:

  1. Обработка незаполненных дат: В платформе 1С пустая дата может быть представлена как значение NULL или как пустая дата 0001-01-01 00:00:00 (в языке запросов — литерал ДАТАВРЕМЯ(1, 1, 1)). В условиях всегда следует обрабатывать обе эти ситуации.
  2. Особенности функции РАЗНОСТЬДАТ: Функция считает количество переходов между датами. Например, для интервала с 01.09 по 01.09 результат функции РАЗНОСТЬДАТ(Дата1, Дата2, ДЕНЬ) равен 0, хотя физически вакансия действовала 1 день. Поэтому для включения обеих границ всегда прибавляется единица.
  3. Учет времени: Если реквизиты дат содержат время (например, конец периода задан как 23:59:59), перед расчетом разности дат необходимо приводить значения к началу дня с помощью функции НАЧАЛОПЕРИОДА(..., ДЕНЬ), чтобы избежать некорректного подсчета дней.
  4. Заполненность производственного календаря: При использовании второго способа убедитесь, что регистр РегламентированныйПроизводственныйКалендарь заполнен на весь период отчета, иначе пропущенные даты исказят итоговую сумму.
← На главную