Как получить последнюю запись из регистра сведений для каждого документа в динамическом списке?

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

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

Нередко разработчики пытаются решить эту задачу «в лоб» — обращением к виртуальной таблице СрезПоследних с соединением по полю Регистратор. Однако результат оказывается ошибочным: в списке либо отображается статус только для одного документа, либо данные заполняются неверно. Разберем по шагам, почему возникает эта ошибка и как составить правильный запрос для объекта ДинамическийСписок.

Выясним причину: почему СрезПоследних не работает с регистратором

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

Из этого следуют две главные проблемы при попытке использовать СрезПоследних:

  1. Срез строится без учета регистратора. Если в регистре измерением является, например, просто тип оборудования или общая номенклатурная позиция (или измерений нет вовсе), виртуальная таблица вернет ровно одну последнюю запись на всю базу. При соединении этой единственной записи по условию Регистратор = Ссылка значение отобразится только у того единственного документа, который сделал эту самую последнюю запись.
  2. Идентичность периода записей внутри одного документа. Если документ формирует несколько записей в регистре в процессе своего проведения, поле Период у всех этих записей будет одинаковым — оно соответствует дате документа. В таком случае порядок записей определяется не временем, а стандартным реквизитом НомерСтроки. Следовательно, выборка максимальной даты не позволит однозначно определить последнюю операцию в рамках одного заказа.

Ограничения объекта ДинамическийСписок

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

  1. Запрет пакетных запросов и временных таблиц. Текст запроса динамического списка может содержать только один неделимый оператор ВЫБРАТЬ. Использовать пакетную конструкцию с точкой с запятой (;) и директивой ПОМЕСТИТЬ для создания временных таблиц в динамическом списке нельзя — платформа выдаст синтаксическую ошибку при инициализации формы.
  2. Недопустимость полного соединения. Конструкция ПОЛНОЕ СОЕДИНЕНИЕ ломает оптимизацию постраничного считывания данных СУБД, затрудняет наложение платформенных отборов и может привести к непредсказуемым замедлениям интерфейса. В динамических списках всегда следует применять ЛЕВОЕ СОЕДИНЕНИЕ.
  3. Свойство ОсновнаяТаблица. Чтобы список открывался мгновенно, поддерживал динамическое дочитывание порциями, стандартный поиск и сортировку, в его свойствах обязательно должна быть заполнена Основная таблица (в нашем случае — Документ.ЗаказКлиента), а поле Ссылка должно быть отмечено как ключевое.

Решение: формируем искусственный срез через вложенный запрос

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

  1. Берем таблицу документов как основу выборки.
  2. Через ЛЕВОЕ СОЕДИНЕНИЕ подключаем вложенный запрос к реальной таблице движений регистра сведений (без использования виртуальной таблицы среза). В подзапросе выполняем группировку по полю Регистратор и вычисляем МАКСИМУМ(НомерСтроки) для каждого документа.
  3. Вторым левым соединением подключаем саму таблицу регистра, связывая её одновременно по двум полям: Регистратор и НомерСтроки. Это позволяет однозначно получить ресурс Операция именно из финальной строки движений документа.

Посмотрим на готовый текст запроса для динамического списка:


ВЫБРАТЬ
    Заказ.Ссылка КАК Ссылка,
    Заказ.Номер КАК Номер,
    Заказ.Дата КАК Дата,
    ОтслеживаниеПО.Операция КАК Операция
ИЗ
    Документ.ЗаказКлиента КАК Заказ
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            Срез.Регистратор КАК Регистратор,
            МАКСИМУМ(Срез.НомерСтроки) КАК МаксНомерСтроки
        ИЗ
            РегистрСведений.ОтслеживаниеПО КАК Срез
        СГРУППИРОВАТЬ ПО
            Срез.Регистратор
    ) КАК ПоследниеСтроки
        ПО Заказ.Ссылка = ПоследниеСтроки.Регистратор
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОтслеживаниеПО КАК ОтслеживаниеПО
        ПО ПоследниеСтроки.Регистратор = ОтслеживаниеПО.Регистратор
        И ПоследниеСтроки.МаксНомерСтроки = ОтслеживаниеПО.НомерСтроки

Разберем важные детали настройки списка в палитре свойств:

  1. В поле Основная таблица динамического списка указываем Документ.ЗаказКлиента.
  2. В таблице полей запроса проверяем, чтобы у поля Ссылка стоял флаг Использовать всегда или статус ключевого поля.
  3. При выводе колонки Операция на форму динамический список будет корректно подтягивать статус именно для той строки документа, которая отображается в текущий момент на экране.

Альтернативные архитектурные подходы для нагруженных систем

Рассмотренный выше запрос с группировкой по всему регистру сведений отлично работает на небольших и средних базах. Однако при росте объема регистра до сотен тысяч или миллионов записей группировка всей таблицы движений во вложенном подзапросе может создавать избыточную нагрузку на СУБД (Index Scan / Table Scan), приводя к задержкам при быстрой прокрутке списка.

Если производительность системы становится критичной, рассмотрим два архитектурных варианта оптимизации:

Вариант 1. Денормализация данных (реквизит в документе)

В документ Документ.ЗаказКлиента добавляется реквизит ТекущаяОперация. При проведении документа или при записи движений по регистру этот реквизит программно обновляется актуальным значением. В этом случае в динамическом списке отпадают любые соединения с регистром — данные берутся напрямую из реквизита документа с максимальной скоростью выборки.

Вариант 2. Вспомогательный регистр текущих состояний

Создается независимый непериодический регистр сведений (например, ТекущееСостояниеЗаказов). В нем создается измерение ЗаказКлиента (тип — ссылка на документ) и ресурс Операция. При изменении статуса в этот регистр записывается ровно одна запись на каждый заказ (новая запись перезаписывает старую). В динамическом списке выполняется простое ЛЕВОЕ СОЕДИНЕНИЕ по измерению, которое отрабатывает по первичному ключу кластерного индекса мгновенно.

← На главную