Разработка и Конфигурация · 11 мин
Как перебрать все записи регистра сведений в 1С
Способы перебора записей регистра сведений в 1С: Выбор(), запрос, набор записей. Примеры кода, отборы, производительность, типичные ошибки и FAQ.
Перебор записей регистра сведений — одна из базовых задач при разработке и обслуживании конфигураций 1С:Предприятие. Она возникает при выгрузке данных, пакетной обработке, поиске дубликатов, синхронизации с внешними системами и при отладке. В языке 1С есть несколько способов обойти записи регистра: метод Выбрать() менеджера регистра, запрос с обходом результата и чтение через набор записей. Каждый способ имеет свои ограничения по скорости, объёму потребляемой памяти и удобству отбора.
Выбор способа зависит от объёма данных и задачи. Для интерактивной обработки небольшого регистра достаточно цикла по выборке. Для массовых операций на сотнях тысяч записей правильнее использовать запрос с пакетной обработкой или порционное чтение, иначе обработка может занять часы и заблокировать таблицу в клиент-серверном режиме. Ниже разобраны все рабочие варианты с примерами кода, которые можно скопировать и адаптировать под свой регистр.
Перебор через метод Выбрать() — базовый способ
Менеджер регистра сведений предоставляет метод Выбрать(), который возвращает выборку записей. Обход выполняется в цикле Пока Выборка.Следующий() Цикл. Внутри цикла доступны все измерения, ресурсы и реквизиты текущей записи, а для периодических регистров — поле Период и Регистратор (если регистр подчинён регистратору).
Выборка = РегистрыСведений.КурсыВалют.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Строка(Выборка.Период) + " | "
+ Выборка.Валюта + " | "
+ Выборка.Курс);
КонецЦикла;
У метода Выбрать() есть параметры: начало и конец интервала (для периодических регистров), отбор по измерениям и порядок сортировки. Отбор передаётся структурой, где ключ — имя измерения, а значение — искомое значение. Это позволяет перебирать не весь регистр, а только нужное подмножество:
Отбор = Новый Структура("Валюта", ВалютаДоллар);
Выборка = РегистрыСведений.КурсыВалют.Выбрать( , , Отбор, "Убыв");
Пока Выборка.Следующий() Цикл
// записи только по выбранной валюте, от новых к старым
КонецЦикла;
Важно понимать: выборка через Выбрать() предназначена в первую очередь для чтения. Записи, полученные в цикле, можно анализировать, но для изменения конкретной записи нужно обращаться к менеджеру записи или набору записей отдельно.
Перебор через запрос — для больших объёмов
Когда записей много или нужен гибкий отбор по условиям, соединения с другими таблицами и группировки, правильнее использовать запрос. Запрос выполняется на сервере СУБД и обычно заметно быстрее перебора через Выбрать(), особенно в клиент-серверном режиме.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Курсы.Период,
| Курсы.Валюта,
| Курсы.Курс
|ИЗ
| РегистрСведений.КурсыВалют КАК Курсы
|ГДЕ
| Курсы.Валюта = &Валюта
|УПОРЯДОЧИТЬ ПО
| Курсы.Период УБЫВ";
Запрос.УстановитьПараметр("Валюта", ВалютаДоллар);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Строка(Выборка.Период) + " — " + Выборка.Курс);
КонецЦикла;
Для независимых периодических регистров часто нужна не вся история, а актуальные значения. В этом случае используйте виртуальную таблицу СрезПоследних — она сразу вернёт по одной записи на каждую комбинацию измерений:
Запрос.Текст =
"ВЫБРАТЬ
| Срез.Валюта,
| Срез.Курс
|ИЗ
| РегистрСведений.КурсыВалют.СрезПоследних(&Дата, ) КАК Срез";
Если результат запроса нужен многократно внутри обработки, выгрузите его в таблицу значений методом Выгрузить() и обходите уже её — это избавит от повторных обращений к СУБД и позволит свободно перемещаться по строкам.
Сравнение вариантов
Простой код, отбор только по равенству измерений. Подходит для небольших регистров и быстрой отладки.
Гибкие условия, соединения, виртуальные таблицы, высокая скорость на больших объёмах. Требует написания текста запроса.
Позволяет читать и изменять группу записей по отбору. Не подходит для перебора всего регистра без отбора — слишком тяжёлый.
Чтение через набор записей
Набор записей — объект РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей(), который загружает в память записи, удовлетворяющие установленному отбору. После чтения методом Прочитать() по набору можно итеративно пройти циклом Для Каждого и при необходимости сразу изменять данные.
Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.ВидЦены.Установить(ВидЦеныРозничный);
Набор.Прочитать();
Для Каждого Запись Из Набор Цикл
Запись.Цена = Запись.Цена * 1.1;
КонецЦикла;
Набор.Записать();
Главное ограничение: набор записей всегда читает данные целиком по установленному отбору и держит их в памяти. Если установить отбор по всем измерениям пустым и прочитать регистр из миллиона записей, можно получить нехватку памяти на сервере. Поэтому набор записей применяют для точечной обработки по конкретному значению измерения, а не для полного обхода регистра.
Для изменения одной конкретной записи независимого регистра удобнее менеджер записи: СоздатьМенеджерЗаписи(), заполнение ключевых полей, Прочитать(), проверка Выбран(), изменение и Записать().
Пакетная обработка большого регистра
Если регистр содержит сотни тысяч или миллионы записей, однопроходный обход может занять много времени и удерживать длительную транзакцию. Практичный приём — порционная обработка: на каждой итерации запрашивается ограниченная пачка записей (например, через ВЫБРАТЬ ПЕРВЫЕ 1000), обрабатывается, после чего берётся следующая порция по условию «ключ больше последнего обработанного».
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Рег.Номенклатура,
| Рег.Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры КАК Рег
|ГДЕ
| Рег.Номенклатура > &ПоследнийКлюч
|УПОРЯДОЧИТЬ ПО
| Рег.Номенклатура";
ПоследнийКлюч = Неопределено;
Пока Истина Цикл
Запрос.УстановитьПараметр("ПоследнийКлюч", ПоследнийКлюч);
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Прервать;
КонецЕсли;
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
// обработка записи
ПоследнийКлюч = Выборка.Номенклатура;
КонецЦикла;
КонецЦикла;
Дополнительные меры для тяжёлых обработок: запуск в фоновом задании, чтобы не блокировать сеанс пользователя; вывод прогресса через ДлительныеОперации в БСП; запись изменений отдельными транзакциями по каждой порции, чтобы при сбое не откатывалась вся работа.
- 1Сделайте копию базы или убедитесь в наличии свежей резервной копии
- 2Напишите запрос с отбором и проверьте на малом подмножестве, что выбираются именно нужные записи
- 3Добавьте порционное чтение и обработку в транзакциях по каждой порции
- 4Запустите обработку в фоновом задании на тестовой копии, замерьте время
- 5Выполните на рабочей базе и проверьте результат контрольным запросом
Особенности для разных видов регистров
Независимый непериодический регистр
Самый простой случай: записи идентифицируются комбинацией измерений. Перебор через Выбрать() или запрос работает напрямую, виртуальных таблиц нет. Порядок обхода по умолчанию — по измерениям.
Периодический регистр
Добавляется поле Период, и у Выбрать() появляются параметры границ интервала. Для получения актуальных данных используйте виртуальные таблицы СрезПоследних и СрезПервых — самостоятельный перебор всей истории с ручным поиском последней записи почти всегда медленнее и сложнее.
Регистр, подчинённый регистратору
Записи такого регистра создаются и удаляются только документами-регистраторами. Перебирать их для чтения можно теми же способами, но изменять напрямую нельзя — набор записей всегда имеет отбор по регистратору, а запись выполняется при проведении документа. Если нужно «исправить» движения, правильный путь — перепроведение регистратора или корректирующий документ.
Проверка результата и отладка
После обработки полезно убедиться, что перебраны все записи и изменения применились корректно. Простейший контроль — сравнить количество записей до и после: запрос ВЫБРАТЬ Количество(*) ИЗ РегистрСведений.ИмяРегистра даёт точное число строк. Для проверки содержимого удобна консоль запросов или динамический список в режиме предприятия с отбором по обработанным значениям.
При отладке цикла ставьте точку останова внутри тела цикла и смотрите значения полей выборки в табло. Если выборка пустая, проверьте: правильно ли указано имя регистра, не мешает ли отбор (значение отбора может быть пустой ссылкой), есть ли права на чтение регистра у текущего пользователя — при включённом RLS часть записей может быть невидима.
Как проверить результат
Совпадает с числом записей по контрольному запросу
Счётчик меньше — часть записей пропущена из-за отбора или изменения в цикле
Контрольный запрос показывает ожидаемые новые значения
Значения старые — запись не выполнена или откатилась транзакция
Обработка порциями идёт равномерно, без роста пауз
Каждая следующая порция медленнее — вероятно, условие отбора ключа составлено неверно
FAQ
Чем Выбрать() отличается от запроса при переборе регистра сведений?
Выбрать() — объектный метод с простым синтаксисом и отбором только по равенству измерений. Запрос выполняется на стороне СУБД, поддерживает произвольные условия, соединения и виртуальные таблицы, поэтому быстрее и гибче на больших объёмах. Для разовой обработки пары тысяч записей разница незаметна.
Как перебрать записи регистра сведений, подчинённого регистратору?
Теми же способами — через Выбрать() или запрос по таблице регистра. Ограничение касается только записи: изменять такие записи можно лишь через набор записей с отбором по регистратору, фактически — через перепроведение документа.
Можно ли удалить часть записей при переборе?
Да, но не внутри цикла по живой выборке. Соберите ключи удаляемых записей в массив, завершите обход, затем для каждой группы создайте набор записей с отбором и вызовите Записать() без чтения — пустой набор по отбору удалит соответствующие записи.
Как получить только последние значения периодического регистра?
Используйте в запросе виртуальную таблицу РегистрСведений.ИмяРегистра.СрезПоследних с параметром даты. Она вернёт по одной записи на каждую комбинацию измерений — перебирать всю историю вручную не нужно.
Почему выборка пустая, хотя записи в регистре есть?
Частые причины: отбор передан со значением несовпадающего типа (например, строка вместо ссылки), у пользователя нет прав чтения или действует ограничение RLS, либо перепутано имя регистра. Проверьте данные консолью запроса без отбора.
Как ускорить перебор очень большого регистра?
Читайте данные запросом порциями с условием по ключу, обрабатывайте в фоновом задании, не выводите сообщения на каждой итерации, а изменения записывайте отдельными транзакциями по порции. При возможности замените перебор одним запросом с группировкой.
Что важно запомнить
Для простого перебора используйте Выбрать() с циклом Пока Выборка.Следующий(), для больших объёмов и сложных условий — запрос, для изменения группы записей — набор записей с отбором. Не изменяйте регистр внутри обхода его выборки, обрабатывайте большие объёмы порциями и всегда проверяйте результат контрольным запросом.