Как получить последнюю запись (статус) из регистра накопления на определенную дату?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

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

Проблема выбора из физической таблицы

Когда мы работаем с регистром накопления, мы привыкли использовать виртуальные таблицы Остатки или Обороты. Однако эти таблицы агрегируют данные. Если нам нужно узнать, какой Статус был установлен товару последним приходом (ВидДвижения.Приход) до определенной даты, нам придется обращаться к физической таблице регистра. Простая выборка из физической таблицы вернет все записи за период, а использование условия Период <= &НаДату без группировки выдаст избыточные данные. Рассмотрим, как решить эту задачу поэтапно.

Этап 1. Базовый подход с использованием МАКСИМУМ(Период)

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

Сначала мы создаем временную таблицу, в которой определяем "точку входа" — последний момент времени, когда изменялось состояние интересующего нас объекта:


ВЫБРАТЬ
    Регистр.Номенклатура КАК Номенклатура,
    Регистр.СерияНоменклатуры КАК СерияНоменклатуры,
    МАКСИМУМ(Регистр.Период) КАК МаксПериод
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
    РегистрНакопления.СостояниеТоваровНаСкладахСВХ КАК Регистр
ГДЕ
    Регистр.Период <= &НаДату
    И Регистр.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
СГРУППИРОВАТЬ ПО
    Регистр.Номенклатура,
    Регистр.СерияНоменклатуры
;

Этот запрос позволяет нам отсечь все старые записи, оставив только дату последнего изменения. Однако здесь кроется подвох, который мы проанализируем далее.

Этап 2. Решение проблемы "Дублей в одну секунду"

Представим ситуацию: в одну и ту же секунду одним или разными документами были сделаны две записи по одной и той же номенклатуре. Поле Период у них будет идентичным. Если мы просто соединим нашу временную таблицу ВТ_МаксимальныеДаты с физической таблицей по периоду и номенклатуре, мы получим две строки вместо одной. Чтобы гарантированно выбрать только одну (самую последнюю) запись, необходимо использовать поле НомерСтроки или МоментВремени.

Рассмотрим более совершенную схему с использованием МАКСИМУМ(НомерСтроки) для тех же периодов:


// Шаг 1: Находим максимальную дату
ВЫБРАТЬ
    Регистр.Номенклатура,
    Регистр.СерияНоменклатуры,
    МАКСИМУМ(Регистр.Период) КАК Период
ПОМЕСТИТЬ ВТ_Периоды
ИЗ
    РегистрНакопления.СостояниеТоваровНаСкладахСВХ КАК Регистр
ГДЕ
    Регистр.Период <= &НаДату
СГРУППИРОВАТЬ ПО
    Регистр.Номенклатура,
    Регистр.СерияНоменклатуры;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Находим максимальный номер строки для этой даты (если записей несколько в секунду)
ВЫБРАТЬ
    Регистр.Номенклатура,
    Регистр.СерияНоменклатуры,
    ВТ_Периоды.Период,
    МАКСИМУМ(Регистр.НомерСтроки) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_КлючиЗаписей
ИЗ
    РегистрНакопления.СостояниеТоваровНаСкладахСВХ КАК Регистр
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Периоды КАК ВТ_Периоды
    ПО Регистр.Номенклатура = ВТ_Периоды.Номенклатура
    И Регистр.СерияНоменклатуры = ВТ_Периоды.СерияНоменклатуры
    И Регистр.Период = ВТ_Периоды.Период
СГРУППИРОВАТЬ ПО
    Регистр.Номенклатура,
    Регистр.СерияНоменклатуры,
    ВТ_Периоды.Период;

////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Получаем финальные данные (Статус)
ВЫБРАТЬ
    ВТ_КлючиЗаписей.Номенклатура,
    ВТ_КлючиЗаписей.СерияНоменклатуры,
    Регистр.Статус КАК АктуальныйСтатус
ИЗ
    ВТ_КлючиЗаписей КАК ВТ_КлючиЗаписей
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.СостояниеТоваровНаСкладахСВХ КАК Регистр
    ПО Регистр.Номенклатура = ВТ_КлючиЗаписей.Номенклатура
    И Регистр.СерияНоменклатуры = ВТ_КлючиЗаписей.СерияНоменклатуры
    И Регистр.Период = ВТ_КлючиЗаписей.Период
    И Регистр.НомерСтроки = ВТ_КлючиЗаписей.НомерСтроки

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

Этап 3. Особенности работы в Системе компоновки данных (СКД)

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

Важный момент для СКД: Чтобы отчет работал быстро, старайтесь ограничивать выборку во временных таблицах по тем измерениям, которые реально используются в компоновке. Помните, что использование МАКСИМУМ в подзапросах к физическим таблицам больших регистров может замедлять работу базы. Рекомендуется убедиться, что по измерениям Номенклатура и СерияНоменклатуры в регистре созданы индексы (в свойствах регистра стоит галочка "Индексировать").

Этап 4. Почему "Обороты" — это не всегда решение?

В обсуждении на форуме предлагалось использовать таблицу Обороты. Разберем, почему это может привести к ошибке. Таблица оборотов агрегирует данные. Если за выбранный период товар успел сменить три статуса (например: "В пути", "На проверке", "Принят"), то таблица оборотов при группировке по статусу выдаст вам три строки. Если же вы не сгруппируете по статусу, вы вообще не сможете его вытащить. Таким образом, для получения значения свойства (измерения или реквизита), которое не является числовым ресурсом, физическая таблица с поиском максимального момента времени — единственный путь.

Рекомендации по архитектуре

Рассмотрим ситуацию с точки зрения производительности системы в целом. Если вам регулярно требуется получать "последний статус" из регистра накопления, это может свидетельствовать о неоптимальной архитектуре метаданных. Регистры накопления предназначены для хранения количественных изменений. Для хранения состояний (статусов) идеально подходят Регистры сведений.

Оптимальным решением будет:

  1. Создать периодический регистр сведений СтатусыТоваров с измерениями Номенклатура, Серия и ресурсом Статус.
  2. При проведении документа делать записи одновременно и в регистр накопления (для учета количества), и в регистр сведений (для фиксации состояния).
  3. В запросах использовать виртуальную таблицу РегистрСведений.СтатусыТоваров.СрезПоследних(&НаДату, ).

Это позволит системе работать на порядок быстрее, так как платформа 1С оптимизирует хранение СрезаПоследних на уровне СУБД, избавляя вас от необходимости писать сложные вложенные запросы с МАКСИМУМ(Период).

Подводя итог: если изменение конфигурации невозможно, используйте метод трех шагов (Макс. Период -> Макс. НомерСтроки -> Выборка данных). Это гарантирует отсутствие дублей и корректность отчета "На дату". Дополнительные варианты реализации таких выборок описывает статья с примерами построения запросов для получения среза по регистру накопления.

← На главную