К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как сравнить две строки таблицы значений в 1С

Способы сравнения двух строк таблицы значений в 1С: по полям через ЗаполнитьЗначенияСвойств, сериализацию и цикл по колонкам. Готовый код и типичные ошибки.

Строка таблицы значений в 1С — это объект типа «СтрокаТаблицыЗначений», и оператор сравнения «=» для таких объектов работает не так, как ожидает разработчик: он сравнивает ссылки на объекты, а не содержимое полей. Даже если две строки содержат абсолютно одинаковые значения во всех колонках, сравнение «Строка1 = Строка2» вернёт Ложь, если это разные объекты. Поэтому задача «сравнить две строки» всегда решается через сравнение значений их полей.

На практике такая задача возникает при поиске дублей в таблице, при контроле изменений данных до и после обработки, при синхронизации табличных частей документа с загружаемыми данными и при сравнении двух таблиц значений построчно. Универсальной встроенной функции «СравнитьСтроки» в платформе нет, поэтому сравнение реализуют вручную — одним из нескольких рабочих способов.

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

Почему нельзя сравнивать строки оператором «=»

Строка таблицы значений — это не примитивный тип вроде числа или строки, а объект со ссылочной семантикой. Когда вы пишете условие Если Строка1 = Строка2 Тогда, платформа проверяет, указывают ли обе переменные на один и тот же объект в памяти. Две строки с идентичными значениями, но полученные из разных таблиц или даже из разных копий одной таблицы, никогда не будут равны при таком сравнении.

Единственный случай, когда «=» возвращает Истину, — когда обе переменные ссылаются на одну и ту же строку, например Строка2 = Строка1 после присваивания. Это свойство иногда используют для проверки идентичности объекта, но для сравнения данных оно бесполезно и является источником скрытых ошибок: код компилируется, выполняется без исключений, но условие никогда не срабатывает.

Аналогичная проблема есть у метода Найти() таблицы значений, если передать в него строку целиком, — но при передаче значения конкретного поля он работает корректно и часто заменяет ручное сравнение.

Сравнение через цикл по колонкам

Самый надёжный и универсальный способ — перебрать колонки таблицы значений и сравнить значения одноимённых полей двух строк. Такой подход не зависит от типов данных в колонках и легко расширяется списком исключаемых полей.

Функция СтрокиРавны(Строка1, Строка2, ИсключаемыеКолонки = Неопределено)

Если ИсключаемыеКолонки = Неопределено Тогда

ИсключаемыеКолонки = Новый Массив;

КонецЕсли;

Для Каждого Колонка Из Строка1.Владелец().Колонки Цикл

Если ИсключаемыеКолонки.Найти(Колонка.Имя) <> Неопределено Тогда

Продолжить;

КонецЕсли;

Если Строка1[Колонка.Имя] <> Строка2[Колонка.Имя] Тогда

Возврат Ложь;

КонецЕсли;

КонецЦикла;

Возврат Истина;

КонецФункции

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

Способ хорошо подходит для сравнения по всем полям или по всем, кроме служебных (например, исключить колонки «Комментарий» или «ДатаИзменения», которые не должны влиять на результат). Для сравнения по небольшому фиксированному набору полей проще перечислить их явно, не перебирая колонки.

Сравнение по выбранным полям

Если сравнивать нужно только ключевые реквизиты — например, номенклатуру, количество и цену в строке документа, — достаточно сравнить эти поля напрямую. Код получается коротким и читаемым:

Если Строка1.Номенклатура = Строка2.Номенклатура

И Строка1.Количество = Строка2.Количество

И Строка1.Цена = Строка2.Цена Тогда

// строки совпадают по ключевым полям

КонецЕсли;

Когда список сравниваемых полей длинный или формируется динамически, удобно задать его строкой и перебрать через СтрРазделить или цикл по массиву имён. Это тот же подход, что в платформенных механизмах, где списки полей передаются строкой с запятыми.

Важная тонкость — сравнение полей ссылочного типа. Значения типа «СправочникСсылка» сравниваются оператором «=» корректно (по значению ссылки), поэтому номенклатуру, контрагента, договор можно сравнивать напрямую. А вот если в колонке хранится таблица значений, массив или другой составной объект, простое «=» снова даст сравнение ссылок, и для таких полей потребуется отдельная логика.

