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

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

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

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

Базовый алгоритм решения через условие ИМЕЮЩИЕ

Для начала проанализируем самую очевидную логику: нам нужно сгруппировать данные по покупателю и посчитать количество уникальных месяцев, в которых у него были документы продаж. Если за год набралось ровно 12 уникальных месяцев с покупками, значит, этот клиент совершал покупки каждый месяц.

Посмотрим на пример базовой структуры запроса:


ВЫБРАТЬ 
    Т.Контрагент КАК Покупатель 
ИЗ 
    ТаблицаПродаж КАК Т 
ГДЕ 
    Т.Период МЕЖДУ &НачалоГода И &КонецГода 
СГРУППИРОВАТЬ ПО 
    Т.Контрагент 
ИМЕЮЩИЕ 
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т.ПериодМесяц) = 12

В этом коде ключевую роль играет конструкция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...). Она гарантирует, что даже если в одном месяце клиент сделал 10 покупок, этот месяц посчитается только один раз. Условие ИМЕЮЩИЕ отсеет всех контрагентов, у которых итоговое количество уникальных месяцев будет меньше требуемого.

Как правильно получить месяц покупки в запросе

Частая ошибка начинающих разработчиков — попытка считать уникальные значения напрямую по полю Период или Дата документа. Поле Дата содержит данные вплоть до секунды. Если мы применим к нему группировку, система посчитает количество различных секунд (или дней), а не месяцев. В результате клиент, купивший товар 12 раз в один месяц, попадет в отчет, что нарушает условие задачи.

Для корректного приведения даты к началу месяца воспользуемся встроенной функцией языка запросов НАЧАЛОПЕРИОДА. Разберем, как это выглядит на практике:


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

Оптимизация: обращаемся к регистрам, а не к документам

Проанализируем ситуацию с производительностью. Если мы строим отчет за целый год, обращение к таблицам самих документов (например, Документ.РасходнаяНакладная) приведет к существенному замедлению работы программы. Таблицы документов не оптимизированы для построения сложных аналитических выборок за большие периоды.

Рассмотрим более грамотный подход: использование виртуальных таблиц оборотов регистров накопления, например РегистрНакопления.Продажи.Обороты. Платформа аппаратно оптимизирует работу с такими таблицами. Более того, если мы укажем параметр периодичности Месяц при обращении к виртуальной таблице, система автоматически создаст поле Период, которое уже будет содержать готовую дату начала месяца.

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


ВЫБРАТЬ
    ПродажиОбороты.Контрагент КАК Покупатель
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоГода, &КонецГода, Месяц, ) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
    ПродажиОбороты.Контрагент
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ПродажиОбороты.Период) = 12

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

Избавляемся от жесткой привязки к числу месяцев

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

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

  1. Вычисление параметра в модуле: Перед выполнением запроса в коде мы можем вычислить разницу в месяцах между датами начала и конца, а затем передать полученное число в качестве параметра &КоличествоМесяцев.
  2. Вычисление прямо в запросе: Мы можем использовать внутреннюю функцию РАЗНОСТЬДАТ.

Пример динамического условия:


ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ПродажиОбороты.Период) = РАЗНОСТЬДАТ(&НачалоГода, &КонецГода, МЕСЯЦ) + 1

Альтернативный метод: использование таблицы-эталона (Календаря)

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

Суть алгоритма:

  1. Создаем временную таблицу со списком всех месяцев за наш период выборки.
  2. Используем ЛЕВОЕ СОЕДИНЕНИЕ нашей таблицы периодов с таблицей продаж по каждому покупателю.
  3. Если при соединении хотя бы по одному месяцу результат оказался пустым (равным NULL), такой контрагент отсеивается из итоговой таблицы.

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

Решение бизнес-задачи готовыми средствами: XYZ-анализ

Проанализируем саму постановку задачи с точки зрения бизнес-логики. Поиск клиентов, которые совершают покупки строго каждый месяц без единого пропуска, — задача весьма узкая и жесткая. На практике таким образом отсеивается подавляющее большинство покупателей, включая тех, кто делает стабильные покупки раз в полтора месяца.

В современных конфигурациях 1С (таких как ERP, Комплексная автоматизация, Управление торговлей) для маркетологов существует встроенный механизм — XYZ-анализ. Система сама вычисляет коэффициент вариации и классифицирует клиентов:

  1. Категория X — клиенты со стабильными регулярными покупками, чьи объемы не сильно колеблются из месяца в месяц.
  2. Категория Y — клиенты с нерегулярными покупками, возможными всплесками и падениями активности.
  3. Категория Z — случайные или разовые клиенты.

Использование встроенного отчета по XYZ-анализу часто полностью закрывает реальную потребность маркетологов в поиске «постоянных» клиентов без необходимости написания сложных самописных запросов.

← На главную