К статье
Практический материал

Разработка и Конфигурация · 11 мин

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

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

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

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

Ниже разобраны все три подхода с готовыми текстами запросов, пояснением параметров и рекомендациями, какой вариант выбрать в конкретной ситуации. Примеры приведены для периодического регистра сведений «ЦеныНоменклатуры», но легко адаптируются под любой другой регистр.

Как устроена задача и почему обычный запрос не подходит

Периодический регистр сведений хранит историю изменений значений: одна и та же номенклатура может иметь десятки записей с разными датами и ценами. Простой запрос ВЫБРАТЬ * ИЗ РегистрСведений.ЦеныНоменклатуры вернёт всю историю целиком, а нужна только одна актуальная строка на каждую номенклатуру.

Понятие «последняя запись» определяется полем Период: чем позже дата записи, тем она актуальнее. Если периодичность регистра — «В пределах секунды» или «В пределах дня», дополнительную роль может играть регистратор, но в большинстве случаев достаточно периода.

Попытка решить задачу «в лоб» — выбрать всё и в коде перебрать записи в поисках максимальной даты — приводит к лишнему трафику между сервером 1С и СУБД и резкому падению производительности на больших таблицах. Правильный подход — заставить СУБД отобрать нужные записи на своей стороне.

Способ 1: виртуальная таблица СрезПоследних

Это штатный и предпочтительный способ для периодических регистров сведений. Виртуальная таблица СрезПоследних возвращает по одной последней записи на каждую комбинацию измерений на заданную дату.

ВЫБРАТЬ

Срез.Номенклатура,

Срез.Цена,

Срез.Период

ИЗ

РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Срез

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

Почему условие нужно передавать в параметры виртуальной таблицы

Частая ошибка — фильтровать результат в секции ГДЕ основного запроса:

// Медленно: срез строится по ВСЕМ записям, потом фильтруется

ГДЕ Срез.Номенклатура = &Номенклатура

Правильно передавать отбор вторым параметром виртуальной таблицы — тогда СУБД сначала ограничит набор записей и только потом вычислит максимумы:

РегистрСведений.ЦеныНоменклатуры.СрезПоследних(

&Период, Номенклатура = &Номенклатура) КАК Срез

Разница в производительности на крупных регистрах может достигать десятков раз. Условие в параметре поддерживает сравнение, В со списком значений и соединение с другой таблицей через В (ВЫБРАТЬ ...).

Способ 2: ПЕРВЫЕ 1 с сортировкой по убыванию периода

Если нужна одна конкретная запись по заранее известному отбору — например, последняя цена одного товара — задачу решает конструкция ПЕРВЫЕ 1:

ВЫБРАТЬ ПЕРВЫЕ 1

Цены.Цена,

Цены.Период

ИЗ

РегистрСведений.ЦеныНоменклатуры КАК Цены

ГДЕ

Цены.Номенклатура = &Номенклатура

И Цены.Период <= &Период

УПОРЯДОЧИТЬ ПО

Цены.Период УБЫВ

Логика проста: записи отбираются по измерению, сортируются от новых к старым, и берётся первая строка. Условие Период <= &Период позволяет получить запись, актуальную на произвольную дату в прошлом, а не только «на сейчас».

Этот способ удобен, когда результат нужен по одному объекту — например, при записи документа подставить текущую цену конкретной номенклатуры. Для списка из тысяч позиций вызывать такой запрос в цикле нельзя: получится тысяча обращений к базе. В этом случае используйте СрезПоследних или соединение с максимумом.

Сравнение способов

СрезПоследних

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

ПЕРВЫЕ 1 + УБЫВ

Подходит для одиночной записи по известному отбору и для непериодических регистров. Ограничение: неприменим в цикле по большому списку объектов.

Соединение с МАКСИМУМ(Период)

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

Способ 3: соединение с вложенным запросом МАКСИМУМ(Период)

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

ВЫБРАТЬ

Цены.Номенклатура,

Цены.Цена

ИЗ

РегистрСведений.ЦеныНоменклатуры КАК Цены

