К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как перебрать все записи регистра сведений в 1С

Способы перебора записей регистра сведений в 1С: Выбор(), запрос, набор записей. Примеры кода, отборы, производительность, типичные ошибки и FAQ.

Перебор записей регистра сведений — одна из базовых задач при разработке и обслуживании конфигураций 1С:Предприятие. Она возникает при выгрузке данных, пакетной обработке, поиске дубликатов, синхронизации с внешними системами и при отладке. В языке 1С есть несколько способов обойти записи регистра: метод Выбрать() менеджера регистра, запрос с обходом результата и чтение через набор записей. Каждый способ имеет свои ограничения по скорости, объёму потребляемой памяти и удобству отбора.

Выбор способа зависит от объёма данных и задачи. Для интерактивной обработки небольшого регистра достаточно цикла по выборке. Для массовых операций на сотнях тысяч записей правильнее использовать запрос с пакетной обработкой или порционное чтение, иначе обработка может занять часы и заблокировать таблицу в клиент-серверном режиме. Ниже разобраны все рабочие варианты с примерами кода, которые можно скопировать и адаптировать под свой регистр.

Перебор через метод Выбрать() — базовый способ

Менеджер регистра сведений предоставляет метод Выбрать(), который возвращает выборку записей. Обход выполняется в цикле Пока Выборка.Следующий() Цикл. Внутри цикла доступны все измерения, ресурсы и реквизиты текущей записи, а для периодических регистров — поле Период и Регистратор (если регистр подчинён регистратору).

Выборка = РегистрыСведений.КурсыВалют.Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить(Строка(Выборка.Период) + " | "

+ Выборка.Валюта + " | "

+ Выборка.Курс);

КонецЦикла;

У метода Выбрать() есть параметры: начало и конец интервала (для периодических регистров), отбор по измерениям и порядок сортировки. Отбор передаётся структурой, где ключ — имя измерения, а значение — искомое значение. Это позволяет перебирать не весь регистр, а только нужное подмножество:

Отбор = Новый Структура("Валюта", ВалютаДоллар);

Выборка = РегистрыСведений.КурсыВалют.Выбрать( , , Отбор, "Убыв");

Пока Выборка.Следующий() Цикл

// записи только по выбранной валюте, от новых к старым

КонецЦикла;

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

Перебор через запрос — для больших объёмов

Когда записей много или нужен гибкий отбор по условиям, соединения с другими таблицами и группировки, правильнее использовать запрос. Запрос выполняется на сервере СУБД и обычно заметно быстрее перебора через Выбрать(), особенно в клиент-серверном режиме.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Курсы.Период,

| Курсы.Валюта,

| Курсы.Курс

|ИЗ

| РегистрСведений.КурсыВалют КАК Курсы

|ГДЕ

| Курсы.Валюта = &Валюта

|УПОРЯДОЧИТЬ ПО

| Курсы.Период УБЫВ";

Запрос.УстановитьПараметр("Валюта", ВалютаДоллар);

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

Пока Выборка.Следующий() Цикл

Сообщить(Строка(Выборка.Период) + " — " + Выборка.Курс);

КонецЦикла;

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

Запрос.Текст =

"ВЫБРАТЬ

| Срез.Валюта,

| Срез.Курс

|ИЗ

| РегистрСведений.КурсыВалют.СрезПоследних(&Дата, ) КАК Срез";

Если результат запроса нужен многократно внутри обработки, выгрузите его в таблицу значений методом Выгрузить() и обходите уже её — это избавит от повторных обращений к СУБД и позволит свободно перемещаться по строкам.

Сравнение вариантов

Выбрать()

Простой код, отбор только по равенству измерений. Подходит для небольших регистров и быстрой отладки.

Запрос

Гибкие условия, соединения, виртуальные таблицы, высокая скорость на больших объёмах. Требует написания текста запроса.

Набор записей

Позволяет читать и изменять группу записей по отбору. Не подходит для перебора всего регистра без отбора — слишком тяжёлый.

Чтение через набор записей

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

Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();

Набор.Отбор.ВидЦены.Установить(ВидЦеныРозничный);

Набор.Прочитать();

Для Каждого Запись Из Набор Цикл

Запись.Цена = Запись.Цена * 1.1;

КонецЦикла;

Набор.Записать();

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

Для изменения одной конкретной записи независимого регистра удобнее менеджер записи: СоздатьМенеджерЗаписи(), заполнение ключевых полей, Прочитать(), проверка Выбран(), изменение и Записать().

Пакетная обработка большого регистра

Если регистр содержит сотни тысяч или миллионы записей, однопроходный обход может занять много времени и удерживать длительную транзакцию. Практичный приём — порционная обработка: на каждой итерации запрашивается ограниченная пачка записей (например, через ВЫБРАТЬ ПЕРВЫЕ 1000), обрабатывается, после чего берётся следующая порция по условию «ключ больше последнего обработанного».

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ ПЕРВЫЕ 1000

| Рег.Номенклатура,

| Рег.Цена

|ИЗ

| РегистрСведений.ЦеныНоменклатуры КАК Рег

|ГДЕ

| Рег.Номенклатура > &ПоследнийКлюч

|УПОРЯДОЧИТЬ ПО

| Рег.Номенклатура";

ПоследнийКлюч = Неопределено;

