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

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

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

Разберем подробно, почему внутреннее строковое представление дает сбой и какими проверенными способами можно корректно сравнить таблицы значений в платформе 1С.

Почему ЗначениеВСтрокуВнутр и СравнениеЗначений не подходят для таблиц

Выясним причину, по которой прямое сравнение сериализованных строк ЗначениеВСтрокуВнутр(Таблица1) = ЗначениеВСтрокуВнутр(Таблица2) не работает даже при визуально одинаковых данных:

  1. Скрытые метаданные колонок: Внутренний строковый формат 1С содержит не только значения ячеек, но и служебные дескрипторы колонок. В них зашиты битовые флаги индексации, доступности на изменение и расширенные квалификаторы типов данных.
  2. Разница между запросом и ручным заполнением: Если первая таблица получена методом Выгрузить() из результата запроса, а вторая создана программно и заполнена вручную, внутренние дескрипторы типов гарантированно разойдутся. Запрос формирует специфические ограничения длины, точности и признака NULL, которые не воспроизводятся при обычном объявлении через Новый ОписаниеТипов.
  3. Порядковые идентификаторы строк: Внутренняя строка учитывает служебные маркеры состояния строк в памяти платформы.
  4. Ограничение объекта СравнениеЗначений: Данный системный объект при работе с коллекциями (таблицы, массивы, структуры) сопоставляет ссылки на объекты в оперативной памяти, а не их содержимое. Соответственно, две разные таблицы значений всегда будут признаны неравными.

Способ 1. Сериализация в XML через СериализаторXDTO

Если нам требуется простое строковое представление для небольших таблиц значений без учета внутренних битовых масок платформы, воспользуемся штатным механизмом СериализаторXDTO. Он сохраняет только структуру колонок и фактические значения строк.

Посмотрим на пример функций для записи и чтения XML:


Функция ПолучитьXMLТаблицы(Данные)
    ЗаписьXML = Новый ЗаписьXML();
    ЗаписьXML.УстановитьСтроку();
    СериализаторXDTO.ЗаписатьXML(ЗаписьXML, Данные);
    Возврат ЗаписьXML.Закрыть();
КонецФункции

Функция ПолучитьТаблицуИзXML(СтрXML)
    ЧтениеXML = Новый ЧтениеXML();
    ЧтениеXML.УстановитьСтроку(СтрXML);
    Возврат СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
КонецФункции

Сравнение выполняется сопоставлением полученных строк:

ТаблицыРавны = (ПолучитьXMLТаблицы(тзн1) = ПолучитьXMLТаблицы(тзн2));

Важно: Для успешного сравнения через XML таблицы должны иметь одинаковый порядок колонок и одинаковую сортировку строк.

Способ 2. Объединение и свертка таблицы (метод Свернуть)

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

Разберем по шагам реализацию этого подхода:


// Сформируем список всех колонок для группировки
ВсеКолонки = "";
Для Каждого Колонка Из Таблица0.Колонки Цикл 
    ВсеКолонки = ВсеКолонки + ", " + Колонка.Имя;
КонецЦикла;
ВсеКолонки = Сред(ВсеКолонки, 3);

// Скопируем первую таблицу и добавим контрольную колонку
Таблица = Таблица1.Скопировать();    
Таблица.Колонки.Добавить("Счет", Новый ОписаниеТипов("Число"));
Таблица.ЗаполнитьЗначения(1, "Счет");

// Добавим строки из второй таблицы
Для Каждого Строка Из Таблица0 Цикл 
    ЗаполнитьЗначенияСвойств(Таблица.Добавить(), Строка);
КонецЦикла;

// Для новых добавленных строк счетчик останется равным 0 (или добавим -1 для баланса)
// Сворачиваем по всем исходным полям, суммируя колонку "Счет"
Таблица.Свернуть(ВсеКолонки, "Счет");

// Если строки дублируются в обеих таблицах одинаково, счетчик будет равен 2
Отбор = Новый Структура("Счет", 1);
Несовпадения = Таблица.Скопировать(Отбор);

Если Несовпадения.Количество() = 0 И Таблица1.Количество() = Таблица0.Количество() Тогда
    ТаблицыРавны = Истина;
Иначе
    ТаблицыРавны = Ложь;
КонецЕсли;

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

Для объемных таблиц или при необходимости детально проанализировать, какие именно строки были добавлены, изменены или удалены, используем объект Запрос и МенеджерВременныхТаблиц.

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


МенеджерВТ = Новый МенеджерВременныхТаблиц;

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Передаем обе таблицы в параметры запроса
Запрос.УстановитьПараметр("Таблица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";

Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
    ТаблицыРавны = Истина;
Иначе
    ТаблицыРавны = Ложь;
    ТаблицаРасхождений = Результат.Выгрузить();
КонецЕсли;

Способ 4. Поэтапная быстрая проверка (Каскадный алгоритм)

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

  1. Сравниваем количество строк: Таблица1.Количество() = Таблица2.Количество(). Если не равно — таблицы не совпадают.
  2. Сравниваем количество и состав колонок.
  3. Сортируем обе таблицы по одинаковому списку колонок через метод Сортировать().
  4. Проходим в цикле по строкам и колонкам, сравнивая значения ячеек:

Функция ТаблицыИдентичны(Таблица1, Таблица2, ИменаКолонокДляСортировки)
    Если Таблица1.Количество() <> Таблица2.Количество() Тогда
        Возврат Ложь;
    КонецЕсли;
    
    Если Таблица1.Колонки.Количество() <> Таблица2.Колонки.Количество() Тогда
        Возврат Ложь;
    КонецЕсли;
    
    Таблица1.Сортировать(ИменаКолонокДляСортировки);
    Таблица2.Сортировать(ИменаКолонокДляСортировки);
    
    ИндексСтроки = 0;
    Для Каждого Стр1 Из Таблица1 Цикл
        Стр2 = Таблица2[ИндексСтроки];
        Для Каждого Колонка Из Таблица1.Колонки Цикл
            Если Стр1[Колонка.Имя] <> Стр2[Колонка.Имя] Тогда
                Возврат Ложь;
            КонецЕсли;
        КонецЦикла;
        ИндексСтроки = ИндексСтроки + 1;
    КонецЦикла;
    
    Возврат Истина;
КонецФункции

Такой подход обеспечивает максимальную скорость работы, так как исключает накладные расходы на конвертацию типов и формирование тяжелых строковых буферов.

← На главную