Как в запросе 1С выбрать последнее посещение контрагента, который ничего не купил?

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

В практике разработчика 1С часто возникают задачи, связанные с анализом поведения клиентов. Одна из типичных ситуаций: необходимо выделить сегмент «неэффективных» посещений. Представим, что у нас есть справочник Контрагенты и подчиненный ему справочник ПосещенияМагазина. Нам нужно получить список тех клиентов, которые заходили к нам (возможно, многократно), но так и не совершили ни одной покупки за всю историю. При этом для анализа причин нам нужна не просто информация о факте, а данные именно из последней строки посещения.

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

Анализ логики и постановка условий

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

  1. Тотальное исключение: Если у контрагента в подчиненном справочнике есть хотя бы одна запись, где ДатаОплаты заполнена, этот контрагент нам не интересен.
  2. Группировка по последнему событию: Для оставшихся «безрезультатных» контрагентов нужно найти дату самого последнего визита.
  3. Получение детальных данных: По найденной дате и контрагенту нужно извлечь конкретный элемент справочника ПосещенияМагазина, чтобы прочитать его специфические реквизиты (например, комментарий менеджера или цель визита).

Решение через использование временных таблиц (ВТ)

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

Шаг 1: Отбор «удачливых» контрагентов

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


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПосещенияМагазина.Владелец КАК Контрагент
ПОМЕСТИТЬ ВТ_КупившиеКонтрагенты
ИЗ
    Справочник.ПосещенияМагазина КАК ПосещенияМагазина
ГДЕ
    ПосещенияМагазина.ДатаОплаты > ДАТАВРЕМЯ(1, 1, 1)
;

Шаг 2: Поиск последнего посещения для остальных клиентов

Теперь мы выберем всех контрагентов, которых нет в нашей первой таблице ВТ_КупившиеКонтрагенты, и найдем для каждого из них максимальную дату посещения. Проанализируем ситуацию: нам важно использовать ЛЕВОЕ СОЕДИНЕНИЕ или конструкцию НЕ В.


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

Шаг 3: Получение финальных данных

На заключительном этапе мы соединяем нашу временную таблицу с исходным справочником ПосещенияМагазина. Соединение выполняется по двум полям: Владелец и ДатаПосещения. Это позволит нам получить ссылку на конкретный элемент и все его реквизиты.


ВЫБРАТЬ
    ПоследниеВизиты.Контрагент,
    Посещения.Ссылка КАК ПоследнееПосещение,
    Посещения.Комментарий,
    Посещения.Менеджер
ИЗ
    ВТ_ПоследниеВизиты КАК ПоследниеВизиты
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПосещенияМагазина КАК Посещения
    ПО ПоследниеВизиты.Контрагент = Посещения.Владелец
    И ПоследниеВизиты.МаксДата = Посещения.ДатаПосещения
;

Альтернативный подход: Использование конструкции ИМЕЮЩИЕ

Если задача кажется слишком громоздкой для временных таблиц, можно попробовать решить её одним запросом, используя агрегатные функции и секцию ИМЕЮЩИЕ. Посмотрим на пример, как отобрать контрагентов, у которых максимальная дата оплаты является пустой.


ВЫБРАТЬ
    ПосещенияМагазина.Владелец КАК Контрагент,
    МАКСИМУМ(ПосещенияМагазина.ДатаПосещения) КАК ДатаПоследнегоВизита
ИЗ
    Справочник.ПосещенияМагазина КАК ПосещенияМагазина
СГРУППИРОВАТЬ ПО
    ПосещенияМагазина.Владелец
ИМЕЮЩИЕ
    МАКСИМУМ(ПосещенияМагазина.ДатаОплаты) = ДАТАВРЕМЯ(1, 1, 1)

Выясним причину, почему этот способ может быть опасен: если в будущем в логику добавится NULL (например, при внешнем соединении), условие с пустой датой может не сработать. Кроме того, этот запрос вернет только дату, но не позволит вытащить другие реквизиты строки без повторного соединения с таблицей.

Важные технические нюансы

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

  1. Работа с пустой датой: В 1С пустая дата — это 0001-01-01. В запросах ее лучше передавать через параметр или использовать конструкцию ДАТАВРЕМЯ(1, 1, 1). Помните, что NULL и пустая дата — это разные сущности. NULL возникает там, где записи физически не существует (например, при левом соединении).
  2. Проблема дублей по времени: Если в системе зарегистрировано два посещения одного и того же клиента в одну и ту же секунду, соединение по дате в финальном запросе вернет две строки. Чтобы этого избежать, рекомендуется в ВТ_ПоследниеВизиты дополнительно выбирать МАКСИМУМ(Ссылка) и в финальном запросе соединяться именно по ссылке.
  3. Индексация: Для быстродействия запроса убедитесь, что реквизиты Владелец и ДатаПосещения проиндексированы. В подчиненных справочниках поле Владелец индексируется автоматически, а вот для даты это нужно проверить в конфигураторе.

Разберем еще один момент: почему мы используем именно ВНУТРЕННЕЕ СОЕДИНЕНИЕ в финальном шаге? Поскольку в ВТ_ПоследниеВизиты мы уже отобрали только тех, кто нам нужен, внутреннее соединение отсечет лишние данные и будет работать быстрее, чем левое.

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

← На главную