В практике разработчика 1С часто возникает задача сравнения двух состояний объекта или двух разных записей в регистре. Ситуация усложняется, когда объект (например, справочник) содержит десятки реквизитов. В нашем случае мы имеем дело со справочником Данные, у которого 33 реквизита, и нам необходимо найти расхождения между двумя записями независимого регистра сведений, отобранными по определенному измерению. Писать 33 условия вручную в консоли запросов — задача трудоемкая и подверженная ошибкам. Разберем наиболее эффективные способы решения этой задачи, от использования динамического кода до механизмов платформы.
Если мы работаем в рамках внешней обработки или программного модуля, наиболее правильным подходом будет использование метаданных. Вместо того чтобы вручную перечислять Реквизит1, Реквизит2 и так далее, мы заставим систему саму сформировать текст запроса. Проанализируем, как это можно реализовать.
Для начала нам необходимо получить список всех реквизитов справочника Данные. Мы можем обойти коллекцию метаданных в цикле и собрать строку условий для секции ГДЕ или сформировать колонки для выбора. Рассмотрим пример кода, который генерирует текст условия для сравнения:
ТекстУсловия = "";
МетаданныеСправочника = Метаданные.Справочники.Данные;
Для Каждого Реквизит Из МетаданныеСправочника.Реквизиты Цикл
ИмяПоля = Реквизит.Имя;
// Формируем сравнение полей двух таблиц Т1 и Т2
Если ТекстУсловия <> "" Тогда
ТекстУсловия = ТекстУсловия + " ИЛИ ";
КонецЕсли;
ТекстУсловия = ТекстУсловия + "ЕСТЬNULL(Т1.Данные." + ИмяПоля + ", 0) <> ЕСТЬNULL(Т2.Данные." + ИмяПоля + ", 0)";
КонецЦикла;
ТекстЗапроса = "ВЫБРАТЬ * ИЗ РегистрСведений.МойРегистр КАК Т1
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.МойРегистр КАК Т2
| ПО Т1.Измерение = &Значение1 И Т2.Измерение = &Значение2
| ГДЕ " + ТекстУсловия;
Обратим внимание на использование функции ЕСТЬNULL. Это критически важный момент: если один из реквизитов не заполнен (имеет значение NULL или пустое значение), обычное сравнение через оператор <> может вернуть неопределенный результат в SQL. Мы должны нормализовать данные перед сравнением.
Иногда задача стоит шире: нужно просто понять, отличаются ли объекты, не вникая в детали конкретного поля в самом запросе. Если количество записей не измеряется сотнями тысяч, мы можем применить "объектный путь". Выясним, как это работает через сериализацию в JSON или XML.
Суть метода заключается в том, что мы считываем ссылки из регистра, получаем объекты справочников и преобразуем их в текстовую строку. Если строки идентичны, значит, все 33 реквизита совпадают. Посмотрим на алгоритм реализации:
ПолучитьОбъект().
// Пример упрощенной сериализации для сравнения
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Объект1);
СтрокаОбъекта1 = ЗаписьJSON.Закрыть();
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Объект2);
СтрокаОбъекта2 = ЗаписьJSON.Закрыть();
Если СтрокаОбъекта1 <> СтрокаОбъекта2 Тогда
Сообщить("Обнаружены расхождения в реквизитах справочника!");
КонецЕсли;
Преимущество: нам абсолютно не важно, 33 реквизита в объекте или 133. Метод универсален. Недостаток: низкая скорость на больших массивах данных и невозможность использования непосредственно в консоли запросов без написания кода.
Рассмотрим ситуацию, когда нам нужно быстро найти расхождения среди множества записей, используя программный код, но без сложной логики соединений. Мы можем выгрузить результат запроса в ТаблицаЗначений.
Проанализируем этот "ленивый", но эффективный способ:
Данные.Наименование, Данные.Код и т.д.).Количество и заполняем её единицей.ТЗ.Свернуть("СписокВсех33Реквизитов", "Количество").После сворачивания строки, в которых все значения реквизитов совпали, объединятся, а значение в колонке Количество станет больше 1. Те строки, где Количество = 1, являются уникальными (имеют расхождения). Это позволяет быстро визуализировать проблемные данные.
Если работа ведется исключительно в консоли запросов и использование кода запрещено, разберем стратегию с временными таблицами. При обращении к 33 реквизитам через точку (разыменование ссылки) СУБД выполняет огромное количество неявных левых соединений (LEFT JOIN). Это может привести к падению производительности.
Для оптимизации проделаем следующие шаги:
Данные во временную таблицу, выбрав только те элементы, которые встречаются в нашем регистре.Пример структуры такого запроса:
// Шаг 1: Подготовка данных справочника
ВЫБРАТЬ Спр.Ссылка, Спр.Реквизит1, Спр.Реквизит2...
ПОМЕСТИТЬ ВТ_РеквизитыДанных
ИЗ Справочник.Данные КАК Спр;
// Шаг 2: Сравнение
ВЫБРАТЬ *
ИЗ РегистрСведений.МойРегистр КАК Рег1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.МойРегистр КАК Рег2
ПО Рег1.Измерение = &Условие1 И Рег2.Измерение = &Условие2
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РеквизитыДанных КАК Данные1 ПО Рег1.Данные = Данные1.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РеквизитыДанных КАК Данные2 ПО Рег2.Данные = Данные2.Ссылка
ГДЕ Данные1.Реквизит1 <> Данные2.Реквизит1 ИЛИ Данные1.Реквизит2 <> Данные2.Реквизит2 ...
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), стоит проверить наличие типовых функций в общих модулях. Например, модуль ОбщегоНазначения часто содержит функции для сравнения структур или получения данных объектов одной выборкой.
Функции типа ДанныеСовпадают или использование подсистемы "Версионирование объектов" могут существенно упростить задачу. В подсистеме версионирования уже реализованы механизмы сравнения двух версий одного объекта, которые можно адаптировать под сравнение двух разных записей регистра. Мы рекомендуем проанализировать состав метаданных вашей базы на предмет наличия этих инструментов перед тем, как приступать к разработке велосипеда.
Выбор метода зависит от контекста:
Помните, что при сравнении большого количества полей важно следить за типами данных и корректно обрабатывать пустые значения, чтобы не пропустить важные расхождения.