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