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