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

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

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

В чем заключается проблема базового запроса с группировкой по дате

Проанализируем типовой запрос, с которого часто начинают разработку такого отчета или алгоритма:


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

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

Также важно помнить об ограничениях языка запросов: тип МоментВремени не поддерживается в запросах 1С. Он доступен исключительно в объектной модели встроенного языка и не может быть использован в секциях ВЫБРАТЬ или СГРУППИРОВАТЬ ПО.

Решение 1: Двухэтапная группировка по дате и ссылке через временные таблицы

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

Разберем алгоритм по шагам:

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

Посмотрим на пример реализации запроса с пакетом временных таблиц:


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

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

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

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

Решение 2: Использование оконных функций (для платформы 8.3.27 и выше)

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

Проанализируем пример с функцией ROW_NUMBER():


ВЫБРАТЬ
    ВыборкаДокументов.Ссылка КАК ПоследнийДокумент,
    ВыборкаДокументов.Контрагент КАК Контрагент,
    ВыборкаДокументов.Дата КАК Дата,
    ВыборкаДокументов.СуммаДокумента КАК СуммаДокумента
ИЗ
    (ВЫБРАТЬ
        Доки.Ссылка КАК Ссылка,
        Доки.Контрагент КАК Контрагент,
        Доки.Дата КАК Дата,
        Доки.СуммаДокумента КАК СуммаДокумента,
        ROW_NUMBER() OVER (PARTITION BY Доки.Контрагент ORDER BY Доки.Дата УБЫВ, Доки.Ссылка УБЫВ) КАК НомерСтроки
    ИЗ
        Документ.РеализацияТоваровУслуг КАК Доки) КАК ВыборкаДокументов
ГДЕ
    ВыборкаДокументов.НомерСтроки = 1

В данном запросе окно формируется для каждого контрагента отдельно через PARTITION BY, а внутри секции выполняется сортировка по убыванию даты и ссылки. Фильтрация по условию НомерСтроки = 1 оставляет ровно один актуальный документ на каждого контрагента.

Решение 3: Оптимальная выборка для одного конкретного контрагента

Если перед нами стоит задача получить последний документ не по всем контрагентам базы сразу, а только для одного выбранного контрагента, не следует применять ресурсоемкие группировки. Оптимальным решением будет прямое ограничение выборки:


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

Конструкция ВЫБРАТЬ ПЕРВЫЕ 1 в сочетании с сортировкой УПОРЯДОЧИТЬ ПО Дата УБЫВ, Ссылка УБЫВ обеспечивает минимальное время выполнения за счет использования штатных индексов таблицы документов.

← На главную