Как в СКД посчитать сотрудников, принятых в одном месяце, а уволенных в том же или следующем?

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

В процессе разработки отчетов в 1С часто возникают задачи, требующие нетривиальной работы с датами и периодами. Классический пример — анализ текучести кадров, где условия отбора зависят от нескольких дат одновременно. Рассмотрим ситуацию: необходимо создать отчет в Системе Компоновки Данных (СКД), который для каждого месяца будет показывать количество принятых сотрудников, а также количество уволенных, причем уволенными считаются те, кто был принят в этом месяце, а уволен в этом же или в следующем месяце.

Например, для колонки "Июль" отчет должен показать:

  1. Количество сотрудников с датой приема в июле.
  2. Количество сотрудников с датой приема в июле и датой увольнения в июле или августе.

Эта логика должна динамически применяться для каждого месяца, который попадает в период отчета. Разберем два эффективных способа решения этой задачи средствами запроса для СКД.

Решение 1: Соединение таблицы с самой собой (Self-Join)

Этот метод является наиболее лаконичным для данной конкретной задачи и был выбран автором вопроса на форуме. Идея заключается в том, чтобы в одном запросе получить данные и о приеме, и об увольнении, соединив таблицу сотрудников саму с собой.

Проанализируем логику по шагам:

  1. Основная таблица. Берем справочник Сотрудники как основной источник. Из него мы получим всех сотрудников и их даты приема. Назовем этот экземпляр таблицы Сотрудники.
  2. Присоединяемая таблица. Берем тот же справочник Сотрудники еще раз, но уже как второй источник данных, который мы присоединим к первому. Назовем этот экземпляр Сотрудники1. Он будет служить исключительно для проверки условия увольнения.
  3. Тип соединения. Используем ЛЕВОЕ СОЕДИНЕНИЕ. Это позволит нам включить в результат всех сотрудников из основной таблицы (то есть всех принятых), даже если для них не найдется соответствующей записи об увольнении в заданном интервале.
  4. Условия соединения. Это ключевой момент. Соединение должно выполняться по двум условиям:
    • Сотрудники.сотрудник = Сотрудники1.сотрудник — это гарантирует, что мы сравниваем даты приема и увольнения одного и того же человека.
    • Условие по дате увольнения. Дата увольнения из таблицы Сотрудники1 должна попадать в нужный нам двухмесячный интервал. Этот интервал начинается с первого дня месяца приема и заканчивается последним днем следующего за ним месяца.
  5. Агрегация и группировка. Группируем результат по месяцу приема, чтобы получить итоговые цифры для каждого месяца. Затем считаем количество записей.

Посмотрим на итоговый запрос:

В этом запросе мы используем функции языка запросов 1С для работы с датами:

Вот как выглядит полный текст запроса:


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Сотрудники.датаприем, МЕСЯЦ) КАК МесяцПриема,
    КОЛИЧЕСТВО(Сотрудники.сотрудник) КАК Принято,
    КОЛИЧЕСТВО(Сотрудники1.сотрудник) КАК Уволено
ИЗ
    Сотрудники КАК Сотрудники
        ЛЕВОЕ СОЕДИНЕНИЕ Сотрудники КАК Сотрудники1
        ПО Сотрудники.сотрудник = Сотрудники1.сотрудник
        И (Сотрудники1.датаувольнения МЕЖДУ НАЧАЛОПЕРИОДА(Сотрудники.датаприем, МЕСЯЦ) И КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(Сотрудники.датаприем, МЕСЯЦ, 1), МЕСЯЦ))
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(Сотрудники.датаприем, МЕСЯЦ)

Как это работает?

Функция КОЛИЧЕСТВО(Сотрудники.сотрудник) посчитает всех сотрудников, сгруппированных по месяцу приема, так как для каждой записи из левой таблицы (Сотрудники) сотрудник всегда будет существовать. Это наш показатель "Принято".

Функция КОЛИЧЕСТВО(Сотрудники1.сотрудник) посчитает только те записи, для которых левое соединение нашло соответствие. Соответствие находится только тогда, когда сотрудник из Сотрудники1 не равен NULL, то есть когда его дата увольнения попала в заданный интервал МЕЖДУ .... Это и будет наш показатель "Уволено".

Этот подход элегантен и решает задачу в рамках одного запроса без лишних усложнений.

Решение 2: Использование временной таблицы периодов (Календаря)

Этот метод является более универсальным и мощным, особенно когда отчет требует анализа по многим пересекающимся или сложным периодам. Суть подхода в том, чтобы создать "основу" отчета — виртуальную таблицу, содержащую все периоды (в нашем случае — месяцы), которые должны быть в отчете. А уже к этой таблице-календарю присоединять необходимые данные.

Разберем по шагам:

  1. Создание таблицы периодов. Первым делом нужно сформировать временную таблицу, например, ВТ_Периоды, с одной колонкой Месяц. В этой колонке будут храниться даты, соответствующие первым дням каждого месяца в рамках периода отчета (например, 01.07.2023, 01.08.2023, 01.09.2023...). Такой календарь гарантирует, что в отчете будут присутствовать все месяцы, даже если в них не было движения сотрудников. На форуме был предложен сложный, но универсальный запрос для генерации такой таблицы на лету на основе таблицы цифр.
  2. Основной запрос к данным. Вторым шагом будет написание запроса, который присоединяет данные о сотрудниках к нашему календарю.
    • За основу берем наш календарь ВТ_Периоды.
    • Присоединяем к нему справочник Сотрудники с помощью ЛЕВОГО СОЕДИНЕНИЯ. Условие связи: месяц из календаря должен совпадать с месяцем приема сотрудника (ВТ_Периоды.Месяц = НАЧАЛОПЕРИОДА(Сотрудники.ДатаПриема, МЕСЯЦ)).
  3. Вычисляемые поля для агрегации. Вместо сложных соединений для подсчета уволенных, мы можем использовать условные выражения прямо в списке полей выбора. Мы создадим поле, которое будет равно 1, если сотрудник уволен в нужный период, и 0 в противном случае.

Посмотрим на пример запроса для этого подхода:

Предположим, временная таблица ВТ_Периоды уже создана. Тогда основной запрос для СКД будет выглядеть так:


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

Настройка ресурсов в СКД:

Этот запрос не группирует данные, он возвращает детальные записи. Всю магию агрегации мы переносим в настройки СКД на вкладку "Ресурсы":

Преимущества этого метода:

В итоге, оба подхода приводят к желаемому результату. Первый метод (self-join) — это быстрое и элегантное решение для конкретно этой задачи. Второй метод (с календарем) — более фундаментальный и масштабируемый, который стоит взять на вооружение для построения сложных аналитических отчетов в СКД.

← На главную