Как правильно читать данные из периодического регистра сведений, подчиненного регистратору?

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

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

Особенности структуры подчиненного регистра сведений

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

  1. Регистратор — ссылка на документ-регистратор, который сформировал запись.
  2. НомерСтроки — порядковый номер строки в наборе записей конкретного регистратора.
  3. Период — дата и время движения (при периодичности «По позиции регистратора» привязывается к моменту времени документа).
  4. Активность — булевый флаг, определяющий, активна ли запись. Неактивные записи не учитываются при расчете срезов и итогов.

Чтение данных из подчиненного регистра сведений выполняется стандартным объектом Запрос, как и для независимых регистров. Однако при составлении текста запроса к физической таблице необходимо учитывать важную деталь: СУБД возвращает как активные, так и отключенные записи, если явно не наложено условие на поле Активность.

Чтение данных через запрос к физической таблице

Разберем классический пример выборки записей за период с отбором по активности. Такой код полностью автономен и может выполняться на стороне сервера в общем модуле, в том числе при вызове из регламентного или фонового задания.

Посмотрим на пример текста запроса и обхода результатов:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Регистр.Период КАК Период,
    |    Регистр.Регистратор КАК Регистратор,
    |    Регистр.Груз КАК Груз,
    |    Регистр.Статус КАК Статус
    |ИЗ
    |    РегистрСведений.уатСтатусГруза_уэ КАК Регистр
    |ГДЕ
    |    Регистр.Период МЕЖДУ &ДатаНач И &ДатаКон
    |    И Регистр.Активность = ИСТИНА";

Запрос.УстановитьПараметр("ДатаНач", НачалоДня(Дата(2024, 4, 1)));
Запрос.УстановитьПараметр("ДатаКон", КонецДня(Дата(2024, 4, 30)));

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    ТекущийРегистратор = Выборка.Регистратор;
    ТекущийГруз = Выборка.Груз;
    ТекущийСтатус = Выборка.Статус;
    
    // Обработка полученных данных
КонецЦикла;

Нюансы производительности и индексации при отборе по периоду

Проанализируем, как запрос обращается к индексам СУБД при фильтрации по периоду. В подчиненных регистрах сведений основной кластерный индекс строится по полям Регистратор + НомерСтроки. Дополнительный индекс формируется из списка измерений и заканчивается полем Период:

Измерение_1 + Измерение_2 + ... + Период

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

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

  1. Добавлять фильтрацию по ключевым измерениям — если известны конкретные значения первых измерений регистра, их всегда следует передавать в условия запроса.
  2. Включать индексирование полей — если по определенному измерению часто выполняются выборки без других условий, в конфигураторе для него следует установить свойство «Индексировать».
  3. Разделять выборку на порции — при работе в регламентных заданиях большой интервал дат лучше обрабатывать небольшими временными окнами, чтобы не удерживать длительные блокировки данных и избегать взаимных блокировок (deadlocks) с проводящимися документами.

Выбор между физической и виртуальной таблицей

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

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

Альтернативные способы чтения в разрезе регистратора

Если данные необходимо прочитать не по произвольному периоду дат, а в контексте конкретного документа-регистратора, применять общий запрос по датам избыточно. Рассмотрим более быстрые методы объектной модели:

1. Использование метода менеджера ВыбратьПоРегистратору:


Выборка = РегистрыСведений.уатСтатусГруза_уэ.ВыбратьПоРегистратору(ДокументСсылка);
Пока Выборка.Следующий() Цикл
    Если Выборка.Активность Тогда
        // Обработка записи
    КонецЕсли;
КонецЦикла;

2. Чтение через РегистрСведенийНаборЗаписей:


Набор = РегистрыСведений.уатСтатусГруза_уэ.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Прочитать();

Для Каждого Запись Из Набор Цикл
    Если Запись.Активность Тогда
        ТекущийГруз = Запись.Груз;
    КонецЕсли;
КонецЦикла;

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

← На главную