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