Как перенести данные из строковой колонки в ссылочную колонку в регистре накопления 1С?

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

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

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

Первое, что следует уяснить, это невозможность прямого копирования строкового значения в ссылочный тип. Платформа 1С строго типизирована. Если мы попытаемся присвоить строковое значение переменной или реквизиту ссылочного типа, система не выдаст ошибку выполнения, но в ссылочной колонке останется пустое значение, так как строка не является валидной ссылкой на объект базы данных.

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

Основной подход: Сопоставление строк и получение ссылок

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

Шаг 1: Формирование соответствия строковых значений и ссылок

Один из наиболее эффективных способов — это предварительно собрать все необходимые ссылки в структуру типа Соответствие или ТаблицаЗначений. Это позволяет минимизировать количество обращений к базе данных при обработке каждой записи регистра и значительно повышает производительность при работе с большими объемами данных.

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


// Создаем объект Соответствие для хранения маппинга
СоответствиеСтрокаСсылка = Новый Соответствие;

// Формируем запрос для получения всех ссылок и их наименований из целевого справочника
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
                  |    Справочник1.Ссылка КАК Ссылка,
                  |    Справочник1.Наименование КАК Наименование
                  |ИЗ
                  |    Справочник.Справочник1 КАК Справочник1";

// Выполняем запрос и обходим результат
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    // Заполняем соответствие: ключ - наименование (строка), значение - ссылка
    // Учитываем, что Наименование может быть неуникальным. В этом случае, будет записана последняя найденная ссылка.
    СоответствиеСтрокаСсылка.Вставить(Выборка.Наименование, Выборка.Ссылка);
КонецЦикла;

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

  1. Поиск по Наименованию: Как показано выше. Обратите внимание, что наименование может быть неуникальным. В случае дублирования система выберет первое найденное или последнее записанное в соответствие, что может привести к выбору неверной ссылки. Этот метод подходит, если вы уверены в уникальности наименований или готовы принять возможные неточности.
  2. Поиск по Коду: Если строки содержат код элемента справочника, то запрос будет использовать поле Код. Это более надежно, если коды уникальны в справочнике.
    
    // ... (начало кода аналогично)
    Запрос.Текст = "ВЫБРАТЬ
                      |    Справочник1.Ссылка КАК Ссылка,
                      |    Справочник1.Код КАК Код
                      |ИЗ
                      |    Справочник.Справочник1 КАК Справочник1";
    // ... (выполнение запроса)
    Пока Выборка.Следующий() Цикл
        СоответствиеСтрокаСсылка.Вставить(Выборка.Код, Выборка.Ссылка);
    КонецЦикла;
    
  3. Поиск по GUID (УникальномуИдентификатору): Это самый надежный способ получить ссылку, если ваша строка содержит строковое представление GUID. Сначала необходимо преобразовать строковое значение в объект УникальныйИдентификатор, а затем получить ссылку через менеджер соответствующего типа.
    
    // ... внутри цикла обхода строковых значений, если соответствие не формировалось заранее по GUID ...
    СтрокаGUID = Запись.Измерение1; // Строка из регистра, содержащая GUID
    Попытка
        УИД = Новый УникальныйИдентификатор(СтрокаGUID);
        Ссылка = Справочники.Справочник1.ПолучитьСсылку(УИД); // Менеджер справочника
        // Добавить в соответствие или использовать напрямую для текущей записи
    Исключение
        // Обработка ошибки, если строка не является корректным GUID
        Ссылка = Неопределено;
        Сообщить("Некорректный GUID: " + СтрокаGUID);
    КонецПопытки;
    
    Такой подход обеспечивает уникальность и однозначность получаемой ссылки.

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

Обновление записей регистра накопления

Регистры накопления в 1С обычно заполняются и корректируются документами-регистраторами при их проведении. Однако в некоторых ситуациях, например, при массовом исправлении данных, перепроведение всех документов может быть нецелесообразным или невозможным из-за длительности процесса, большого количества документов, или отсутствия необходимой логики проведения в старых документах. В таких случаях мы можем напрямую работать с наборами записей регистра.

Шаг 2: Обход регистраторов и изменение наборов записей

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


// Получаем список всех уникальных регистраторов, которые создавали записи в регистре
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
                  |    РегистрНакопления1.Регистратор КАК Регистратор
                  |ИЗ
                  |    РегистрНакопления.РегистрНакопления1 КАК РегистрНакопления1
                  |ГДЕ
                  |    НЕ РегистрНакопления1.Измерение1 = &ПустаяСтрока // Оптимизация: работаем только с записями, где есть что менять";
Запрос.УстановитьПараметр("ПустаяСтрока", ""); // Параметр для фильтрации пустых строк

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

