Как определить новые, измененные и удаляемые записи в регистре сведений через запрос?

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

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

Архитектурная схема: правильное сопоставление данных

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

Корректный подход разделяет поля на две группы:

  1. Ключевые поля (измерения регистра, а также период для периодических регистров): по ним выполняется полное соединение таблиц. Это позволяет однозначно связать строку источника со строкой приемника.
  2. Информационные поля (ресурсы и реквизиты): анализируются в секции выборки через условные операторы для выявления факта изменения данных.

Запрос для определения дельты изменений

Рассмотрим практический пример. Предположим, у нас есть независимый непериодический регистр сведений ЦеныНоменклатурыСрез с измерением Номенклатура и ресурсом Цена. Во входящей таблице значений содержатся актуальные данные, полученные из внешнего источника.

Посмотрим на текст запроса, который классифицирует строки по типам операций: ВСТАВКА, УДАЛЕНИЕ, ОБНОВЛЕНИЕ или отсутствие изменений:


ВЫБРАТЬ
    НовыеДанные.Номенклатура КАК Номенклатура,
    НовыеДанные.Цена КАК Цена
ПОМЕСТИТЬ ВТ_НовыеДанные
ИЗ
    &ТаблицаНовыхДанных КАК НовыеДанные
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЕСТЬNULL(ВТ.Номенклатура, ТекущийРС.Номенклатура) КАК Номенклатура,
    ВЫБОР
        КОГДА ТекущийРС.Номенклатура ЕСТЬ NULL ТОГДА
            "ВСТАВИТЬ"
        КОГДА ВТ.Номенклатура ЕСТЬ NULL ТОГДА
            "УДАЛИТЬ"
        КОГДА ЕСТЬNULL(ВТ.Цена, 0) <> ЕСТЬNULL(ТекущийРС.Цена, 0) ТОГДА
            "ОБНОВИТЬ"
        ИНАЧЕ
            "НЕ_ИЗМЕНЕНО"
    КОНЕЦ КАК Действие,
    ВТ.Цена КАК НоваяЦена,
    ТекущийРС.Цена КАК СтараяЦена
ИЗ
    ВТ_НовыеДанные КАК ВТ
        ПОЛНОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатурыСрез КАК ТекущийРС
        ПО ВТ.Номенклатура = ТекущийРС.Номенклатура
ГДЕ
    // Отсекаем записи, где значения полностью идентичны
    ТекущийРС.Номенклатура ЕСТЬ NULL
    ИЛИ ВТ.Номенклатура ЕСТЬ NULL
    ИЛИ ЕСТЬNULL(ВТ.Цена, 0) <> ЕСТЬNULL(ТекущийРС.Цена, 0)

Проанализируем работу этого запроса:

  1. Мы передаем подготовленную таблицу значений в виде параметра &ТаблицаНовыхДанных и помещаем её во временную таблицу.
  2. Используем ПОЛНОЕ СОЕДИНЕНИЕ строго по ключевому полю Номенклатура.
  3. Если поле ТекущийРС.Номенклатура ЕСТЬ NULL, значит, такой комбинации измерений в базе еще нет — требуется добавление.
  4. Если ВТ.Номенклатура ЕСТЬ NULL, значит, в источнике запись отсутствует, а в базе она есть — требуется удаление.
  5. Если ключевые поля присутствуют с обеих сторон, мы сравниваем ресурсы. При несовпадении фиксируем необходимость обновления, а при совпадении — отсекаем строку в условии ГДЕ.

Важное замечание: Обратите внимание на использование функции ЕСТЬNULL при сравнении ресурсов. В языке запросов 1С прямое сравнение значения с NULL через операторы = или <> возвращает ЛОЖЬ, из-за чего изменения при наличии пустых или незаполненных значений могут быть пропущены.

Ограничение режима «Слияние» наборов записей

В платформе 1С для объекта РегистрСведенийНаборЗаписей предусмотрено свойство РежимЗамещения со значением РежимЗамещения.Слияние. Выясним, почему оно не решает задачу целиком.

При использовании слияния существующие записи перезаписываются при совпадении ключа, а отсутствующие — дописываются. Однако данный режим в принципе не умеет удалять записи, которых больше нет в наборе. Если внешний источник перестал передавать часть строк, они навсегда останутся в регистре. Поэтому полноценный анализ дельты через запрос с полным соединением остается единственным надежным способом синхронизации при необходимости физического удаления устаревших строк.

Блокировки и параллельная работа

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

Посмотрим, как организовать блокировку в коде 1С:


НачатьТранзакцию();

Попытка
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЦеныНоменклатурыСрез");
    ЭлементБлокировки.ИсточникДанных = ТаблицаНовыхДанных;
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    Блокировка.Заблокировать();
    
    // Выполнение запроса анализа изменений
    // Запись только измененных наборов записей...
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

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

Когда анализ дельты не имеет смысла?

Проанализируем ситуации, когда поштучное вычисление изменений создает избыточную нагрузку:

  1. Большой объем изменений: если по результатам анализа обновляется более 20–30% строк набора, затраты сервера СУБД на вычисление полного соединения и точечную перезапись превысят время обычной очистки и полной перезаписи набора с отбором по ведущему измерению (с установленным свойством Замещение = Истина).
  2. Огромные массивы данных: передача таблицы на сотни тысяч строк через параметр запроса нагружает временные таблицы (в том числе системную базу tempdb СУБД). В таких сценариях данные следует дробить на пачки или организовывать сравнение порциями по группам измерений.
  3. Подчиненные регистратору регистры: для регистров с признаком подчинения регистратору точечная правка строк без перезаписи всего документа невозможна. Управление набором осуществляется исключительно в разрезе регистратора.
← На главную