Пока Истина Цикл

Запрос.УстановитьПараметр("ПоследнийКлюч", ПоследнийКлюч);

Результат = Запрос.Выполнить();

Если Результат.Пустой() Тогда

Прервать;

КонецЕсли;

Выборка = Результат.Выбрать();

Пока Выборка.Следующий() Цикл

// обработка записи

ПоследнийКлюч = Выборка.Номенклатура;

КонецЦикла;

КонецЦикла;

Дополнительные меры для тяжёлых обработок: запуск в фоновом задании, чтобы не блокировать сеанс пользователя; вывод прогресса через ДлительныеОперации в БСП; запись изменений отдельными транзакциями по каждой порции, чтобы при сбое не откатывалась вся работа.

Порядок действий для безопасной массовой обработки
  1. 1
    Сделайте копию базы или убедитесь в наличии свежей резервной копии
  2. 2
    Напишите запрос с отбором и проверьте на малом подмножестве, что выбираются именно нужные записи
  3. 3
    Добавьте порционное чтение и обработку в транзакциях по каждой порции
  4. 4
    Запустите обработку в фоновом задании на тестовой копии, замерьте время
  5. 5
    Выполните на рабочей базе и проверьте результат контрольным запросом

Особенности для разных видов регистров

Независимый непериодический регистр

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

Периодический регистр

Добавляется поле Период, и у Выбрать() появляются параметры границ интервала. Для получения актуальных данных используйте виртуальные таблицы СрезПоследних и СрезПервых — самостоятельный перебор всей истории с ручным поиском последней записи почти всегда медленнее и сложнее.

Регистр, подчинённый регистратору

Записи такого регистра создаются и удаляются только документами-регистраторами. Перебирать их для чтения можно теми же способами, но изменять напрямую нельзя — набор записей всегда имеет отбор по регистратору, а запись выполняется при проведении документа. Если нужно «исправить» движения, правильный путь — перепроведение регистратора или корректирующий документ.

Типичные ошибки
01
Изменение регистра внутри цикла по его же выборке — записи пропускаются или обрабатываются дважды
02
Чтение всего регистра набором записей без отбора — переполнение памяти на сервере
03
Перебор всей истории периодического регистра вместо использования СрезПоследних — лишняя работа и медленный код
04
Вызов Сообщить() на каждой итерации большого цикла — обработка замедляется в разы
05
Отсутствие проверки Выборка.Следующий() на пустой результат — код молча ничего не делает, и это принимают за ошибку платформы

Проверка результата и отладка

После обработки полезно убедиться, что перебраны все записи и изменения применились корректно. Простейший контроль — сравнить количество записей до и после: запрос ВЫБРАТЬ Количество(*) ИЗ РегистрСведений.ИмяРегистра даёт точное число строк. Для проверки содержимого удобна консоль запросов или динамический список в режиме предприятия с отбором по обработанным значениям.

При отладке цикла ставьте точку останова внутри тела цикла и смотрите значения полей выборки в табло. Если выборка пустая, проверьте: правильно ли указано имя регистра, не мешает ли отбор (значение отбора может быть пустой ссылкой), есть ли права на чтение регистра у текущего пользователя — при включённом RLS часть записей может быть невидима.

Контрольные точки

Как проверить результат

Количество обработанных записей
Норма

Совпадает с числом записей по контрольному запросу

Тревожный признак

Счётчик меньше — часть записей пропущена из-за отбора или изменения в цикле

Значения ресурсов после обработки
Норма

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

Тревожный признак

Значения старые — запись не выполнена или откатилась транзакция

Время выполнения
Норма

Обработка порциями идёт равномерно, без роста пауз

Тревожный признак

Каждая следующая порция медленнее — вероятно, условие отбора ключа составлено неверно

FAQ

Чем Выбрать() отличается от запроса при переборе регистра сведений?

Выбрать() — объектный метод с простым синтаксисом и отбором только по равенству измерений. Запрос выполняется на стороне СУБД, поддерживает произвольные условия, соединения и виртуальные таблицы, поэтому быстрее и гибче на больших объёмах. Для разовой обработки пары тысяч записей разница незаметна.

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

Теми же способами — через Выбрать() или запрос по таблице регистра. Ограничение касается только записи: изменять такие записи можно лишь через набор записей с отбором по регистратору, фактически — через перепроведение документа.

Можно ли удалить часть записей при переборе?

Да, но не внутри цикла по живой выборке. Соберите ключи удаляемых записей в массив, завершите обход, затем для каждой группы создайте набор записей с отбором и вызовите Записать() без чтения — пустой набор по отбору удалит соответствующие записи.

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

Используйте в запросе виртуальную таблицу РегистрСведений.ИмяРегистра.СрезПоследних с параметром даты. Она вернёт по одной записи на каждую комбинацию измерений — перебирать всю историю вручную не нужно.

Почему выборка пустая, хотя записи в регистре есть?

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

Как ускорить перебор очень большого регистра?

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

Итог

Что важно запомнить

Для простого перебора используйте Выбрать() с циклом Пока Выборка.Следующий(), для больших объёмов и сложных условий — запрос, для изменения группы записей — набор записей с отбором. Не изменяйте регистр внутри обхода его выборки, обрабатывайте большие объёмы порциями и всегда проверяйте результат контрольным запросом.