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