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