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