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

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

Срез первых и срез последних в 1С: как это работает

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

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

Важно понимать: срез — это не отдельная таблица в базе данных. Физически хранится только основная таблица регистра сведений, а виртуальная таблица строится платформой на лету при выполнении запроса. Отсюда следуют и особенности производительности, и ограничения по использованию, о которых пойдёт речь ниже.

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

Что такое периодический регистр сведений и почему срезы существуют только у него

Виртуальные таблицы срезов доступны только для регистров сведений, у которых в конфигураторе установлена периодичность, отличная от «Непериодический». Именно периодичность добавляет в регистр поле Период и делает осмысленным вопрос «какое значение было актуально на дату». Если регистр непериодический, записи не имеют истории, и платформа не создаёт для него таблиц срезов — обращение к ним вызовет ошибку компиляции запроса.

Типичные примеры периодических регистров сведений в типовых конфигурациях: «Цены номенклатуры», «Курсы валют», «Кадровая история сотрудников», «Паспортные данные физических лиц». Во всех этих случаях прикладная задача звучит одинаково: получить значение, действовавшее на определённый момент, — и решается она именно через срез последних.

Как работает срез последних: логика и пример запроса

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

Типовой запрос для получения цен на конкретную дату выглядит так:

ВЫБРАТЬ

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

Срез.Цена

ИЗ

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

Обратите внимание на синтаксис: первый параметр в скобках — дата среза, второй — условие отбора по измерениям. Условие внутри параметров виртуальной таблицы эффективнее, чем фильтрация через секцию ГДЕ, потому что платформа применяет его на этапе построения среза, а не после. Это критично для производительности на больших регистрах.

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

Как работает срез первых и когда он применяется

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

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

ВЫБРАТЬ

Срез.Валюта,

Срез.Курс,

Срез.Период

ИЗ

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

Если после указанной даты записей по данной комбинации измерений нет, строка в срез первых просто не попадёт. Это нужно учитывать: отсутствие строки означает «изменений не было», а не ошибку запроса.

Сравнение вариантов

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

Актуальное значение на дату: цены, курсы, кадровые данные на момент документа. Самый частый сценарий.

СрезПервых

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

Метод ПолучитьПоследнее()

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

Получение среза из встроенного языка без запроса

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

Отбор = Новый Структура("Валюта", ВалютаUSD);

ДанныеКурса = РегистрыСведений.КурсыВалют.ПолучитьПоследнее(ДатаДокумента, Отбор);

Если ДанныеКурса.Количество() > 0 Тогда

Курс = ДанныеКурса.Курс;

КонецЕсли;

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

bsl
Отбор = Новый Структура("Номенклатура", ТекТовар);
Данные = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(ТекущаяДатаСеанса(), Отбор);
Если Данные.Количество() > 0 Тогда
    Цена = Данные.Цена;
Иначе
    Цена = 0; // записей нет — обработать ситуацию явно
КонецЕсли;

Производительность: почему срезы тормозят и как это исправить

Главная причина медленных запросов со срезами — отсутствие фильтрации по измерениям. Когда вы строите срез по всему регистру без условий, платформа вынуждена сгруппировать все записи по всем комбинациям измерений и найти максимум периода в каждой группе. На регистре с миллионами записей это тяжёлая операция, даже при наличии индексов.

Правила эффективной работы со срезами:

  • Всегда передавайте условие отбора по измерениям вторым параметром виртуальной таблицы, если набор данных ограничен.
  • Если измерений много и отбор идёт по таблице значений (например, список номенклатуры из документа), сначала поместите список во временную таблицу, проиндексируйте её и соединяйте срез с ней.
  • Не используйте срез последних без даты там, где нужна история, — и наоборот, не тяните лишние периоды, если нужна только текущая запись.
  • Избегайте срезов внутри подзапросов с последующим соединением по неиндексированным полям.

Отдельный случай — регистры, подчинённые регистратору. Для них срез последних учитывает и момент времени документа-регистратора, что делает построение ещё дороже. Если периодичность «по позиции регистратора» не нужна прикладной логике, рассмотрите переход на периодичность «в пределах секунды».

Типичные ошибки при работе со срезами

Первая и самая частая ошибка — фильтрация через ГДЕ вместо параметра виртуальной таблицы. Запрос сработает корректно по результату, но платформа сначала построит срез по всему регистру, а потом отбросит лишние строки. На больших объёмах разница во времени выполнения — порядки.

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

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

Типичные ошибки
01
Фильтр через ГДЕ вместо параметра виртуальной таблицы — срез строится по всему регистру, запрос резко замедляется.
02
ПолучитьПоследнее() в цикле по строкам — тысячи обращений к базе вместо одного запроса.
03
Отсутствие проверки пустого результата — обращение к несуществующему ключу структуры вызывает исключение.
04
Ожидание строк по измерениям без записей — срез их не возвращает, нужно левое соединение от справочника.
05
Путаница границы даты: запись с периодом, равным дате среза, попадает в СрезПоследних, но не попадёт при передаче начала того же дня, если запись сделана позже.

Проверка результата и контрольные точки

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

Для оценки производительности используйте консоль запросов с замером времени выполнения на данных, близких к реальным по объёму. Если в типовой конфигурации доступен технологический журнал или инструменты замера, сравните план запроса до и после переноса условия в параметры виртуальной таблицы — разница обычно видна сразу.

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

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

Граница даты
Норма

Запись с периодом, равным дате среза, попадает в СрезПоследних

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

Запись на дату среза теряется — проверьте, не передано ли начало дня

Полнота данных
Норма

Все ожидаемые измерения присутствуют, где были записи

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

Пропали измерения без записей до даты — требуется левое соединение

Скорость выполнения
Норма

Запрос с отбором в параметрах выполняется за приемлемое время

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

Запрос сканирует весь регистр — условие вынесено в ГДЕ или отсутствует

Часто задаваемые вопросы

Можно ли получить срез последних по непериодическому регистру сведений?

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

Чем отличается периодичность «в пределах дня» от «по позиции регистратора»?

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

Почему СрезПоследних без указания даты иногда медленнее, чем с датой?

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

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

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

Влияет ли время в дате среза на результат?

Да. Период хранится с точностью до секунды, и сравнение идёт с учётом времени. Если передать дату 10:00:00, запись с периодом 15:30:00 того же дня в срез последних не попадёт. Для получения состояния «на конец дня» передавайте конец дня — например, через функцию КонецДня() при формировании параметра.

Можно ли отобрать срез по списку значений, например по нескольким товарам?

Да, но условие в параметрах виртуальной таблицы должно ссылаться на поле временной таблицы. Правильный приём: поместить список во временную таблицу, проиндексировать по полю связи и в условии среза написать отбор вида «Номенклатура В (ВЫБРАТЬ Т.Номенклатура ИЗ ВТ_Список КАК Т)». Прямая передача массива в условие через «В (&Список)» тоже работает, но временная таблица надёжнее на больших списках.

Итог

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

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