Как получить последние записи регистра сведений на определенную дату в запросе 1С

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

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

Использование виртуальной таблицы СрезПоследних

Самый простой и правильный способ получить актуальные данные в 1С — это использование виртуальной таблицы СрезПоследних. Платформа 1С автоматически формирует сложный SQL-запрос, который эффективно выбирает нужные строки. Рассмотрим, как это работает на примере номенклатуры и её цен или статусов.

Когда мы обращаемся к виртуальной таблице, крайне важно передавать параметры непосредственно в саму таблицу, а не накладывать фильтры в секции ГДЕ. Проанализируем пример кода:


ВЫБРАТЬ
    Рег.Номенклатура КАК Номенклатура,
    Рег.Статус КАК Статус,
    Рег.Период КАК Период
ИЗ
    РегистрСведений.АссортиментныеСтатусы.СрезПоследних(&ДатаСреза, Номенклатура В (&МассивНоменклатуры)) КАК Рег

Разберем по шагам, почему этот метод является приоритетным:

  1. Оптимизация: Условие, переданное в параметрах виртуальной таблицы (например, по номенклатуре), обрабатывается на уровне СУБД максимально быстро.
  2. Точность: Платформа сама заботится о том, чтобы выбрать запись с максимальной датой, не превышающей &ДатаСреза.
  3. Учет позиции: Если регистр имеет периодичность "Позиция регистратора", виртуальная таблица учтет не только время, но и логический порядок документов в пределах одной секунды.

Если мы напишем условие ГДЕ Номенклатура = &Номенклатура после вызова таблицы, система сначала может попытаться построить срез по всему регистру, и только потом отфильтровать его. На больших объемах данных это приведет к значительным задержкам.

Решение задачи с помощью вложенных запросов и временных таблиц

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

Для начала нам необходимо определить максимальную дату для каждого измерения. Для этого используем временную таблицу:


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

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

Проанализируем ситуацию, упомянутую в обсуждении: что делать, если на один и тот же период приходится две разные записи? В 1С такая ситуация возможна, если измерения регистра не обеспечивают уникальность или если используется периодичность, допускающая несколько записей в одну секунду. Чтобы избежать дублирования в конечном результате, в итоговом запросе можно использовать ключевое слово РАЗЛИЧНЫЕ или дополнительно соединяться по Регистратору и НомеруСтроки.

Современный подход: Оконные функции (ROW_NUMBER)

Начиная с версии платформы 1С 8.3.21, разработчикам стали доступны оконные функции. Это мощный инструмент, который позволяет решить задачу "последней записи" гораздо лаконичнее и зачастую быстрее. Мы можем пронумеровать записи внутри группы и выбрать только первую из них.

Посмотрим на пример реализации такого запроса:


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

Выясним причину, почему этот метод эффективен. В данном случае СУБД выполняет один проход по индексам, группируя данные по номенклатуре и сразу сортируя их внутри группы. Нам остается лишь отсечь всё, кроме первой строки в каждой "партии". Это избавляет от необходимости делать тяжелые соединения (JOIN) таблиц самих с собой.

Важные нюансы и производительность

Независимо от выбранного метода, следует помнить о нескольких критически важных моментах, влияющих на корректность и скорость работы системы:

  1. Индексация: Поиск максимальной даты будет быстрым только в том случае, если измерения регистра, по которым идет группировка, проиндексированы. В 1С измерения индексируются автоматически, но если вы используете "Реквизиты" регистра в условиях, убедитесь, что для них включен признак "Индексировать".
  2. Момент времени: Если ваш регистр подчинен регистратору, использование просто Периода может быть недостаточно для точного определения последовательности. В таких случаях лучше оперировать понятием Момент времени, который включает в себя и дату, и ссылку на документ.
  3. Объектный метод: Если вам нужно получить значение только для одного конкретного объекта (например, одной номенклатуры) вне запроса, рассмотрите метод РегистрыСведений.[Имя].ПолучитьПоследнее(). Он работает крайне быстро, так как оптимизирован платформой под точечное получение данных.

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

← На главную