Сравнение через сериализацию

Быстрый приём для сравнения строк целиком — преобразовать каждую строку в строковое представление и сравнить результаты. Для этого копируют строку в отдельную таблицу или используют функцию ЗначениеВСтрокуВнутр, которая сериализует значение во внутренний формат платформы:

Функция СтрокаКакСтрока(СтрокаТЗ)

Копия = СтрокаТЗ.Владелец().СкопироватьКолонки();

НоваяСтрока = Копия.Добавить();

ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗ);

Возврат ЗначениеВСтрокуВнутр(НоваяСтрока);

КонецФункции

Если СтрокаКакСтрока(Строка1) = СтрокаКакСтрока(Строка2) Тогда

// содержимое строк совпадает

КонецЕсли;

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

Для разовой проверки или отладки способ вполне рабочий. Для производственного кода с большими объёмами лучше цикл по колонкам или поиск через индексы таблицы значений.

Сравнение вариантов

Цикл по колонкам

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

Явное сравнение полей

Самый быстрый и читаемый вариант для фиксированного набора ключевых полей.

ЗначениеВСтрокуВнутр

Сравнивает всё сразу одной операцией, но медленнее на больших объёмах и зависит от формата платформы.

Найти() с индексом

Оптимально для поиска совпадающей строки в большой таблице, а не попарного сравнения.

Поиск совпадающей строки в другой таблице

Частая реальная задача — не сравнить две конкретные строки, а проверить, есть ли в таблице Б строка, совпадающая со строкой таблицы А по набору полей. Здесь вместо попарного сравнения эффективнее использовать НайтиСтроки() с отбором:

Отбор = Новый Структура;

Отбор.Вставить("Номенклатура", Строка1.Номенклатура);

Отбор.Вставить("Характеристика", Строка1.Характеристика);

Найденные = Таблица2.НайтиСтроки(Отбор);

Если Найденные.Количество() > 0 Тогда

// совпадающая строка найдена

КонецЕсли;

Для ускорения поиска на больших таблицах добавьте индекс по полям отбора: Таблица2.Индексы.Добавить("Номенклатура,Характеристика"). Без индекса каждый вызов НайтиСтроки перебирает всю таблицу, и обработка двух таблиц по несколько тысяч строк превращается в квадратичный перебор.

Если нужно сравнить две таблицы целиком и найти расхождения, альтернативой служит запрос: выгрузить обе таблицы во временные таблицы через параметр-таблицу и выполнить полное соединение с условием на NULL. Такой подход переносит работу на СУБД и обычно быстрее построчного перебора в коде.

Порядок действий
  1. 1
    Определите, какие поля участвуют в сравнении: все или только ключевые.
  2. 2
    Проверьте, что структура колонок обеих таблиц совпадает по именам и типам.
  3. 3
    Реализуйте функцию сравнения через цикл по колонкам или явное сравнение полей.
  4. 4
    Для поиска в большой таблице добавьте индекс и используйте НайтиСтроки.
  5. 5
    Проверьте результат на тестовых данных: совпадающие и заведомо различные строки.

Тонкости сравнения значений в полях

Даже при корректном переборе полей результат может отличаться от ожидаемого из-за особенностей типов данных. Числа с разной точностью хранения, строки с неразрывными пробелами и даты с разным временем — типичные источники «ложных» расхождений.

Строковые поля сравнивайте с учётом регистра и пробелов, если данные приходят из внешних источников: СокрЛП(ВРег(Значение1)) = СокрЛП(ВРег(Значение2)). Даты, загруженные из файлов, часто содержат время, которого нет в учётных данных, — сравнивайте через НачалоДня(), если время не имеет значения.

Отдельный случай — значение Неопределено и пустые ссылки. Неопределено = Неопределено даёт Истину, но Неопределено = ПустаяСсылка — Ложь, хотя визуально оба поля «пустые». Перед сравнением приведите пустые значения к единому виду, если для логики они равнозначны.

