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