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