ВНУТРЕННЕЕ СОЕДИНЕНИЕ (

ВЫБРАТЬ

Т.Номенклатура КАК Номенклатура,

МАКСИМУМ(Т.Период) КАК МаксПериод

ИЗ

РегистрСведений.ЦеныНоменклатуры КАК Т

ГДЕ

Т.Период <= &Период

СГРУППИРОВАТЬ ПО

Т.Номенклатура

) КАК Макс

ПО Цены.Номенклатура = Макс.Номенклатура

И Цены.Период = Макс.МаксПериод

Этот способ используют, когда виртуальная таблица недоступна — например, регистр непериодический, а «последность» определяется собственным реквизитом типа даты, или когда запрос строится внешним инструментом, не поддерживающим виртуальные таблицы 1С.

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

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

Непериодический регистр сведений

У непериодического регистра нет поля Период, поэтому понятие «последней записи» нужно определить самостоятельно: по реквизиту даты, по номеру строки, по регистратору. Если такого признака нет, задача теряет смысл — записи равнозначны, и «последней» среди них не существует.

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

Срез на дату документа с учётом момента времени

Когда цена нужна «на дату документа», простое сравнение дат может дать неточность: несколько документов в одну секунду изменяют цену, и важна их последовательность. Если регистр подчинён регистратору и периодичность — «по позиции документа», передавайте в параметр среза объект типа МоментВремени:

&Период = Новый МоментВремени(ДатаДокумента, СсылкаНаДокумент)

Тогда срез учтёт не только дату, но и положение документа в последовательности, и результат будет соответствовать тому, что «видел» документ на момент проведения.

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

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

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

Контрольные точки

Как проверить результат

Число строк результата
Норма

По одной строке на каждую комбинацию измерений

Тревожный признак

Несколько строк на одну комбинацию — дубли по периоду или ошибка соединения

Значение поля Период
Норма

Не позже даты среза, совпадает с последней записью в форме регистра

Тревожный признак

Период больше даты среза или запись не та, что видна вручную

Время выполнения
Норма

Доли секунды при отборе по измерениям

Тревожный признак

Секунды и минуты — вероятно, не задано условие в параметрах виртуальной таблицы

Типичные ошибки
01
Отбор в ГДЕ вместо параметра виртуальной таблицы — срез строится по всей таблице, запрос работает в разы медленнее.
02
Запрос ПЕРВЫЕ 1 в цикле по списку товаров — тысячи обращений к базе вместо одного пакетного запроса со СрезПоследних.
03
Сравнение только по дате без учёта момента времени — на документах в одну секунду получается не та цена, что была при проведении.
04
Соединение с МАКСИМУМ(Период) без учёта дублей периода — в результат попадают две строки, и итоги задваиваются.
05
Срез без параметра даты там, где нужна историческая запись, — возвращается текущее значение вместо значения на прошлую дату.

Частые вопросы

Чем СрезПоследних отличается от СрезПервых?

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

Можно ли получить последнюю запись без запроса?

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

Почему СрезПоследних вернул пустой результат?

Наиболее вероятные причины: дата среза раньше самой первой записи регистра, условие отбора не соответствует ни одному значению измерения (например, передана пустая ссылка) или в регистре вообще нет записей. Проверьте параметры и наличие данных в форме списка регистра.

Что делать, если у одной номенклатуры две записи с одинаковым периодом?

При периодичности «по позиции документа» или после загрузки данных такое возможно. СрезПоследних в этом случае вернёт запись с учётом регистратора, а ручное соединение с МАКСИМУМ — обе строки. Добавьте в соединение условие по регистратору или согласуйте данные, устранив дубли.

Работает ли СрезПоследних в СКД и динамических списках?

Да, виртуальные таблицы доступны в схеме компоновки данных и в запросах динамических списков. В СКД период удобно передавать через параметр данных с выражением &Период, а отбор по измерениям задавать в параметрах виртуальной таблицы, чтобы не потерять производительность.

Как получить не одну, а, например, три последние записи по каждому объекту?

Штатной виртуальной таблицы для этого нет. Используйте соединение таблицы регистра самой с собой с подсчётом более поздних записей и условием, что их меньше трёх, либо выберите данные с упорядочиванием и обработайте в коде, отбирая по три строки на каждую группу.

Итог

Что важно запомнить

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