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

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

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

Разбор проблемы: почему нельзя получить документ «в лоб»

Рассмотрим ситуацию, когда нам нужно получить по каждому партнеру дату его последней покупки и ссылку на соответствующий документ. Если мы попробуем написать простой запрос с группировкой по партнеру и функцией МАКСИМУМ(Период), то при добавлении поля Регистратор система потребует включить его в секцию СГРУППИРОВАТЬ ПО. В итоге мы получим список всех документов по каждому партнеру, так как каждый регистратор уникален.

Если же мы попробуем применить МАКСИМУМ(Регистратор), то получим документ с максимальной внутренней ссылкой (GUID), что далеко не всегда совпадает с хронологически последним документом. Поэтому стандартным подходом является двухэтапное решение задачи.

Решение через временные таблицы и соединения

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

Шаг 1: Подготовка данных и поиск максимальной даты

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


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

Шаг 2: Получение документа через соединение

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


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

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

Оптимизация: использование «точек» и временных таблиц

Разберем важный аспект производительности. В примере выше используется обращение через точку: Продажи.Регистратор.Партнер. Проанализируем ситуацию: каждое такое обращение в запросе 1С превращается в LEFT JOIN на уровне СУБД. Если в регистре миллионы записей, это может существенно замедлить выполнение.

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

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

Использование оконных функций (Платформа 8.3.20+)

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

Функция ROW_NUMBER() позволяет пронумеровать строки внутри группы (например, по партнеру), отсортировав их по дате в обратном порядке. Строка с номером 1 и будет нашим последним документом.


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Партнер,
    ВТ.ДатаПоследнейПродажи,
    ВТ.ДокументПоследнейПродажи
ИЗ
    ВТ_ПронумерованныеПродажи КАК ВТ
ГДЕ
    ВТ.НомерПродажи = 1

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

Важные нюансы при работе с «Моментом времени»

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

В запросах 1С Момент времени нельзя использовать напрямую как объект сравнения в секции ПО, но его можно эмулировать. Для этого в сортировку или условия добавляется ссылка на документ. Поскольку ссылка гарантированно уникальна, она служит «арбитром» при равенстве периодов.

Посмотрим на пример правильного условия соединения:

Если мы хотим быть абсолютно уверены в результате, во временную таблицу стоит помещать не только МАКСИМУМ(Период), но и МАКСИМУМ(Регистратор) (если мы договорились, что при равенстве времени нам подходит любая ссылка, либо мы полагаемся на то, что более поздняя ссылка соответствует более позднему документу, что в 8.3 часто является правдой, но не гарантируется на 100%).

Заключение

Подведем итог. Для решения задачи получения последнего документа продажи:

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

Правильное построение таких запросов — залог быстродействия ваших отчетов и стабильной работы системы даже при больших объемах данных.

← На главную