При разработке интерфейсов на платформе 1С:Предприятие 8.3 часто возникает задача вывести в список документов их актуальный статус или последнюю выполненную операцию. Например, в форме списка документов Документ.ЗаказКлиента требуется отобразить колонку с текущим состоянием из регистра сведений РегистрСведений.ОтслеживаниеПО, для которого данный документ выступает регистратором.
Нередко разработчики пытаются решить эту задачу «в лоб» — обращением к виртуальной таблице СрезПоследних с соединением по полю Регистратор. Однако результат оказывается ошибочным: в списке либо отображается статус только для одного документа, либо данные заполняются неверно. Разберем по шагам, почему возникает эта ошибка и как составить правильный запрос для объекта ДинамическийСписок.
Проанализируем механику работы виртуальной таблицы СрезПоследних. Платформа 1С рассчитывает срез последних записей строго в разрезе уникальных комбинаций измерений регистра сведений. Стандартный реквизит Регистратор измерением не является — это системное служебное поле связи с документом-источником движения.
Из этого следуют две главные проблемы при попытке использовать СрезПоследних:
Регистратор = Ссылка значение отобразится только у того единственного документа, который сделал эту самую последнюю запись.Период у всех этих записей будет одинаковым — оно соответствует дате документа. В таком случае порядок записей определяется не временем, а стандартным реквизитом НомерСтроки. Следовательно, выборка максимальной даты не позволит однозначно определить последнюю операцию в рамках одного заказа.Для вывода больших объемов данных в управляемых формах 1С используется ДинамическийСписок. При формировании произвольного запроса для динамического списка необходимо учитывать ключевые архитектурные ограничения платформы:
ВЫБРАТЬ. Использовать пакетную конструкцию с точкой с запятой (;) и директивой ПОМЕСТИТЬ для создания временных таблиц в динамическом списке нельзя — платформа выдаст синтаксическую ошибку при инициализации формы.ПОЛНОЕ СОЕДИНЕНИЕ ломает оптимизацию постраничного считывания данных СУБД, затрудняет наложение платформенных отборов и может привести к непредсказуемым замедлениям интерфейса. В динамических списках всегда следует применять ЛЕВОЕ СОЕДИНЕНИЕ.Документ.ЗаказКлиента), а поле Ссылка должно быть отмечено как ключевое.Поскольку временные таблицы использовать нельзя, построим искусственный срез с помощью вложенного подзапроса. Проанализируем алгоритм:
ЛЕВОЕ СОЕДИНЕНИЕ подключаем вложенный запрос к реальной таблице движений регистра сведений (без использования виртуальной таблицы среза). В подзапросе выполняем группировку по полю Регистратор и вычисляем МАКСИМУМ(НомерСтроки) для каждого документа.Регистратор и НомерСтроки. Это позволяет однозначно получить ресурс Операция именно из финальной строки движений документа.Посмотрим на готовый текст запроса для динамического списка:
ВЫБРАТЬ
Заказ.Ссылка КАК Ссылка,
Заказ.Номер КАК Номер,
Заказ.Дата КАК Дата,
ОтслеживаниеПО.Операция КАК Операция
ИЗ
Документ.ЗаказКлиента КАК Заказ
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
Срез.Регистратор КАК Регистратор,
МАКСИМУМ(Срез.НомерСтроки) КАК МаксНомерСтроки
ИЗ
РегистрСведений.ОтслеживаниеПО КАК Срез
СГРУППИРОВАТЬ ПО
Срез.Регистратор
) КАК ПоследниеСтроки
ПО Заказ.Ссылка = ПоследниеСтроки.Регистратор
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОтслеживаниеПО КАК ОтслеживаниеПО
ПО ПоследниеСтроки.Регистратор = ОтслеживаниеПО.Регистратор
И ПоследниеСтроки.МаксНомерСтроки = ОтслеживаниеПО.НомерСтроки
Разберем важные детали настройки списка в палитре свойств:
Документ.ЗаказКлиента.Ссылка стоял флаг Использовать всегда или статус ключевого поля.Операция на форму динамический список будет корректно подтягивать статус именно для той строки документа, которая отображается в текущий момент на экране.Рассмотренный выше запрос с группировкой по всему регистру сведений отлично работает на небольших и средних базах. Однако при росте объема регистра до сотен тысяч или миллионов записей группировка всей таблицы движений во вложенном подзапросе может создавать избыточную нагрузку на СУБД (Index Scan / Table Scan), приводя к задержкам при быстрой прокрутке списка.
Если производительность системы становится критичной, рассмотрим два архитектурных варианта оптимизации:
В документ Документ.ЗаказКлиента добавляется реквизит ТекущаяОперация. При проведении документа или при записи движений по регистру этот реквизит программно обновляется актуальным значением. В этом случае в динамическом списке отпадают любые соединения с регистром — данные берутся напрямую из реквизита документа с максимальной скоростью выборки.
Создается независимый непериодический регистр сведений (например, ТекущееСостояниеЗаказов). В нем создается измерение ЗаказКлиента (тип — ссылка на документ) и ресурс Операция. При изменении статуса в этот регистр записывается ровно одна запись на каждый заказ (новая запись перезаписывает старую). В динамическом списке выполняется простое ЛЕВОЕ СОЕДИНЕНИЕ по измерению, которое отрабатывает по первичному ключу кластерного индекса мгновенно.