// Обходим каждый уникальный регистратор
Пока ВыборкаРегистраторов.Следующий() Цикл
    // Создаем новый набор записей для текущего регистратора
    НаборЗаписей = РегистрыНакопления.РегистрНакопления1.СоздатьНаборЗаписей();
    
    // Устанавливаем отбор по текущему регистратору
    НаборЗаписей.Отбор.Регистратор.Установить(ВыборкаРегистраторов.Регистратор);
    
    // Считываем записи регистратора
    Попытка
        НаборЗаписей.Прочитать();
    Исключение
        // Могут быть ситуации, когда регистратор уже удален или недоступен.
        // Мы игнорируем такие наборы записей.
        Сообщить("Не удалось прочитать набор записей для регистратора " + ВыборкаРегистраторов.Регистратор + ". Возможно, объект удален.");
        Продолжить;
    КонецПопытки;
    
    // Переменная для отслеживания изменений в наборе записей
    БылиИзменения = Ложь;
    
    // Обходим каждую запись в полученном наборе
    Для Каждого ЗаписьРегистра Из НаборЗаписей Цикл
        // Получаем строковое значение из исходной колонки (допустим, Измерение1)
        СтроковоеЗначение = ЗаписьРегистра.Измерение1;
        
        // Ищем соответствующую ссылку в нашем предварительно сформированном соответствии
        ПолученнаяСсылка = СоответствиеСтрокаСсылка.Получить(СтроковоеЗначение);
        
        // Проверяем, что ссылка действительно найдена и заполнена
        Если ЗначениеЗаполнено(ПолученнаяСсылка) Тогда
            // Если текущее значение ссылочной колонки отличается от найденной ссылки, обновляем его
            Если НЕ ЗаписьРегистра.Измерение2 = ПолученнаяСсылка Тогда
                ЗаписьРегистра.Измерение2 = ПолученнаяСсылка; // Измерение2 - целевая ссылочная колонка
                БылиИзменения = Истина;
            КонецЕсли;
        Иначе
            // Обработка случая, когда ссылка не найдена для данного строкового значения
            // Мы можем оставить ссылку пустой, записать значение по умолчанию или пропустить
            // Если там была какая-то ссылка, а теперь для этого строкового значения не нашлось, очищаем ее
            Если ЗначениеЗаполнено(ЗаписьРегистра.Измерение2) Тогда
                ЗаписьРегистра.Измерение2 = Неопределено; // Или Справочники.Справочник1.ПустаяСсылка()
                БылиИзменения = Истина;
                Сообщить("Для строкового значения '" + СтроковоеЗначение + "' в регистре накопления для регистратора " + ВыборкаРегистраторов.Регистратор + " не найдена соответствующая ссылка. Ссылка очищена.");
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
    // Если были изменения в наборе записей, записываем его
    Если БылиИзменения Тогда
        // Установка ОбменДанными.Загрузка = Истина отключает проверки прав доступа
        // и не вызывает обработчики событий при проведении документов.
        // Это полезно для технического обновления данных и повышения производительности.
        НаборЗаписей.ОбменДанными.Загрузка = Истина;
        
        // Записываем измененный набор записей
        Попытка
            НаборЗаписей.Записать();
        Исключение Как Искл
            Сообщить("Ошибка записи набора записей для регистратора " + ВыборкаРегистраторов.Регистратор + ": " + Искл.Описание);
            // Можно добавить логику для повторной попытки или логирования ошибки
        КонецПопытки;
    КонецЕсли;
КонецЦикла;

Важный момент: Установка свойства НаборЗаписей.ОбменДанными.Загрузка = Истина является критически важной. Она позволяет записывать данные в регистр, обходя стандартные механизмы проведения документов, проверки прав и вызова обработчиков событий. Это значительно повышает производительность при массовом обновлении, но требует осторожности и понимания последствий, так как стандартная логика бизнес-процессов, привязанная к проведению документов, может быть нарушена. Используйте эту опцию только при техническом обновлении данных.

Оптимизация и контроль

При работе с большими объемами данных в реальных системах важно учитывать производительность, надежность и целостность данных:

  1. Транзакции: Мы настоятельно рекомендуем оборачивать весь процесс изменения данных в транзакцию (с использованием функций НачатьТранзакцию(), ЗафиксироватьТранзакцию(), ОтменитьТранзакцию()). Это гарантирует атомарность операции: либо все изменения будут применены успешно, либо в случае возникновения ошибки вся операция будет отменена, что предотвратит повреждение данных.
  2. Блокировки: Массовое обновление регистров может вызывать блокировки таблиц базы данных, что негативно скажется на работе других пользователей. Постарайтесь минимизировать время блокировок, разделяя операции на более мелкие части или выполняя их в нерабочее время.
  3. Расширенные режимы замещения: Для некоторых специфических сценариев записи набора записей, особенно при частичной перезаписи движений, можно использовать свойство НаборЗаписей.РасширенныеРежимыЗамещения и перечисление РежимЗамещения, что может обеспечить более эффективное обновление данных в таблицах регистра.
  4. Логирование: Обязательно ведите подробный лог всех выполняемых операций: какие записи были изменены, какие ссылки не найдены, какие ошибки возникли. Это значительно упростит отладку и проверку результатов после выполнения обработки.

Альтернативные подходы

Хотя прямое обновление наборов записей часто является быстрым и эффективным решением для коррекции данных, в идеале, если есть возможность, мы рекомендуем рассмотреть следующие подходы, обеспечивающие большую консистентность:

  1. Изменение процедур проведения документов: Корректировка кода в модулях документов-регистраторов, чтобы они изначально формировали правильные движения с ссылочными типами. После внесения изменений можно выполнить перепроведение всех документов, что обеспечит максимальную консистентность данных и соблюдение всех бизнес-правил, заложенных в логике проведения.
  2. "Конвертация данных": Для более сложных сценариев, миграции данных между базами или масштабного обмена данными, конфигурация "Конвертация данных 2.0/3.0" предоставляет мощный инструментарий для гибкой настройки правил конвертации и сопоставления объектов. Она позволяет создавать сложные правила по поиску, созданию и сопоставлению ссылок на основе строковых значений и других критериев.

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

← На главную