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

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

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

Почему нельзя просто изменить измерение в цикле

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

Способ 1. Использование промежуточной таблицы значений (наиболее надежный)

Этот метод считается классическим и наиболее безопасным для целостности данных. Его суть заключается в том, чтобы «перенести» данные в память, удалить старые записи из базы и записать новые. Разберем этот процесс по шагам:

  1. Создаем объект НаборЗаписей.
  2. Устанавливаем отбор по тому значению измерения, которое хотим заменить (например, по старому контрагенту).
  3. Читаем данные из базы в набор.
  4. Выгружаем содержимое набора в ТаблицуЗначений.
  5. Очищаем старые записи: записываем пустой набор с установленным отбором.
  6. В таблице значений программно меняем старое значение на новое.
  7. Загружаем модифицированную таблицу обратно в набор (но уже с новым отбором) и записываем.

Рассмотрим пример кода для реализации этого алгоритма:


// Определяем параметры замены
СтарыйКонтрагент = Справочники.Контрагенты.НайтиПоНаименованию("Старый клиент");
НовыйКонтрагент  = Справочники.Контрагенты.НайтиПоНаименованию("Новый клиент");

// 1. Подготавливаем набор для чтения старых записей
НаборСтарый = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
НаборСтарый.Отбор.Контрагент.Установить(СтарыйКонтрагент);
НаборСтарый.Прочитать();

// 2. Выгружаем данные в ТЗ и очищаем регистр от старых записей
ТаблицаДанных = НаборСтарый.Выгрузить();
НаборСтарый.Очистить();
НаборСтарый.Записать(Истина); // Удаляем записи по СтарыйКонтрагент

// 3. Модифицируем данные в таблице значений
Для Каждого Строка Из ТаблицаДанных Цикл
    Строка.Контрагент = НовыйКонтрагент;
КонецЦикла;

// 4. Записываем новые данные
НаборНовый = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
НаборНовый.Отбор.Контрагент.Установить(НовыйКонтрагент);
НаборНовый.Загрузить(ТаблицаДанных);
НаборНовый.Записать(Ложь); // Дописываем данные, не затирая существующие по НовомуКонтрагенту

Обратите внимание на параметр Ложь в методе Записать(). Если вы уверены, что по «Новому контрагенту» записей в регистре еще нет, можно использовать Истина, но для безопасности лучше использовать добавление, чтобы случайно не стереть другие данные по целевому измерению.

Способ 2. Использование Менеджера записи в цикле

Если объем данных невелик, можно воспользоваться объектом РегистрСведенийМенеджерЗаписи. Этот метод позволяет работать с каждой записью индивидуально. Рассмотрим подробнее, как это реализовать:


// Создаем набор для перебора всех или части записей
Набор = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
// Можно установить отбор, если нужно менять не всё
Набор.Прочитать();

Для Каждого ТекЗапись Из Набор Цикл
    // Используем менеджер записи для каждой строки
    Менеджер = РегистрыСведений.МойРегистр.СоздатьМенеджерЗаписи();
    ЗаполнитьЗначенияСвойств(Менеджер, ТекЗапись);
    
    // Сначала читаем и удаляем старую запись
    Менеджер.Прочитать();
    Если Менеджер.Выбран() Тогда
        Менеджер.Удалить();
    КонецЕсли;
    
    // Меняем измерение и записываем как новую запись
    Менеджер.Контрагент = НовыйКонтрагент;
    Менеджер.Записать();
КонецЦикла;

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

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

При работе с большими объемами данных (сотни тысяч записей) выгрузка всего регистра в ТаблицуЗначений может вызвать ошибку «Недостаточно памяти». В таких ситуациях проанализируем следующие рекомендации:

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

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

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

← На главную