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