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