Как реализовать выборку данных по индивидуальным (скользящим) периодам для каждого контрагента в 1С

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

В типовых конфигурациях 1С большинство отчетов строится на основе календарных периодов: день, неделя, месяц или год. Однако в управленческом анализе часто возникает задача отслеживания активности клиентов (например, звонков, заказов или оплат) в рамках индивидуальных жизненных циклов. Это означает, что для Контрагента А "неделя" может начинаться в понедельник, а для Контрагента Б — в среду, в зависимости от даты их первого взаимодействия с компанией. Рассмотрим подробно, как реализовать такой механизм с помощью запросов и вспомогательных объектов платформы 1С.

Постановка задачи: календарный период против скользящего

Традиционный подход к учету предполагает использование функции НАЧАЛОПЕРИОДА. Если мы хотим посчитать звонки за неделю, мы просто ограничиваем выборку условием Дата МЕЖДУ &НачалоНедели И &КонецНедели. В этом случае все звонки всех клиентов попадают в одни и те же временные рамки.

Скользящий период (rolling period) работает иначе. Нам необходимо:

  1. Определить дату самого первого события (звонка) для каждого контрагента.
  2. От этой даты отсчитать фиксированный интервал (например, 7 дней для недели или 30 дней для месяца).
  3. Сгруппировать все последующие события по этим индивидуальным интервалам.

Такой подход позволяет проводить когортный анализ: мы можем сравнивать поведение клиентов на их первой, второй или третьей неделе обслуживания, независимо от того, в какой календарный день они к нам пришли.

Решение 1: Использование временных таблиц и среза первых дат

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

Для начала нам нужно найти точку отсчета для каждого клиента. Воспользуемся агрегатной функцией МИНИМУМ:


ВЫБРАТЬ
    ТаблицаЗвонков.Контрагент КАК Контрагент,
    МИНИМУМ(ТаблицаЗвонков.ДатаЗвонка) КАК ДатаПервогоЗвонка
ПОМЕСТИТЬ ВТ_ПервыеЗвонки
ИЗ
    РегистрСведений.Звонки КАК ТаблицаЗвонков
СГРУППИРОВАТЬ ПО
    ТаблицаЗвонков.Контрагент;

После того как мы получили таблицу ВТ_ПервыеЗвонки, мы можем построить сетку периодов. Рассмотрим пример, где мы хотим рассчитать количество звонков именно в первую неделю от момента касания. Разберем по шагам соединение основной таблицы звонков с нашей временной таблицей:


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

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

Решение 2: Математический расчет индекса периода

Для вывода списка всех периодов (1-я неделя, 2-я неделя и т.д.) без создания бесконечного количества соединений, можно использовать расчет "номера периода" от базовой даты. Это позволит сгруппировать данные по вычисляемому полю.

Выясним причину, почему это удобнее: использование функции РАЗНОСТЬДАТ позволяет нам получить количество дней между первым звонком и текущим, а затем простым делением определить номер интервала. Рассмотрим пример кода:


ВЫБРАТЬ
    Звонки.Контрагент КАК Контрагент,
    ВТ_ПервыеЗвонки.ДатаПервогоЗвонка КАК БазоваяДата,
    // Вычисляем номер недели (0 - первая неделя, 1 - вторая и т.д.)
    ВЫРАЗИТЬ((РАЗНОСТЬДАТ(ВТ_ПервыеЗвонки.ДатаПервогоЗвонка, Звонки.ДатаЗвонка, ДЕНЬ)) / 7 КАК ЧИСЛО(10, 0)) КАК НомерНедели,
    КОЛИЧЕСТВО(*) КАК КолВоЗвонков
ИЗ
    РегистрСведений.Звонки КАК Звонки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПервыеЗвонки КАК ВТ_ПервыеЗвонки
    ПО Звонки.Контрагент = ВТ_ПервыеЗвонки.Контрагент
СГРУППИРОВАТЬ ПО
    Звонки.Контрагент,
    ВТ_ПервыеЗвонки.ДатаПервогоЗвонка,
    (РАЗНОСТЬДАТ(ВТ_ПервыеЗвонки.ДатаПервогоЗвонка, Звонки.ДатаЗвонка, ДЕНЬ)) / 7;

Важно помнить, что в языке запросов 1С при делении целых чисел результат может округляться по разным правилам в зависимости от используемой СУБД. Поэтому иногда надежнее использовать конструкцию ВЫБОР КОГДА или предварительно рассчитанную таблицу интервалов.

Решение 3: Использование регистра смещений (для гибкой настройки)

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

Посмотрим на пример запроса, который учитывает индивидуальное смещение в днях, заданное в ресурсе регистра СмещениеПериодаДней:


ВЫБРАТЬ
    ВТЗвонки.АвторЗвонка КАК АвторЗвонка,
    КОЛИЧЕСТВО(ВЫБОР
        КОГДА ДОБАВИТЬКДАТЕ(ВТЗвонки.ДатаЗвонка, ДЕНЬ, -Настройки.СмещениеПериодаДней) < &НачалоПериода
            ТОГДА NULL
        ИНАЧЕ ВТЗвонки.Звонок
    КОНЕЦ) КАК ЗвонокКолВо
ИЗ
    ВТЗвонки КАК ВТЗвонки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПериодов КАК Настройки
    ПО Настройки.Объект = ВТЗвонки.АвторЗвонка
ГДЕ
    ВТЗвонки.ДатаЗвонка МЕЖДУ &ДатаС И &ДатаПо
СГРУППИРОВАТЬ ПО
    ВТЗвонки.АвторЗвонка;

Этот метод позволяет динамически "сдвигать" границы календарного периода для каждого пользователя, делая отчетность более гибкой.

Оптимизация производительности: Регистры накопления

При работе с большими объемами данных (сотни тысяч звонков) расчет "на лету" через МИНИМУМ и соединения временных таблиц может существенно замедлить работу системы. Проанализируем, как можно оптимизировать этот процесс:

  1. Индексация: Поля Контрагент и ДатаЗвонка в регистре звонков обязательно должны быть проиндексированы. В языке запросов при создании временных таблиц всегда используйте конструкцию ИНДЕКСИРОВАТЬ ПО.
  2. Регистр накопления: Вместо использования регистра сведений для звонков, можно создать РегистрНакопления. При проведении документа "Звонок" система может программно определять дату первого звонка клиента (хранящуюся в отдельном регистре сведений) и записывать в измерение регистра накопления уже готовый период (например, дату начала индивидуальной недели).
  3. Регламентные задания: Если данные нужны только для аналитических отчетов, можно создать регламентное задание, которое раз в сутки будет заполнять агрегированную таблицу (срез), где данные уже распределены по индивидуальным периодам. Это позволит строить отчеты мгновенно.

Рекомендации по реализации в СКД

При использовании Системы компоновки данных (СКД) для таких отчетов, рекомендуется:

Таким образом, реализация индивидуальных периодов в 1С требует отхода от стандартных функций работы с датами и перехода к логике вычисления смещений. Наиболее стабильным и производительным решением является сочетание временных таблиц с предварительным поиском "даты инициации" клиента.

← На главную