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

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

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

Разберем причину: почему записывается только одна строка?

Прежде чем переходить к коду, проанализируем ситуацию, когда при попытке массовой загрузки в регистре оказывается всего одна запись. В системе 1С уникальность записи в регистре сведений определяется комбинацией значений его измерений. Если в вашем регистре всего одно измерение (например, Контрагент), а ФИО контактного лица является ресурсом, то для одного контрагента в принципе не может существовать две разные записи. Каждая новая попытка записи будет просто затирать предыдущую.

Если ваша задача — хранить нескольких контактных лиц для одного контрагента, необходимо сделать поле ФИО (или Имя) вторым измерением регистра. Если же бизнес-логика подразумевает только одного ответственного, тогда ФИО остается ресурсом, и мы должны решить: обновлять его или игнорировать изменения.

Способ 1. Использование Менеджера Записи (для небольших объемов)

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


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

Важно: Этот способ прост в написании, но крайне неэффективен при больших объемах данных (от 1000 строк и выше), так как на каждую строку таблицы происходит отдельное обращение к базе данных.

Способ 2. Использование Набора Записей (оптимальный вариант)

Более профессиональный подход заключается в использовании объекта НаборЗаписей. Это позволяет работать с данными порциями, что значительно ускоряет процесс. Проанализируем ситуацию: нам нужно записать данные по конкретному контрагенту, не затронув других.


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

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

Способ 3. Массовая перезагрузка через Загрузить()

Если ваше регламентное задание подразумевает полное обновление данных в регистре по списку контрагентов, самым быстрым методом будет использование метода Загрузить(). Посмотрим на алгоритм:

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

Набор = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();
// Без установки отбора Записать() очистит ВЕСЬ регистр и запишет данные из ТЗ
Набор.Загрузить(ВашаТаблицаЗначений);
Набор.Записать();

Рекомендации по повышению надежности

Проанализируем несколько важных моментов, которые часто упускают при разработке:

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

Транзакционность. Если вы обрабатываете таблицу циклом, лучше обернуть весь цикл в одну транзакцию (НачатьТранзакцию() ... ЗафиксироватьТранзакцию()). Это гарантирует, что либо все данные будут записаны успешно, либо (в случае ошибки) система вернется к исходному состоянию.

Проверка на идентичность. Перед вызовом метода Записать() всегда проверяйте, действительно ли данные изменились. В 1С операция записи в базу данных — одна из самых «дорогих» по времени. Простая проверка Если СтароеЗначение <> НовоеЗначение Тогда может ускорить выполнение вашего кода в десятки раз, если большая часть данных в источнике не меняется день ото дня.

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

← На главную