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

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

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

Вариант 1. Получение последней строки для одного конкретного документа

Если ссылка на документ уже известна и передается в запрос через параметр, задача решается прямолинейно с помощью конструкции ПЕРВЫЕ 1 и сортировки по убыванию номера строки.

Рассмотрим пример такого запроса:


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

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

Вариант 2. Получение последней строки для группы документов (двухэтапный запрос)

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

Разберем логику построения такого запроса по шагам:

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

Посмотрим на пример запроса:


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

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

Оптимизация производительности: устранение неявных соединений

В приведенном выше примере условие тДок.Ссылка.Дата МЕЖДУ ... содержит обращение через точку к реквизиту шапки документа из таблицы его табличной части. На уровне СУБД такое обращение приводит к созданию скрытого неявного соединения (Left Outer Join) таблицы табличной части с таблицей шапки документа. При большом объеме данных это может существенно замедлить выполнение запроса.

Чтобы избежать неявных соединений и максимально использовать индексы СУБД, оптимизируем первый пакет запроса: вынесем отбор документов в отдельную временную таблицу по таблице шапки документа:


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

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

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

Какое соединение использовать: внутреннее или левое?

На форумах часто встречается заблуждение, что ЛЕВОЕ СОЕДИНЕНИЕ работает быстрее, чем ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Это не так. В оптимизаторах реляционных СУБД (таких как MS SQL Server или PostgreSQL) внутреннее соединение дает планировщику больше свободы: он может произвольно менять порядок обхода таблиц, выбирая наиболее эффективный алгоритм соединения (например, слияние или хеш-соединение).

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

Вариант 3. Использование составного условия в секции ГДЕ

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


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

Такой вариант короче в написании, однако при обработке десятков и сотен тысяч документов классический подход с индексированными временными таблицами демонстрирует более предсказуемое время выполнения и оптимизированный план запроса.

Особый случай: получение первой строки документа

Если перед вами стоит обратная задача — получить не последнюю, а первую строку табличной части для каждого документа, алгоритм становится еще проще. Нумерация строк в табличных частях 1С начинается с единицы. Следовательно, нет необходимости искать минимальный номер через агрегатные функции, достаточно соединить отобранные документы с табличной частью по жесткому условию НомерСтроки = 1:


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

Подводные камни и рекомендации

При решении подобных задач учитывайте следующие особенности платформы 1С:

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