При разработке отчетов и механизмов обработки данных в 1С часто требуется получить значения реквизитов из самой последней строки табличной части документа. Ситуация усложняется, когда выборку нужно сделать не для одного конкретного документа, а сразу для целого массива документов, отобранных по определенному условию (например, за период или по контрагенту). Разберем по шагам, как правильно составить такой запрос, избежать падения производительности и исключить распространенные ошибки.
Если ссылка на документ уже известна и передается в запрос через параметр, задача решается прямолинейно с помощью конструкции ПЕРВЫЕ 1 и сортировки по убыванию номера строки.
Рассмотрим пример такого запроса:
ВЫБРАТЬ ПЕРВЫЕ 1
ДокТЧ.Ссылка КАК Ссылка,
ДокТЧ.НомерСтроки КАК НомерСтроки,
ДокТЧ.Номенклатура КАК Номенклатура
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ДокТЧ
ГДЕ
ДокТЧ.Ссылка = &Ссылка
УПОРЯДОЧИТЬ ПО
ДокТЧ.НомерСтроки УБЫВ
В этом случае платформа выбирает все строки указанного документа, упорядочивает их от максимального номера строки к минимальному и возвращает самую первую запись полученного набора, то есть фактически последнюю строку табличной части.
Когда документов много, использовать конструкцию ПЕРВЫЕ 1 невозможно, так как она ограничит всю выборку одной строкой для всей базы данных, а не для каждого документа в отдельности. Стандартным и наиболее производительным подходом является использование временной таблицы с агрегатной функцией МАКСИМУМ.
Разберем логику построения такого запроса по шагам:
НомерСтроки для каждого из них, группируя результат по ссылке на документ. Помещаем эти данные во временную таблицу и обязательно индексируем ключевые поля.Посмотрим на пример запроса:
ВЫБРАТЬ
тДок.Ссылка КАК Ссылка,
МАКСИМУМ(тДок.НомерСтроки) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_ПоследниеСтроки
ИЗ
Документ.ОтправкаВагонов.Вагоны КАК тДок
ГДЕ
тДок.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
СГРУППИРОВАТЬ ПО
тДок.Ссылка
ИНДЕКСИРОВАТЬ ПО
Ссылка,
НомерСтроки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Ссылка КАК Документ,
ВТ.НомерСтроки КАК НомерСтроки,
ТабЧасть.Груз КАК Груз
ИЗ
ВТ_ПоследниеСтроки КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ОтправкаВагонов.Вагоны КАК ТабЧасть
ПО ВТ.Ссылка = ТабЧасть.Ссылка
И ВТ.НомерСтроки = ТабЧасть.НомерСтроки
В приведенном выше примере условие тДок.Ссылка.Дата МЕЖДУ ... содержит обращение через точку к реквизиту шапки документа из таблицы его табличной части. На уровне СУБД такое обращение приводит к созданию скрытого неявного соединения (Left Outer Join) таблицы табличной части с таблицей шапки документа. При большом объеме данных это может существенно замедлить выполнение запроса.
Чтобы избежать неявных соединений и максимально использовать индексы СУБД, оптимизируем первый пакет запроса: вынесем отбор документов в отдельную временную таблицу по таблице шапки документа:
ВЫБРАТЬ
ДокШапка.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ОтобранныеДокументы
ИЗ
Документ.ОтправкаВагонов КАК ДокШапка
ГДЕ
ДокШапка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТабЧасть.Ссылка КАК Ссылка,
МАКСИМУМ(ТабЧасть.НомерСтроки) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_ПоследниеСтроки
ИЗ
ВТ_ОтобранныеДокументы КАК Отобранные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ОтправкаВагонов.Вагоны КАК ТабЧасть
ПО Отобранные.Ссылка = ТабЧасть.Ссылка
СГРУППИРОВАТЬ ПО
ТабЧасть.Ссылка
ИНДЕКСИРОВАТЬ ПО
Ссылка,
НомерСтроки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Ссылка КАК Документ,
ТабЧасть.Груз КАК Груз
ИЗ
ВТ_ПоследниеСтроки КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ОтправкаВагонов.Вагоны КАК ТабЧасть
ПО ВТ.Ссылка = ТабЧасть.Ссылка
И ВТ.НомерСтроки = ТабЧасть.НомерСтроки
На форумах часто встречается заблуждение, что ЛЕВОЕ СОЕДИНЕНИЕ работает быстрее, чем ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Это не так. В оптимизаторах реляционных СУБД (таких как MS SQL Server или PostgreSQL) внутреннее соединение дает планировщику больше свободы: он может произвольно менять порядок обхода таблиц, выбирая наиболее эффективный алгоритм соединения (например, слияние или хеш-соединение).
Левое соединение жестко определяет ведущую таблицу и добавляет накладные расходы на обработку значений NULL для несовпавших строк. Поскольку максимальный номер строки гарантированно существует в табличной части соответствующего документа, во втором пакете запроса следует использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Если не требуется сохранять промежуточные данные в таблицах или нужно написать компактный запрос, можно использовать конструкцию проверки вхождения кортежа полей в секции ГДЕ:
ВЫБРАТЬ
ТабЧасть.Ссылка КАК Документ,
ТабЧасть.НомерСтроки КАК НомерСтроки,
ТабЧасть.Груз КАК Груз
ИЗ
Документ.ОтправкаВагонов.Вагоны КАК ТабЧасть
ГДЕ
(ТабЧасть.Ссылка, ТабЧасть.НомерСтроки) В
(ВЫБРАТЬ
Вложенный.Ссылка,
МАКСИМУМ(Вложенный.НомерСтроки)
ИЗ
Документ.ОтправкаВагонов.Вагоны КАК Вложенный
ГДЕ
Вложенный.Ссылка В (&МассивДокументов)
СГРУППИРОВАТЬ ПО
Вложенный.Ссылка)
Такой вариант короче в написании, однако при обработке десятков и сотен тысяч документов классический подход с индексированными временными таблицами демонстрирует более предсказуемое время выполнения и оптимизированный план запроса.
Если перед вами стоит обратная задача — получить не последнюю, а первую строку табличной части для каждого документа, алгоритм становится еще проще. Нумерация строк в табличных частях 1С начинается с единицы. Следовательно, нет необходимости искать минимальный номер через агрегатные функции, достаточно соединить отобранные документы с табличной частью по жесткому условию НомерСтроки = 1:
ВЫБРАТЬ
Док.Ссылка КАК Документ,
ТабЧасть.Груз КАК ПервыйГруз
ИЗ
Документ.ОтправкаВагонов КАК Док
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ОтправкаВагонов.Вагоны КАК ТабЧасть
ПО Док.Ссылка = ТабЧасть.Ссылка
И ТабЧасть.НомерСтроки = 1
ГДЕ
Док.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
При решении подобных задач учитывайте следующие особенности платформы 1С:
ЛЕВЫМ.НомерСтроки гарантированно уникально в пределах одного документа. Если последняя строка определяется по значению реквизита внутри строки (например, по дате этапа), значения могут совпасть. В таких случаях, чтобы избежать задвоения строк при обратном соединении, в группировку обязательно добавляют МАКСИМУМ(НомерСтроки) как разрешающий критерий.