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