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

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

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

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

Анализ исходной задачи и кода

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


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

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

Проблема производительности: Запись в цикле

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

Правильный подход: необходимо подготавливать весь набор записей в оперативной памяти и выполнять метод Записать() один раз для всего набора.

Разберем исправленный алгоритм:

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

Посмотрим на пример оптимизированного кода:


Процедура ПриЗаписи(Отказ)
    
    // Создаем набор один раз
    НаборЗаписей = РегистрыСведений.СурвГрафикиРаботы.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.ГрафикРаботы.Установить(Ссылка);
    
    // Если объект помечен на удаление — просто записываем пустой набор (очищаем регистр)
    Если ПометкаУдаления Тогда
        НаборЗаписей.Записать();
        Возврат;
    КонецЕсли;
    
    // Если не помечен — заполняем набор данными из ТЧ
    Для Каждого стр Из Список Цикл
        НовЗапись = НаборЗаписей.Добавить();
        НовЗапись.Период = стр.Дата; // Или иная логика определения периода
        НовЗапись.ГрафикРаботы = Ссылка;
        НовЗапись.ВремяВхода = стр.ВремяВхода;
        НовЗапись.ВремяВыхода = стр.ВремяВыхода;
        // ... заполнение остальных полей
    КонецЦикла;
    
    // Записываем всё одним пакетом
    НаборЗаписей.Записать();
    
КонецПроцедуры

Обеспечение целостности данных и очистка старых записей

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

Вариант 1: Свойство «Ведущее». Рассмотрим настройки регистра в Конфигураторе. Если для измерения ГрафикРаботы установить свойство Ведущее, то при физическом удалении элемента справочника из базы данных платформа автоматически удалит все связанные записи из этого регистра. Это надежный механизм контроля ссылочной целостности.

Вариант 2: Замещение записей. Метод Записать() по умолчанию (с параметром Истина) сначала удаляет все существующие в базе данных записи, которые попадают под установленный в наборе отбор, а затем записывает новые. В нашем примере отбор установлен только по полю ГрафикРаботы. Это означает, что при каждой перезаписи справочника все старые записи по этому графику будут удалены и заменены актуальными данными из табличной части. Это гарантирует, что если вы удалили строку из ТЧ справочника, она исчезнет и из регистра после сохранения.

Транзакционность: ПриЗаписи или ПослеЗаписи?

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

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

Архитектурный совет: Нужна ли табличная часть?

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

Специалисты часто рекомендуют следующий подход: 1. Отказаться от ТЧ в справочнике. Храните данные только в регистре сведений.

2. Использовать Динамический список. На форму справочника добавьте таблицу, источником данных для которой будет динамический список этого регистра сведений с отбором по ссылке текущего элемента.

3. Редактирование. Реализуйте добавление и изменение записей регистра непосредственно в этой форме. Таким образом, данные будут существовать в единственном экземпляре, а необходимость в сложном коде синхронизации в событии ПриЗаписи полностью отпадет.

Заключение

Мы рассмотрели основные аспекты работы со связкой "Справочник — Независимый регистр сведений". Подведем итоги:

Соблюдение этих простых правил позволит вам создавать быстрые и надежные решения на платформе 1С:Предприятие.

← На главную