При разработке в среде 1С:Предприятие часто возникает задача выборки записей из периодических регистров сведений, подчиненных регистратору, за определенный интервал времени. Нередко разработчики сомневаются, есть ли отличия в получении данных между независимыми и подчиненными регистрами, как учитывать поле активности и будет ли такой код стабильно работать в общем модуле в рамках регламентного задания. Рассмотрим все эти нюансы подробно и разберем практические примеры.
В подчиненном регистре сведений состав стандартных полей отличается от независимого регистра:
Чтение данных из подчиненного регистра сведений выполняется стандартным объектом Запрос, как и для независимых регистров. Однако при составлении текста запроса к физической таблице необходимо учитывать важную деталь: СУБД возвращает как активные, так и отключенные записи, если явно не наложено условие на поле Активность.
Разберем классический пример выборки записей за период с отбором по активности. Такой код полностью автономен и может выполняться на стороне сервера в общем модуле, в том числе при вызове из регламентного или фонового задания.
Посмотрим на пример текста запроса и обхода результатов:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Регистр.Период КАК Период,
| Регистр.Регистратор КАК Регистратор,
| Регистр.Груз КАК Груз,
| Регистр.Статус КАК Статус
|ИЗ
| РегистрСведений.уатСтатусГруза_уэ КАК Регистр
|ГДЕ
| Регистр.Период МЕЖДУ &ДатаНач И &ДатаКон
| И Регистр.Активность = ИСТИНА";
Запрос.УстановитьПараметр("ДатаНач", НачалоДня(Дата(2024, 4, 1)));
Запрос.УстановитьПараметр("ДатаКон", КонецДня(Дата(2024, 4, 30)));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ТекущийРегистратор = Выборка.Регистратор;
ТекущийГруз = Выборка.Груз;
ТекущийСтатус = Выборка.Статус;
// Обработка полученных данных
КонецЦикла;
Проанализируем, как запрос обращается к индексам СУБД при фильтрации по периоду. В подчиненных регистрах сведений основной кластерный индекс строится по полям Регистратор + НомерСтроки. Дополнительный индекс формируется из списка измерений и заканчивается полем Период:
Измерение_1 + Измерение_2 + ... + Период
Если в блоке ГДЕ мы указываем отбор исключительно по полю Период, не задавая значений для первых измерений регистра, СУБД не сможет использовать составной индекс оптимально. Это приведет к полному сканированию таблицы (Index Scan или Table Scan), что при миллионах записей вызовет высокую нагрузку на дисковую подсистему и процессор сервера.
Чтобы избежать просадок производительности, применим следующие рекомендации:
Выясним причину, по которой иногда вместо физической таблицы регистра выгоднее использовать виртуальную таблицу СрезПоследних:
РегистрСведений.ИмяРегистра является правильным решением.РегистрСведений.ИмяРегистра.СрезПоследних(&Период, ...). Виртуальная таблица сама отсекает неактивные записи и оптимизирует получение последнего среза. При этом отборы по измерениям нужно передавать во второй параметр виртуальной таблицы, а не в блок ГДЕ.Если данные необходимо прочитать не по произвольному периоду дат, а в контексте конкретного документа-регистратора, применять общий запрос по датам избыточно. Рассмотрим более быстрые методы объектной модели:
1. Использование метода менеджера ВыбратьПоРегистратору:
Выборка = РегистрыСведений.уатСтатусГруза_уэ.ВыбратьПоРегистратору(ДокументСсылка);
Пока Выборка.Следующий() Цикл
Если Выборка.Активность Тогда
// Обработка записи
КонецЕсли;
КонецЦикла;
2. Чтение через РегистрСведенийНаборЗаписей:
Набор = РегистрыСведений.уатСтатусГруза_уэ.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Прочитать();
Для Каждого Запись Из Набор Цикл
Если Запись.Активность Тогда
ТекущийГруз = Запись.Груз;
КонецЕсли;
КонецЦикла;
Чтение набора с отбором по регистратору обращается напрямую к кластерному индексу таблицы, что гарантирует максимальное быстродействие.