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