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