Как правильно обновить или пропустить записи в регистре сведений 1С при загрузке данных?

Программист 1С v8.3 (Управляемые формы) Сфера услуг, туризм и социальный сектор
← На главную

При разработке механизмов загрузки данных из внешних файлов (Excel, CSV, JSON) программисты часто сталкиваются с проблемой дублирования записей в регистрах сведений. Если в регистре определено множество измерений (например, 12, как в рассматриваемом случае), риск допустить ошибку в ключе записи возрастает. Рассмотрим подробно, как реализовать логику «обновить, если существует, или создать новую», разберем работу с отборами и оптимизируем производительность кода.

Разбор типичных ошибок при работе с набором записей

В представленном в теме коде автор совершает несколько классических ошибок, которые мешают корректному обновлению данных. Проанализируем ситуацию: когда мы используем СоздатьНаборЗаписей и вызываем метод Добавить внутри цикла, платформа пытается записать в базу данных новые строки. Если в базе уже есть запись с таким же набором измерений, возникает ошибка «Запись с такими ключевыми полями существует».

Важный момент: если вы хотите обновить конкретную запись, вы должны сначала «сообщить» платформе, какую именно запись вы ищете. Это делается через установку отборов по всем измерениям регистра.

Проблема соответствия типов в отборах

Рассмотрим вопрос автора о передаче строки в отбор по ссылочному измерению. В 1С Отбор работает строго по типам данных, определенным в метаданных. Если измерение имеет тип СправочникСсылка.КСГ, а вы пытаетесь установить отбор следующим образом:


НаборЗаписей.Отбор.КСГ.Установить("Номер123"); // Передана строка вместо ссылки

Отбор установлен не будет. Платформа не выполнит автоматический поиск ссылки по строковому представлению или коду. В результате при вызове метода Прочитать() набор окажется пустым или отбор будет проигнорирован, что приведет к некорректной записи данных.

Правильный подход: сначала необходимо найти ссылку в справочнике, и только потом передавать её в отбор:


СсылкаКСГ = Справочники.КСГ.НайтиПоРеквизиту("НомерКСГ", СтрокаДанных.КСГ);
Если Не СсылкаКСГ.Пустая() Тогда
    НаборЗаписей.Отбор.КСГ.Установить(СсылкаКСГ);
КонецЕсли;

Реализация логики обновления через Набор Записей

Чтобы обновить существующую запись или добавить новую, если её нет, воспользуемся советом из обсуждения и структурируем алгоритм следующим образом:

  1. Создаем объект НаборЗаписей.
  2. Устанавливаем значения отборов по всем измерениям, которые однозначно идентифицируют запись.
  3. Вызываем метод Прочитать(). Это загрузит из базы данных существующую запись, если она есть.
  4. Проверяем количество записей в наборе.
  5. Если записей 0 — используем Добавить(). Если 1 (или более) — работаем с существующей строкой через Получить(0).
  6. Заполняем ресурсы и реквизиты.
  7. Вызываем Записать().

Рассмотрим пример кода для одной итерации цикла:


Набор = РегистрыСведений.КСГГруппировщик.СоздатьНаборЗаписей();

// Устанавливаем отборы (обязательно по всем измерениям для уникальности)
Набор.Отбор.КСГ.Установить(НайденнаяСсылкаКСГ);
Набор.Отбор.НоменклатураМедицинскойУслуги.Установить(НайденнаяСсылкаНоменклатура);
// ... установка остальных измерений ...

Набор.Прочитать();

Если Набор.Количество() = 0 Тогда
    НоваяЗапись = Набор.Добавить();
    // Заполняем измерения, так как это новая запись
    НоваяЗапись.КСГ = НайденнаяСсылкаКСГ;
    НоваяЗапись.НоменклатураМедицинскойУслуги = НайденнаяСсылкаНоменклатура;
Иначе
    НоваяЗапись = Набор[0]; // Редактируем существующую запись
КонецЕсли;

// Заполняем ресурсы
НоваяЗапись.ВведенВРТ = Истина;
// ... заполнение остальных ресурсов ...

Набор.Записать(); 

Альтернативный вариант: Менеджер Записи

Для работы с единичными записями в регистрах сведений (особенно если их много и нужно обновлять их по одной) удобнее использовать РегистрСведенийМенеджерЗаписи. Это объект, который представляет собой одну строку регистра.

Проанализируем шаги работы с менеджером записи:


МенеджерЗаписи = РегистрыСведений.КСГГруппировщик.СоздатьМенеджерЗаписи();

// Заполняем ключевые поля (измерения)
МенеджерЗаписи.КСГ = НайденнаяСсылкаКСГ;
МенеджерЗаписи.НоменклатураМедицинскойУслуги = НайденнаяСсылкаНоменклатура;
// ... заполняем все остальные 10 измерений ...

МенеджерЗаписи.Прочитать(); // Пытаемся найти запись в БД

// Если запись не найдена, МенеджерЗаписи.Выбран() вернет Ложь, 
// но значения измерений сохранятся, что позволит просто дозаполнить ресурсы и записать.
МенеджерЗаписи.ВведенВРТ = Истина;
МенеджерЗаписи.Пол = Перечисления.ПолПациентов.Мужской;

МенеджерЗаписи.Записать(Истина); // Параметр Истина означает замещение существующей записи

Оптимизация производительности: избегаем запросов в цикле

В исходном сообщении автор использует методы НайтиПоРеквизиту и НайтиПоКоду внутри цикла Для Каждого. Если в файле 10 000 строк, система выполнит 10 000 отдельных запросов к базе данных для каждого справочника. Это крайне медленно.

Выясним, как сделать загрузку быстрее. Профессиональный подход подразумевает предварительную подготовку данных:

  1. Сначала пройдем по всей таблице данных из файла и соберем все уникальные коды/номера в массивы.
  2. Одним запросом выберем все нужные ссылки из справочников (КСГ, МКБ10, Номенклатура) и поместим их во временные таблицы или в объект Соответствие (где ключ — код из файла, значение — ссылка 1С).
  3. В основном цикле будем получать ссылку из Соответствия в оперативной памяти, не обращаясь к серверу БД.

Пример подготовки Соответствия:


МассивКодов = Объект.ДанныеФайла.ВыгрузитьКолонку("КодУслуги");
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК Ссылка,
    |	Номенклатура.КодМинздрава КАК Код
    |ИЗ
    |	Справочник.НоменклатураМедицинскихУслуг КАК Номенклатура
    |ГДЕ
    |	Номенклатура.КодМинздрава В(&МассивКодов)";
Запрос.УстановитьПараметр("МассивКодов", МассивКодов);

ТаблицаСсылок = Запрос.Выполнить().Выгрузить();
СоответствиеУслуг = Новый Соответствие;
Для Каждого Стр Из ТаблицаСсылок Цикл
    СоответствиеУслуг.Вставить(Стр.Код, Стр.Ссылка);
КонецЦикла;

// Теперь в цикле загрузки:
НайденнаяСсылка = СоответствиеУслуг.Получить(СтрокаДанных.КодУслуги);

Работа с большим количеством измерений (12 измерений)

Когда в регистре 12 измерений, уникальность записи определяется комбинацией всех двенадцати значений. Если хотя бы одно значение отличается — это уже другая запись. Важно помнить:

Таким образом, мы рассмотрели основные механизмы обновления записей. Использование МенеджераЗаписи является наиболее простым и понятным способом для поинтерного обновления, в то время как предварительная выборка данных через Запрос значительно ускоряет процесс массовой загрузки.

← На главную