Программная работа с регистрами сведений — одна из базовых и наиболее часто встречающихся задач при разработке на платформе 1С. Однако начинающие разработчики часто сталкиваются с ситуацией, когда данные либо не записываются вовсе, либо «затирают» уже существующие записи в регистре. В этой статье мы подробно разберем, как избежать подобных ошибок на примере загрузки данных из файла Excel в регистр сведений Прайс.
Рассмотрим ситуацию, с которой столкнулся автор темы. При использовании объекта РегистрСведенийНаборЗаписей и вызове метода Записать() без предварительной установки отборов, система по умолчанию пытается записать «пустоту» или только те строки, которые вы добавили в набор в текущем сеансе. Если в наборе нет отбора по измерениям, 1С воспринимает это как команду: «Очистить весь регистр и записать туда только то, что я подготовил сейчас».
Важный момент: Если ваш регистр содержит тысячи записей, а вы создаете набор, добавляете в него одну строку и вызываете Записать(), в итоге в регистре останется всего одна эта строка. Все остальные данные будут удалены. Именно это и произошло у автора в первом сообщении.
Если нам необходимо записать данные по одной конкретной номенклатуре (как в случае с построчным чтением Excel), проще и безопаснее использовать МенеджерЗаписи. Этот объект предназначен для работы с одной конкретной записью регистра, определяемой набором значений его измерений.
Проанализируем исправленный код, который позволяет корректно добавить или обновить запись:
Если Выборка.Ссылка = НайденнаяСсылка Тогда
Сообщить("Запись номенклатуры: " + Выборка.Ссылка);
// Создаем объект менеджера записи для конкретного регистра
МенеджерЗаписи = РегистрыСведений.Прайс.СоздатьМенеджерЗаписи();
// Заполняем измерения регистра (это ключевые поля, по которым ищется запись)
МенеджерЗаписи.Номенклатура = Выборка.Ссылка;
// Если регистр периодический, обязательно указываем период
// МенеджерЗаписи.Период = ТекущаяДата();
// Заполняем ресурсы (данные, которые мы храним)
// МенеджерЗаписи.Цена = ЦенаИзExcel;
// Метод Записать() проверит: если запись с такой номенклатурой есть — обновит, если нет — создаст новую
МенеджерЗаписи.Записать();
КонецЕсли;
Этот метод хорош тем, что нам не нужно вручную управлять отборами. Мы просто заполняем измерения, и система сама понимает, какую именно ячейку данных нужно изменить.
Если вы все же предпочитаете работать через НаборЗаписей (что более правильно с точки зрения архитектуры при массовых изменениях), необходимо обязательно устанавливать отбор по измерениям. Рассмотрим, как это сделать правильно, чтобы не удалить лишние данные:
НаборПрайс = РегистрыСведений.Прайс.СоздатьНаборЗаписей();
// Устанавливаем отбор, чтобы работа велась только по конкретной номенклатуре
НаборПрайс.Отбор.Номенклатура.Установить(НайденнаяСсылка);
// Теперь можно прочитать существующую запись, если нужно
НаборПрайс.Прочитать();
Если НаборПрайс.Количество() = 0 Тогда
// Если записи нет, создаем новую
НоваяЗапись = НаборПрайс.Добавить();
НоваяЗапись.Номенклатура = НайденнаяСсылка;
Иначе
// Если запись уже есть, берем первую (единственную по этому отбору)
НоваяЗапись = НаборПрайс[0];
КонецЕсли;
// Заполняем данные
// НоваяЗапись.Цена = НоваяЦена;
// Записываем набор. Благодаря отбору, удалятся/перезапишутся только записи по этой номенклатуре
НаборПрайс.Записать();
В обсуждении на форуме автор упоминает, что процесс записи занимает очень много времени. Давайте выясним причину и разберем, как ускорить код в десятки раз.
1. Убираем запросы из цикла. В исходном коде автора внутри цикла Пока по строкам Excel выполняется Новый Запрос. Это критическая ошибка производительности. На каждой итерации программа обращается к базе данных, компилирует запрос и исполняет его.
Как правильно: Сначала загрузите все данные из Excel в Таблицу Значений, а затем одним запросом получите все необходимые ссылки на номенклатуру или существующие цены.
2. Использование транзакций. При записи каждой строки через МенеджерЗаписи.Записать() или Набор.Записать(), 1С инициирует транзакцию в СУБД. Это требует времени на запись логов и подтверждение операции дисковой подсистемой.
Рассмотрим, как ускорить процесс, обернув весь цикл загрузки в одну транзакцию:
НачатьТранзакцию();
Попытка
// Цикл по строкам файла
Пока СокрЛП(НоменклатураНаименование) <> "" Цикл
// ... логика поиска и заполнения МенеджераЗаписи ...
МенеджерЗаписи.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Сообщить("Произошла ошибка при загрузке: " + ОписаниеОшибки());
КонецПопытки;
3. Работа с COM-объектом Excel. Чтение каждой ячейки Cells(Row, Col).Value напрямую из 1С происходит медленно из-за накладных расходов на COM-взаимодействие.
Совет: Гораздо быстрее прочитать весь лист Excel в массив одним махом (через UsedRange.Value) и дальше работать уже с массивом в памяти 1С.
Прайс является периодическим (например, цена меняется каждый день), то измерение Период является ключевым. Если вы его не укажете при записи, 1С запишет данные на дату 01.01.0001. В результате в актуальных отчетах и формах вы эти данные просто не увидите.Double. Если вы ищете номенклатуру по коду, убедитесь, что тип кода в 1С (строка или число) совпадает с тем, что пришло из Excel. Пользуйтесь функциями СокрЛП() и Число() для приведения типов.НайтиПоНаименованию() вернет пустую ссылку. Старайтесь использовать Артикул или Штрихкод.Подводя итог, можно сказать: для разовых правок или небольших загрузок МенеджерЗаписи — ваш лучший друг. Для массовой обработки данных всегда используйте НаборЗаписей с четко установленными отборами и не забывайте про использование транзакций для ускорения работы системы.