Как эффективно обновить независимый регистр сведений из таблицы значений?

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

При работе с большими объемами данных в 1С программисты часто сталкиваются с задачей обновления регистра сведений на основании данных из таблицы значений. Стандартный подход с перебором строк и вызовом метода Прочитать() для каждой записи крайне неэффективен, так как порождает огромное количество запросов к базе данных. Разберем, как оптимизировать этот процесс, используя классические методы и новые возможности платформы 1С:Предприятие 8.3.25 и выше.

Проблема поштучного обновления данных

Рассмотрим типичную ситуацию: у нас есть независимый непериодический регистр сведений с измерениями и ресурсами. Нам нужно обновить ресурс В только в том случае, если изменился ресурс Р. Если мы будем использовать СоздатьМенеджерЗаписи внутри цикла и вызывать Прочитать(), система будет каждый раз обращаться к СУБД. Это создает колоссальную нагрузку. Наша задача — минимизировать количество серверных вызовов и максимально использовать мощь SQL-запросов.

Решение 1: Использование запроса и временных таблиц (классический метод)

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


// Предположим, ТЗ_Данные содержит колонки Измерение1, Ресурс1
// Важно: колонки таблицы должны быть типизированы!

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТЗ", ТЗ_Данные);

Запрос.Текст = 
"ВЫБРАТЬ
|	ТЗ.Измерение1 КАК Измерение1,
|	ТЗ.Ресурс1 КАК Ресурс1
|ПОМЕСТИТЬ ВТ_НовыеДанные
|ИЗ
|	&ТЗ КАК ТЗ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ_НовыеДанные.Измерение1,
|	ВТ_НовыеДанные.Ресурс1,
|	Регистр.Ресурс1 КАК СтарыйРесурс1
|ИЗ
|	ВТ_НовыеДанные КАК ВТ_НовыеДанные
|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИмяРегистра КАК Регистр
|		ПО ВТ_НовыеДанные.Измерение1 = Регистр.Измерение1
|ГДЕ
|	Регистр.Измерение1 ЕСТЬ NULL
|	ИЛИ ВТ_НовыеДанные.Ресурс1 <> Регистр.Ресурс1";

Результат = Запрос.Выполнить().Выбрать();

Пока Результат.Следующий() Цикл
    МенеджерЗаписи = РегистрыСведений.ИмяРегистра.СоздатьМенеджерЗаписи();
    ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Результат);
    МенеджерЗаписи.ДатаИзменения = ТекущаяДата();
    МенеджерЗаписи.Записать();
КонецЦикла;

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

Решение 2: Новые режимы записи 8.3.25+ (Слияние и Обновление)

Начиная с версии платформы 8.3.25, в 1С появились революционные изменения для работы с регистрами. Теперь метод Записать() объекта НаборЗаписей поддерживает системное перечисление РежимЗамещения. Это максимально приближает работу 1С к SQL-операциям MERGE или INSERT ... ON DUPLICATE KEY UPDATE.

Рассмотрим два ключевых режима:

  1. Слияние (Merge): Платформа обновляет существующие записи, если ключи совпали, и добавляет новые, если их не было. Это идеальный вариант для синхронизации регистра с таблицей значений.
  2. Обновление (Update): Платформа только обновляет существующие записи. Если в наборе есть новые записи, которых нет в базе, они будут проигнорированы.

Посмотрим на пример использования режима Слияние. Этот способ позволяет избежать цикла с менеджером записи и выполнить операцию массово:


НаборЗаписей = РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей();
// Загружаем данные из подготовленной таблицы значений
НаборЗаписей.Загрузить(ТЗ_Обновления); 

// Выполняем запись в режиме слияния
// Параметр Замещение теперь может принимать РежимЗамещения.Слияние
НаборЗаписей.Записать(РежимЗамещения.Слияние);

Важный нюанс: использование набора записей требует осторожности с памятью. Если в таблице значений сотни тысяч строк, лучше записывать их порциями (пакетами) по 10-50 тысяч строк, чтобы не вызвать переполнение памяти на сервере и не блокировать таблицу на слишком долгое время.

Решение 3: Как использовать «звездочку» в запросах к ТЗ

Часто программисты жалуются, что при попытке написать ВЫБРАТЬ * ПОМЕСТИТЬ ВТ ИЗ &ТЗ возникает ошибка «Тип не может быть выбран в запросе». Выясним причину: это происходит из-за отсутствия явной типизации колонок в таблице значений. Чтобы запрос понимал структуру данных без перечисления всех полей, колонки должны быть созданы с указанием ОписаниеТипов.

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


ТЗ = Новый ТаблицаЗначений;
КвалификаторыСтроки = Новый КвалификаторыСтроки(100);
ТипСтрока = Новый ОписаниеТипов("Строка", , , КвалификаторыСтроки);
ТипЧисло = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));

ТЗ.Колонки.Добавить("Ключ", ТипСтрока);
ТЗ.Колонки.Добавить("Значение", ТипЧисло);

// Теперь в запросе можно использовать сокращенный синтаксис
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ";

Если вы получаете таблицу из управляемой формы (типа ДанныеФормыКоллекция), обязательно преобразуйте ее в «настоящую» таблицу значений методом Выгрузить(), иначе запрос не сможет с ней работать.

Синхронизация: удаление отсутствующих записей

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

  1. Помещаем ТЗ во временную таблицу.
  2. Выбираем записи из РегистрСведений.ИмяРегистра.
  3. Присоединяем ВТ_ТЗ по измерениям.
  4. В условии ГДЕ указываем ВТ_ТЗ.Измерение ЕСТЬ NULL.

Таким образом, мы получим список «лишних» записей, которые можно массово удалить или модифицировать. Для удаления в версии 8.3.25 также можно использовать РежимЗамещения.Удаление, передав в набор записей только ключи (измерения), которые нужно стереть из базы.

Подведем итоги

Проанализировав доступный инструментарий, можно сделать следующие выводы:

Для старых версий платформы (ниже 8.3.25) оптимальным остается предварительный отбор данных через запрос к временной таблице ТЗ с последующим циклом по МенеджеруЗаписи (без вызова Прочитать, так как все нужные данные мы уже получили в запросе).

Для современных систем (8.3.25+) лучшим выбором будет использование НабораЗаписей с режимом Слияние. Это позволяет переложить логику сопоставления данных на СУБД, что в разы ускоряет выполнение задачи и упрощает код. Всегда помните о типизации колонок ТаблицыЗначений — это залог стабильной работы запросов и производительности системы.

← На главную