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

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

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

Анализ проблемы: почему нельзя просто записывать всё подряд?

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

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

Способ 1: Универсальное сравнение через перебор метаданных

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

Предположим, у нас уже есть загруженный Объект (тип ДокументОбъект, еще не записанный) и Ссылка на этот же документ в базе. Нам нужно понять, отличаются ли они. Проанализируем следующий алгоритм:


Функция ДанныеДокументаИзменены(Объект, Ссылка)
    
    // 1. Сравнение основных реквизитов
    МетаданныеОбъекта = Объект.Метаданные();
    Для каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
        Если Объект[Реквизит.Имя] <> Ссылка[Реквизит.Имя] Тогда
            Возврат Истина;
        КонецЕсли;
    КонецЦикла;

    // 2. Сравнение табличных частей
    Для каждого ТЧ Из МетаданныеОбъекта.ТабличныеЧасти Цикл
        // Сначала проверяем количество строк - это самая быстрая проверка
        Если Объект[ТЧ.Имя].Количество() <> Ссылка[ТЧ.Имя].Количество() Тогда
            Возврат Истина;
        КонецЕсли;

        // Если количество совпадает, сравниваем построчно и пореквизитно
        Для Индекс = 0 По Объект[ТЧ.Имя].Количество() - 1 Цикл
            СтрокаОбъекта = Объект[ТЧ.Имя][Индекс];
            СтрокаСсылки = Ссылка[ТЧ.Имя][Индекс];
            
            Для каждого РеквизитТЧ Из ТЧ.Реквизиты Цикл
                Если СтрокаОбъекта[РеквизитТЧ.Имя] <> СтрокаСсылки[РеквизитТЧ.Имя] Тогда
                    Возврат Истина;
                КонецЕсли;
            КонецЦикла;
        КонецЦикла;
    КонецЦикла;

    Возврат Ложь;
КонецФункции

Этот подход хорош своей универсальностью. Однако стоит помнить, что обращение к Ссылка[ИмяРеквизита] в цикле приведет к множественным обращениям к базе данных, если объект еще не был полностью считан в кэш. Для оптимизации лучше один раз прочитать все данные документа в структуру или таблицу значений.

Способ 2: Сравнение через временные таблицы в запросе

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

Разберем этапы этого метода:

  1. Формируем Таблицу Значений из входящих данных (например, из JSON, полученного по вебу).
  2. Передаем эту ТЗ в запрос как параметр.
  3. В запросе помещаем ТЗ во временную таблицу.
  4. Выполняем ЛЕВОЕ СОЕДИНЕНИЕ нашей временной таблицы с реальной таблицей документа (или его ТЧ) по ключевым полям (номер строки, номенклатура и т.д.).
  5. В секции ГДЕ проверяем неравенство значимых реквизитов или наличие NULL (что будет означать появление новых или удаление старых строк).

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


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

ВЫБРАТЬ ПЕРВЫЕ 1
    ИСТИНА
ИЗ
    ВТ_Входящие КАК Входящие
    ПОЛНОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.Товары КАК РеальныеДанные
        ПО Входящие.Номенклатура = РеальныеДанные.Номенклатура
        И РеальныеДанные.Ссылка = &Ссылка
ГДЕ
    Входящие.Количество <> РеальныеДанные.Количество
    ИЛИ Входящие.Номенклатура ЕСТЬ NULL
    ИЛИ РеальныеДанные.Номенклатура ЕСТЬ NULL

Если запрос вернул хотя бы одну строку — документ нуждается в обновлении. Это максимально производительный метод для документов с огромными табличными частями.

Способ 3: Механизм хэширования (Контрольные суммы)

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

Выясним, как это работает по шагам:

  1. При создании или изменении документа мы собираем все значимые данные в одну длинную строку (или структуру).
  2. С помощью функции КриптографияМенеджер или встроенных средств работы со строками вычисляем MD5 или SHA-хэш от этой строки.
  3. Сохраняем полученный хэш в дополнительный реквизит документа (например, ХэшДанныхВнешнейСистемы).
  4. При следующем получении данных извне мы сначала вычисляем хэш входящего пакета и просто сравниваем две строки. Если хэши равны — данные идентичны.

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

Способ 4: Сериализация в JSON

Проанализируем ситуацию, когда нам нужно сравнить объект быстро и без написания сложных циклов. В платформе 1С 8.3 есть мощные средства работы с JSON. Мы можем сериализовать текущий ДокументОбъект в строку JSON и сравнить её с той строкой, которая пришла к нам извне (или также сериализовать входящую структуру).

Нужно учитывать, что порядок реквизитов в JSON должен быть строго идентичен. Рассмотрим пример использования ЗаписьJSON:


Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, СтруктураВходящихДанных);
Строка1 = Запись.Закрыть();

Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, ДанныеИзБазы); // Структура, прочитанная из документа
Строка2 = Запись.Закрыть();

Если Строка1 <> Строка2 Тогда
    // Есть изменения
КонецЕсли;

Важный нюанс: при сравнении через JSON или хэш нужно быть внимательным к типам данных. Например, пустая ссылка 1С и NULL в JSON — это разные вещи. Также даты должны быть приведены к единому формату (ISO).

Рекомендации по выбору метода

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

Выбор оптимального способа позволит вам избежать "пустых" записей, сохранит чистоту журнала регистрации и существенно ускорит работу механизмов интеграции.

← На главную