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

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

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

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

Метод 1: Использование временной таблицы и группировки по максимальной ссылке

Это классический и наиболее производительный метод. Основная проблема заключается в том, что в одну секунду может быть проведено несколько документов. Чтобы система однозначно определила, какой документ считать «последним», мы будем использовать комбинацию даты и уникального идентификатора (ссылки). Согласно документации 1С, порядок документов определяется их моментом времени, который включает дату и ссылку.

Рассмотрим алгоритм решения:

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

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


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

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

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

Почему МАКСИМУМ(Ссылка) — это важно?

Выясним причину, по которой недостаточно просто использовать МАКСИМУМ(Дата). Если у вас в 23:59:59 было две продажи разным клиентам, запрос по максимуму даты вернет обе записи (если соединять по дате). Использование ссылки как дополнительного критерия — это стандарт платформы 1С для обеспечения детерминированности (однозначности) выборки. Ссылки сравниваются побайтово, и одна из них всегда будет «больше» другой.

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

Метод 2: Использование функции АВТОНОМЕРЗАПИСИ (для версии 8.3.10+)

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

Рассмотрим пример реализации:


ВЫБРАТЬ ПЕРВЫЕ 1000000
    НАЧАЛОПЕРИОДА(Реализация.Дата, ДЕНЬ) КАК ПериодДень,
    Реализация.Контрагент,
    Реализация.Ссылка,
    АВТОНОМЕРЗАПИСИ() КАК НомерСтроки
ПОМЕСТИТЬ ВТ_ПронумерованныеПродажи
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
УПОРЯДОЧИТЬ ПО
    ПериодДень,
    Реализация.Дата,
    Реализация.Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ1.ПериодДень,
    ВТ1.Контрагент
ИЗ
    ВТ_ПронумерованныеПродажи КАК ВТ1
ГДЕ
    ВТ1.НомерСтроки В (
        ВЫБРАТЬ
            МАКСИМУМ(ВТ2.НомерСтроки)
        ИЗ
            ВТ_ПронумерованныеПродажи КАК ВТ2
        СГРУППИРОВАТЬ ПО
            ВТ2.ПериодДень
    )

Проанализируем ситуацию: здесь мы сначала жестко упорядочиваем все продажи по дате и ссылке внутри временной таблицы, присваивая каждой строке порядковый номер через АВТОНОМЕРЗАПИСИ. Затем мы просто выбираем записи, чьи номера являются максимальными для каждого дня. Обратите внимание на ВЫБРАТЬ ПЕРВЫЕ 1000000 — это техническое ограничение 1С: использование УПОРЯДОЧИТЬ ПО во временных таблицах работает только в связке с ПЕРВЫЕ.

Метод 3: Срез последних через соединение по неравенству (SQL-стиль)

Существует еще один способ, который часто встречается в классическом SQL, но в 1С его стоит использовать с осторожностью из-за возможного падения производительности на больших таблицах. Это соединение таблицы самой с собой по условию «не существует записи с более поздней датой».

Посмотрим на логику запроса:


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

Данный подход ищет такие записи, для которых в рамках того же дня нет документов с датой больше текущей. Однако он не решает проблему двух документов в одну секунду (в этом случае в результат попадут оба). Чтобы доработать этот запрос до идеала, в условие ГДЕ нужно добавлять сравнение ссылок.

Рекомендации по оптимизации

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

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

Таким образом, мы рассмотрели различные способы получения последнего контрагента за каждый день. Оптимальным выбором для большинства конфигураций будет Метод №1, как наиболее сбалансированный по скорости и простоте реализации.

← На главную