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

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

В процессе разработки в 1С часто возникает задача отследить изменения в табличной части (ТЧ) документа. Обычное сравнение по номеру строки неэффективно: если пользователь удалит четвертую строку из десяти, все последующие строки сместятся, и стандартные индексы перестанут соответствовать первоначальным данным. Мы разберем, как правильно идентифицировать строки и какие программные методы лучше всего подходят для поиска отличий "до" и "после" записи.

Проблема уникальности строк и использование идентификаторов формы

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

Для решения этой задачи в Управляемых Формах (УФ) предусмотрен механизм идентификаторов. Каждая строка коллекции ДанныеФормыКоллекция имеет свой внутренний идентификатор. Проанализируем, как с ним работать:

  1. При открытии формы или в момент получения данных мы можем запомнить текущее состояние ТЧ.
  2. Для получения уникального ключа строки в контексте формы используется метод ПолучитьИдентификатор().
  3. Этот идентификатор является числом, которое остается неизменным для конкретной строки, пока открыта форма, даже если строки сортируются или удаляются соседние элементы.

Рассмотрим пример кода для обхода строк с получением их идентификаторов:


Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    Идентификатор = СтрокаТЧ.ПолучитьИдентификатор();
    // Сохраняем идентификатор вместе с данными для последующего сравнения
КонецЦикла;

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

Сравнение через запрос методом FULL OUTER JOIN

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

Сначала мы выгружаем текущую табличную часть объекта (которая еще не записана в БД) во временную таблицу. Затем в этом же запросе выбираем данные из таблицы этой же ТЧ, но уже по ссылке на документ.

Рассмотрим логику условий в запросе:

Посмотрим на пример структуры такого запроса:


"ВЫБРАТЬ * ПОМЕСТИТЬ НовыеДанные ИЗ &ТЗ_Новые КАК ТЗ;
 
 ВЫБРАТЬ
    ЕСТЬNULL(Старые.Номенклатура, Новые.Номенклатура) КАК Номенклатура,
    ВЫБОР
        КОГДА Старые.Номенклатура ЕСТЬ NULL ТОГДА ""Добавлена""
        КОГДА Новые.Номенклатура ЕСТЬ NULL ТОГДА ""Удалена""
        ИНАЧЕ ""Изменена""
    КОНЕЦ КАК СтатусИзменения
 ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Старые
 ПОЛНОЕ СОЕДИНЕНИЕ НовыеДанные КАК Новые
 ПО Старые.Номенклатура = Новые.Номенклатура И Старые.НомерСтроки = Новые.НомерСтроки
 ГДЕ Ссылка = &Ссылка"

Метод сериализации в JSON или XML

Выясним причину популярности метода сериализации. Если нам не нужно детально знать, какая именно ячейка изменилась, а достаточно зафиксировать сам факт отличия ТЧ (например, для логирования), можно использовать "снимки" данных.

Рассмотрим последовательность действий:

  1. При начале редактирования документа сериализуем ТЧ в строку (JSON или XML).
  2. Перед записью снова выполняем сериализацию текущего состояния.
  3. Сравниваем две строки.

Использование ЗаписатьJSON в современных версиях платформы 8.3 работает быстрее, чем старые методы ЗначениеВСтрокуВнутр. Это позволяет эффективно хранить состояние ТЧ во временном хранилище или в скрытом реквизите формы для последующего сопоставления.


ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Объект.Товары.Выгрузить());
СтрокаСостояния = ЗаписьJSON.Закрыть();

Использование скрытого реквизита "КлючСтроки"

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

В метаданные табличной части добавляется реквизит КлючСтроки с типом УникальныйИдентификатор или Число. При добавлении новой строки (событие ПриУстановкеНовогоНомера или ПередЗаписью) мы программно генерируем уникальное значение для этого поля и записываем его в базу.

Преимущества этого подхода:

Применение хеширования (MD5/CRC32)

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

Для каждой строки формируется текстовая строка из всех значимых полей (Номенклатура + Количество + Цена + Склад), и от этого текста вычисляется короткий хеш-код. При проверке изменений вы сравниваете только эти коды. Если хеш совпал — строка гарантированно не менялась. Если не совпал — мы анализируем строку подробнее.

Рекомендации по использованию БСП

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

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

← На главную