При интеграции 1С с внешними системами (веб-сайтами, мобильными приложениями, сторонними базами данных) мы часто сталкиваемся с задачей обновления существующих документов. Основная сложность здесь заключается в обеспечении производительности: постоянная перезапись объектов ДокументОбъект без реальной необходимости создает колоссальную нагрузку на базу данных, провоцирует лишние блокировки и забивает журнал регистрации. В этой статье мы разберем, как эффективно определить, изменилось ли что-то в данных, и стоит ли выполнять метод Записать().
Рассмотрим ситуацию: из внешней системы по API прилетает пакет данных по заказу. У нас в базе уже есть этот заказ. Если мы просто найдем документ, заполним его реквизиты и нажмем Записать, платформа выполнит полный цикл записи: проверку прав, выполнение кода в модуле объекта (ПередЗаписью, ПриЗаписи), транзакционную запись в таблицы основной базы и итоговое обновление индексов. Если изменений в данных не было, все эти операции были выполнены впустую.
Наша задача — разработать механизм "умного" сравнения, который сопоставит входящие данные и текущее состояние документа в 1С. Проанализируем несколько стратегий реализации этой задачи.
Самый гибкий способ, который позволяет не прописывать каждый реквизит вручную, — это использование коллекции метаданных документа. Проанализируем программный код, который позволяет обойти все реквизиты и табличные части объекта для поиска расхождений.
Предположим, у нас уже есть загруженный Объект (тип ДокументОбъект, еще не записанный) и Ссылка на этот же документ в базе. Нам нужно понять, отличаются ли они. Проанализируем следующий алгоритм:
Функция ДанныеДокументаИзменены(Объект, Ссылка)
// 1. Сравнение основных реквизитов
МетаданныеОбъекта = Объект.Метаданные();
Для каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
Если Объект[Реквизит.Имя] <> Ссылка[Реквизит.Имя] Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
// 2. Сравнение табличных частей
Для каждого ТЧ Из МетаданныеОбъекта.ТабличныеЧасти Цикл
// Сначала проверяем количество строк - это самая быстрая проверка
Если Объект[ТЧ.Имя].Количество() <> Ссылка[ТЧ.Имя].Количество() Тогда
Возврат Истина;
КонецЕсли;
// Если количество совпадает, сравниваем построчно и пореквизитно
Для Индекс = 0 По Объект[ТЧ.Имя].Количество() - 1 Цикл
СтрокаОбъекта = Объект[ТЧ.Имя][Индекс];
СтрокаСсылки = Ссылка[ТЧ.Имя][Индекс];
Для каждого РеквизитТЧ Из ТЧ.Реквизиты Цикл
Если СтрокаОбъекта[РеквизитТЧ.Имя] <> СтрокаСсылки[РеквизитТЧ.Имя] Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЦикла;
Возврат Ложь;
КонецФункции
Этот подход хорош своей универсальностью. Однако стоит помнить, что обращение к Ссылка[ИмяРеквизита] в цикле приведет к множественным обращениям к базе данных, если объект еще не был полностью считан в кэш. Для оптимизации лучше один раз прочитать все данные документа в структуру или таблицу значений.
Если данных много (например, сотни строк в табличных частях), программный перебор может быть медленным. Проанализируем ситуацию, когда выгоднее использовать возможности SQL-движка. Мы можем передать входящие данные в запрос в качестве параметров и сравнить их с текущими данными в базе.
Разберем этапы этого метода:
Таблицу Значений из входящих данных (например, из JSON, полученного по вебу).ЛЕВОЕ СОЕДИНЕНИЕ нашей временной таблицы с реальной таблицей документа (или его ТЧ) по ключевым полям (номер строки, номенклатура и т.д.).ГДЕ проверяем неравенство значимых реквизитов или наличие NULL (что будет означать появление новых или удаление старых строк).Пример логики запроса для сравнения табличной части:
ВЫБРАТЬ
ВТ_ВходящиеДанные.Номенклатура,
ВТ_ВходящиеДанные.Количество
ПОМЕСТИТЬ ВТ_Входящие
ИЗ
&ТЗ_Входящие КАК ВТ_ВходящиеДанные
;
ВЫБРАТЬ ПЕРВЫЕ 1
ИСТИНА
ИЗ
ВТ_Входящие КАК Входящие
ПОЛНОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.Товары КАК РеальныеДанные
ПО Входящие.Номенклатура = РеальныеДанные.Номенклатура
И РеальныеДанные.Ссылка = &Ссылка
ГДЕ
Входящие.Количество <> РеальныеДанные.Количество
ИЛИ Входящие.Номенклатура ЕСТЬ NULL
ИЛИ РеальныеДанные.Номенклатура ЕСТЬ NULL
Если запрос вернул хотя бы одну строку — документ нуждается в обновлении. Это максимально производительный метод для документов с огромными табличными частями.
Рассмотрим современный и очень быстрый способ, который часто применяется в высоконагруженных системах. Суть заключается в том, чтобы не сравнивать каждое поле по отдельности, а хранить в документе "отпечаток" его данных.
Выясним, как это работает по шагам:
КриптографияМенеджер или встроенных средств работы со строками вычисляем MD5 или SHA-хэш от этой строки.ХэшДанныхВнешнейСистемы).Этот метод позволяет даже не считывать ДокументОбъект и его табличные части из базы. Достаточно получить значение одного реквизита хэша через ОбщегоНазначения.ЗначениеРеквизитаОбъекта(), что выполняется мгновенно.
Проанализируем ситуацию, когда нам нужно сравнить объект быстро и без написания сложных циклов. В платформе 1С 8.3 есть мощные средства работы с JSON. Мы можем сериализовать текущий ДокументОбъект в строку JSON и сравнить её с той строкой, которая пришла к нам извне (или также сериализовать входящую структуру).
Нужно учитывать, что порядок реквизитов в JSON должен быть строго идентичен. Рассмотрим пример использования ЗаписьJSON:
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, СтруктураВходящихДанных);
Строка1 = Запись.Закрыть();
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, ДанныеИзБазы); // Структура, прочитанная из документа
Строка2 = Запись.Закрыть();
Если Строка1 <> Строка2 Тогда
// Есть изменения
КонецЕсли;
Важный нюанс: при сравнении через JSON или хэш нужно быть внимательным к типам данных. Например, пустая ссылка 1С и NULL в JSON — это разные вещи. Также даты должны быть приведены к единому формату (ISO).
Подведем итог и выясним, какой метод в каком случае использовать:
ОбщегоНазначения.ДанныеСовпадают(). Она позволяет рекурсивно сравнивать структуры и таблицы значений, что значительно сокращает объем вашего кода.Выбор оптимального способа позволит вам избежать "пустых" записей, сохранит чистоту журнала регистрации и существенно ускорит работу механизмов интеграции.