При интеграции 1С с внешними системами (например, веб-сайтами) часто возникает задача актуализации данных в регистрах сведений. Если использовать стандартный метод Загрузить() для набора записей, платформа полностью очистит существующие данные по отбору и запишет новые. Это категорически не подходит для периодических регистров, где важно хранить историю изменений, а также для случаев, когда в регистре присутствуют записи, внесенные пользователями вручную.
В данной статье мы разберем, как правильно "скрестить" входящую таблицу значений с данными информационной базы, обеспечив высокую производительность и целостность истории.
Самый надежный способ, работающий на любой версии платформы 8.3, — это использование временных таблиц для вычисления разницы (дельты) между входящими данными и текущим состоянием регистра. Разберем этот процесс по шагам.
Шаг 1. Загрузка данных во временную таблицу
Сначала мы помещаем нашу таблицу значений ДанныеСВебсайта в менеджер временных таблиц. Это позволит нам оперировать данными на стороне СУБД. Важно не забыть проиндексировать поле Сотрудник, так как по нему будет происходить соединение.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_ДанныеСВебсайта ИЗ &ТЗ КАК ТЗ ИНДЕКСИРОВАТЬ ПО Сотрудник";
Запрос.УстановитьПараметр("ТЗ", ДанныеСВебсайта);
Запрос.Выполнить();
Шаг 2. Получение актуальных данных из регистра
Проанализируем текущее состояние регистра сведений АдресаСотрудниковКомпании. Нам нужен срез последних, чтобы понять, изменилось ли что-то по сравнению с тем, что пришло с сайта.
Шаг 3. Сравнение и поиск изменений
Мы выполним запрос, который разделит наши данные на две категории: новые сотрудники (которых нет в регистре) и сотрудники с изменившимися данными. Рассмотрим пример текста такого запроса:
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Сотрудник,
| ТЗ.АдресПроживания,
| ТЗ.ГородПроживания
|ИЗ
| ВТ_ДанныеСВебсайта КАК ТЗ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.АдресаСотрудниковКомпании.СрезПоследних(&ТекущаяДата, ) КАК Срез
| ПО ТЗ.Сотрудник = Срез.Сотрудник
|ГДЕ
| Срез.Сотрудник ЕСТЬ NULL
| ИЛИ ТЗ.АдресПроживания <> Срез.АдресПроживания
| ИЛИ ТЗ.ГородПроживания <> Срез.ГородПроживания";
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
В этом запросе условие Срез.Сотрудник ЕСТЬ NULL отбирает новые записи, а сравнение ресурсов (Адрес, Город) — измененные. Проанализируем ситуацию: если данные идентичны, запись в регистр не производится, что экономит ресурсы сервера и объем транзакционного лога.
После того как мы получили таблицу только с теми данными, которые действительно нужно обновить или добавить, переходим к записи. При объеме в 1000 строк использование МенеджерЗаписи в цикле вполне допустимо и не вызовет критических задержек.
Для Каждого СтрокаДанных Из РезультатЗапроса Цикл
МЗ = РегистрыСведений.АдресаСотрудниковКомпании.СоздатьМенеджерЗаписи();
ЗаполнитьЗначенияСвойств(МЗ, СтрокаДанных);
МЗ.Период = ТекущаяДата(); // Устанавливаем текущий момент времени для истории
МЗ.Записать();
КонецЦикла;
Важно помнить: МенеджерЗаписи работает с конкретной записью, определяемой уникальным ключом (в данном случае Период + Сотрудник). Если на ту же секунду уже есть запись, она будет перезаписана. Если нет — создана новая, что и обеспечивает хранение истории.
В последних версиях платформы 1С реализованы расширенные режимы записи наборов данных. Если ваша конфигурация работает на версии 8.3.25 и выше, вы можете использовать РежимЗамещения.Слияние (или Merge).
Этот режим позволяет отправить в базу целый пакет данных. Платформа сама определит: если запись с таким ключом (Период + Измерения) существует — она обновит ресурсы, если нет — добавит новую. Это работает значительно быстрее, чем цикл с менеджером записи.
НаборЗаписей = РегистрыСведений.АдресаСотрудниковКомпании.СоздатьНаборЗаписей();
НаборЗаписей.РасширенныеРежимыЗамещения = Истина;
Для Каждого Строка Из ДанныеСВебсайта Цикл
НоваяЗапись = НаборЗаписей.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, Строка);
НоваяЗапись.Период = ТекущаяДата();
КонецЦикла;
// Используем режим Слияние для обновления существующих и добавления новых
НаборЗаписей.Записать(РежимЗамещения.Слияние);
Важный нюанс: режим слияния для независимых регистров требует осторожности. Если вы хотите сохранить историю, вы должны передавать в Период новое время (текущий момент). Если вы передадите старый период, вы можете случайно перезатереть историческую запись вместо создания новой.
Если количество сотрудников вырастет с 1000 до 100 000, стоит применить дополнительные техники оптимизации:
СрезПоследних всегда передавайте условие по сотрудникам во второй параметр (отбор). Это заставит СУБД считывать не весь регистр, а только нужные строки.
РегистрСведений.АдресаСотрудниковКомпании.СрезПоследних(&Период, Сотрудник В (ВЫБРАТЬ Сотрудник ИЗ ВТ_ДанныеСВебсайта))
НачатьТранзакцию();
Попытка
// Ваш цикл записи
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписатьЛогСобытий(ОписаниеОшибки());
КонецПопытки;
ЕСТЬNULL. Часто бывает, что на сайте город не заполнен (приходит NULL), а в 1С это пустая строка. Прямое сравнение NULL <> "" всегда даст истину, и вы получите лишние записи в историю. Используйте ВЫБОР КОГДА для нормализации данных перед сравнением.Выясним причину, почему разработчики часто боятся циклов. В 1С обращение к базе внутри цикла считается "плохим тоном", но это правило касается в первую очередь запросов (SELECT). Пакетная запись (INSERT/UPDATE) — это всегда компромисс между скоростью и блокировками таблиц. Для 1000 записей МенеджерЗаписи — это "золотая середина", обеспечивающая простоту кода и корректную работу бизнес-логики (обработчиков событий ПередЗаписью и т.д.).
Рассмотренные методы позволяют эффективно синхронизировать данные, сохраняя при этом всю историю изменений адресов сотрудников, что критически важно для кадрового учета и отчетности.