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

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

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

Как программно перезаписать регистр сведений в 1С 8.3: наборы записей, менеджеры записи, отборы, типичные ошибки и проверка результата с примерами кода.

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

Главный принцип безопасной перезаписи: набор записей в 1С работает по правилу «прочитал — изменил — записал». Если записать пустой или незаполненный набор с установленным отбором, платформа удалит все записи, попадающие под этот отбор. Поэтому перед любой массовой перезаписью нужно понимать, какие записи уже существуют и что с ними должно произойти.

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

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

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

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

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

Перезапись через набор записей с отбором

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

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

НаборЗаписей.Отбор.Номенклатура.Установить(ТекущаяНоменклатура);

НаборЗаписей.Прочитать();

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

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

КонецЦикла;

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

Если существующие данные не нужны и требуется полная замена по отбору, чтение можно пропустить: заполнить набор новыми строками и записать. Старые строки по этому отбору будут удалены автоматически:

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

НаборЗаписей.Отбор.Владелец.Установить(Товар);

НоваяЗапись = НаборЗаписей.Добавить();

НоваяЗапись.Владелец = Товар;

НоваяЗапись.Штрихкод = "4600000000001";

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

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

Точечная перезапись через менеджер записи

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

МенеджерЗаписи = РегистрыСведений.ОтветственныеЛица.СоздатьМенеджерЗаписи();

МенеджерЗаписи.Организация = Организация;

МенеджерЗаписи.Период = ТекущаяДатаСеанса();

МенеджерЗаписи.Ответственный = НовыйСотрудник;

МенеджерЗаписи.Записать();

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

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

Сравнение способов перезаписи

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

Подходит для пакетной перезаписи группы строк и когда нужно прочитать текущие значения. Требует аккуратности с отбором.

Менеджер записи

Подходит для одиночной строки по известному ключу. Не читает существующие ресурсы — незаполненные поля обнулятся.

Запрос + набор записей

Оптимален для массовой обработки: запрос выбирает данные, наборы записей пишут порциями по ключам.

Массовая перезапись через запрос

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

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

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

"ВЫБРАТЬ

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

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

| Курсы.Курс КАК Курс

|ИЗ

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

|ГДЕ

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

|ИТОГИ ПО

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

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

ВыборкаВалюты = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

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

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

НаборЗаписей.Отбор.Валюта.Установить(ВыборкаВалюты.Валюта);

Выборка = ВыборкаВалюты.Выбрать();

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

Запись = НаборЗаписей.Добавить();

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

Запись.Период = Выборка.Период;

Запись.Курс = Выборка.Курс * Коэффициент;

КонецЦикла;

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

КонецЦикла;

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

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

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

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

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

При записи набора с отбором по измерению (без отбора по периоду) будут заменены все периоды этого измерения. Это частая причина потери истории: разработчик хотел изменить одну дату, а удалил всю историю по объекту.

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

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

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

НаборЗаписей.Отбор.Регистратор.Установить(ДокументСсылка);

НаборЗаписей.Прочитать();

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

Запись.Статус = Перечисления.Статусы.Согласован;

КонецЦикла;

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

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

Типичные ошибки при программной перезаписи

Большинство проблем при перезаписи регистров сведений сводится к нескольким повторяющимся сценариям. Их знание позволяет проверить код до запуска на рабочей базе.

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

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

Проверка результата и откат изменений

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

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

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

"ВЫБРАТЬ

| КОЛИЧЕСТВО(*) КАК КоличествоЗаписей

|ИЗ

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

|ГДЕ

| Цены.Номенклатура = &Номенклатура";

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

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

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

Количество записей по отбору до и после
Норма

Совпадает с ожидаемым по логике операции

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

Записей стало меньше без причины — вероятно, набор записан без чтения

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

Обновлены только целевые ресурсы, остальные сохранились

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

Нецелевые ресурсы обнулены — перезапись шла без чтения существующих строк

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

История по датам сохранена, изменены нужные точки

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

Пропала история — набор записан с отбором по измерению без учёта периодов

Частые вопросы

Чем отличается Записать() у набора записей от Записать() у менеджера записи?

Набор записей замещает все строки, попадающие под его отбор, содержимым набора — это операция над группой. Менеджер записи работает с одной строкой по конкретному ключу измерений: создаёт её или перезаписывает, не затрагивая соседние записи.

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

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

Почему возникает ошибка «Запись с такими ключевыми полями существует»?

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

Можно ли перезаписать регистр сведений, подчинённый регистратору, без документа?

Нет, отбор по регистратору обязателен — записать «висячие» строки нельзя. Корректный путь — изменить логику в обработчике проведения документа и перепровести его, либо записать набор с отбором по конкретному документу-регистратору.

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

Прочитайте существующую строку набором записей с отбором по ключу, измените нужный ресурс в полученной строке и запишите набор обратно. Менеджер записи для этого не подходит: незаполненные в нём ресурсы запишутся пустыми значениями.

Нужно ли выполнять массовую перезапись в транзакции?

Для порционной записи транзакция на каждую порцию устанавливается платформой автоматически при вызове Записать(). Оборачивать всю обработку в одну внешнюю транзакцию не стоит: это удлинит блокировки таблицы. Лучше фиксировать порции и хранить признак обработанных ключей для возможности продолжения после сбоя.

Итог

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

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