Типичные ошибки
01
Сравнение строк оператором «=» — сравниваются ссылки на объекты, условие никогда не выполняется.
02
Перебор колонок одной таблицы при разной структуре таблиц — обращение к несуществующей колонке вызывает ошибку исполнения.
03
Сравнение строк с неразрывными пробелами и разным регистром — ложные расхождения при визуально одинаковых данных.
04
Поиск по большой таблице без индекса — квадратичный перебор и заметные задержки.
05
Игнорирование различия между Неопределено и пустой ссылкой — совпадающие по смыслу строки считаются разными.

Проверка результата и отладка

Чтобы убедиться, что функция сравнения работает правильно, проверьте её на контрольных парах: две ссылки на одну строку (должно быть Истина), две строки с одинаковыми значениями из разных таблиц (Истина), строки с отличием в одном поле (Ложь) и строки с отличием в исключаемой колонке (Истина, если поле исключено).

При расхождениях, которые «не видны глазом», выведите значения проблемных полей в отладчике через ЗначениеВСтрокуВнутр или смотрите их типы: часто оказывается, что в одной таблице поле имеет тип «Строка», а в другой — «Число» после загрузки из файла. Несовпадение типов при сравнении «=» даёт Ложь без всяких предупреждений.

Контрольные точки

Как проверить результат

Одинаковые строки из разных таблиц
Норма

Функция возвращает Истина

Тревожный признак

Возвращает Ложь — сравниваются ссылки, а не значения, или отличаются типы полей

Строки с отличием в одном поле
Норма

Функция возвращает Ложь

Тревожный признак

Возвращает Истина — поле попало в список исключений или не перебирается

Производительность на больших таблицах
Норма

Поиск по индексу выполняется быстро

Тревожный признак

Заметные задержки — индекс не добавлен, идёт полный перебор

Частые вопросы

Можно ли сравнить две таблицы значений целиком одной командой?

Встроенной функции сравнения таблиц нет. Практичные варианты: построчное сравнение через цикл, запрос с полным соединением двух таблиц, переданных как параметры, либо сериализация обеих таблиц через ЗначениеВСтрокуВнутр с последующим сравнением строк — последнее подходит только для небольших таблиц и контрольных проверок.

Почему Найти() не находит строку, которая точно есть в таблице?

Чаще всего искомое значение отличается по типу (число против строки), содержит неразрывные пробелы или лишние пробелы, либо в дате присутствует время. Проверьте тип и точное значение поля в отладчике и приведите данные к единому виду перед поиском.

Как исключить из сравнения служебные колонки?

Передайте в функцию сравнения массив имён исключаемых колонок и пропускайте их в цикле, как показано в примере с параметром ИсключаемыеКолонки. Альтернатива — явно перечислить только сравниваемые поля, тогда служебные колонки не участвуют в проверке по умолчанию.

Что быстрее: цикл по колонкам или ЗначениеВСтрокуВнутр?

На единичных сравнениях разница незаметна. На массовых обработках цикл по колонкам быстрее, поскольку сериализация создаёт копию таблицы и промежуточную строку для каждого сравнения. Для поиска совпадений в больших таблицах оба варианта проигрывают НайтиСтроки с индексом.

Как сравнить строки, если таблицы имеют разный набор колонок?

Сравнивать нужно только по общим полям: сформируйте список имён колонок, присутствующих в обеих таблицах, и перебирайте его. Обращение к колонке, которой нет в таблице-владельце строки, вызовет ошибку исполнения, поэтому проверка наличия колонки обязательна.

Работает ли сравнение «=» для строк дерева значений?

Нет, строка дерева значений — тоже объект со ссылочной семантикой, и «=» сравнивает ссылки на объекты. Подходы те же: цикл по колонкам владельца, явное сравнение полей или сериализация. Дополнительно учитывайте, что у строк дерева есть родитель и подчинённые строки, которые при необходимости сравнивают отдельно.

Итог

Что важно запомнить

Оператор «=» для строк таблицы значений сравнивает ссылки на объекты, а не данные. Содержимое строк сравнивайте циклом по колонкам, явным перечислением полей или сериализацией. Для поиска совпадений в больших таблицах используйте НайтиСтроки с индексом. Контролируйте типы полей, пробелы и различие между Неопределено и пустой ссылкой.