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

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

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

Постановка задачи и основная проблема «одинаковых дат»

Представим ситуацию: нам нужно выбрать по одному последнему заказу для каждого покупателя за текущий месяц. Первая мысль, которая приходит в голову любому начинающему разработчику — использовать группировку по Контрагент и выбрать МАКСИМУМ(Дата). Однако здесь кроется главная ловушка.

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

Способ 1: Классический метод через временные таблицы (Золотой стандарт)

Рассмотрим самый надежный и производительный метод, который корректно работает на любых версиях платформы 1С v8.3. Этот метод основан на последовательном сужении выборки с использованием временных таблиц.

Разберем этот процесс по шагам:

  1. Создание ВТ_МаксимальныеДаты: Группируем заказы по контрагенту и находим максимальную дату для каждого.
  2. Создание ВТ_МаксимальныеСсылки: Соединяем исходную таблицу заказов с первой временной таблицей по полям Контрагент и Дата. В этой таблице мы снова делаем группировку по контрагенту, но теперь выбираем МАКСИМУМ(Ссылка). Это гарантирует нам получение ровно одного документа, даже если даты совпадают.
  3. Получение итоговых данных: Соединяем ВТ_МаксимальныеСсылки с основной таблицей документов для получения всех необходимых реквизитов (сумма, склад, товары и т.д.).

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


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

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

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

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

Способ 2: Использование коррелированного запроса

Рассмотрим более лаконичный способ написания запроса, который позволяет решить задачу без использования цепочки временных таблиц. В 1С это реализуется через использование конструкции В (ВЫБРАТЬ ПЕРВЫЕ 1...) в блоке ГДЕ.

Посмотрим на пример, где нам нужно получить последний заказ клиента:


ВЫБРАТЬ
    Заказ.Ссылка,
    Заказ.Контрагент,
    Заказ.Дата
ИЗ
    Документ.ЗаказПокупателя КАК Заказ
ГДЕ
    Заказ.Ссылка В
            (ВЫБРАТЬ ПЕРВЫЕ 1
                Док.Ссылка
            ИЗ
                Документ.ЗаказПокупателя КАК Док
            ГДЕ
                Док.Контрагент = Заказ.Контрагент
            УПОРЯДОЧИТЬ ПО
                Док.Дата УБЫВ,
                Док.Ссылка УБЫВ)

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

Способ 3: Современный подход с АВТОНОМЕРЗАПИСИ()

В последних версиях платформы 1С v8.3 появилась возможность использовать функцию АВТОНОМЕРЗАПИСИ(). Это приближает нас к функционалу оконных функций SQL (таких как ROW_NUMBER()), хотя и с определенными ограничениями.

Выясним, как применить эту функцию для нашей задачи:

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

Этот метод особенно полезен, когда нужно выбрать не один последний заказ, а, например, «по 3 последних заказа каждого клиента». Рассмотрим, как это реализовать:


ВЫБРАТЬ
    Заказ.Контрагент КАК Контрагент,
    Заказ.Ссылка КАК Ссылка,
    Заказ.Дата КАК Дата
ПОМЕСТИТЬ ВТ_ВсеЗаказы
ИЗ
    Документ.ЗаказПокупателя КАК Заказ
УПОРЯДОЧИТЬ ПО
    Контрагент,
    Дата УБЫВ;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т1.Контрагент,
    Т1.Ссылка,
    КОЛИЧЕСТВО(Т2.Ссылка) КАК НомерСтроки
ИЗ
    ВТ_ВсеЗаказы КАК Т1
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеЗаказы КАК Т2
        ПО Т1.Контрагент = Т2.Контрагент
            И (Т1.Дата < Т2.Дата ИЛИ (Т1.Дата = Т2.Дата И Т1.Ссылка < Т2.Ссылка))
ГРУППИРОВАТЬ ПО
    Т1.Контрагент,
    Т1.Ссылка
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(Т2.Ссылка) < 3

В данном примере условие КОЛИЧЕСТВО(Т2.Ссылка) < 3 позволит отобрать ровно по 3 последних заказа для каждого покупателя. Это мощный инструмент для сложной аналитики.

Архитектурные рекомендации

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

В таких случаях рекомендуется рассмотреть следующие подходы:

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

← На главную