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