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

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

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

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

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

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

Что проверить перед сравнением таблиц

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

Во-первых, состав и типы колонок должны совпадать. Если в одной таблице колонка «Количество» имеет тип «Число», а в другой — «Строка» (типичная ситуация после загрузки из Excel или CSV), сравнение значений даст ложные расхождения. Проверить состав можно через коллекцию Таблица.Колонки, перебрав её в цикле.

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

Сравнение обходом цикла по ключевым колонкам

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

Пример типовой реализации:

Функция СравнитьТаблицы(Таблица1, Таблица2, КлючевыеПоля, СравниваемыеПоля)

Результат = Новый ТаблицаЗначений;

Результат.Колонки.Добавить("Ключ");

Результат.Колонки.Добавить("Статус"); // "ТолькоВ1", "ТолькоВ2", "Изменена"

Отбор = Новый Структура(КлючевыеПоля);

Для Каждого Строка1 Из Таблица1 Цикл

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

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

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

НоваяСтрока = Результат.Добавить();

НоваяСтрока.Ключ = Строка1[КлючевыеПоля];

НоваяСтрока.Статус = "ТолькоВ1";

Иначе

Строка2 = Найденные[0];

Для Каждого ИмяПоля Из СтрРазделить(СравниваемыеПоля, ",") Цикл

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

НоваяСтрока = Результат.Добавить();

НоваяСтрока.Ключ = Строка1[КлючевыеПоля];

НоваяСтрока.Статус = "Изменена";

Прервать;

КонецЕсли;

КонецЦикла;

КонецЕсли;

КонецЦикла;

Возврат Результат;

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

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

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

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

Когда таблицы содержат тысячи строк, эффективнее поместить их во временные таблицы и выполнить сравнение одним запросом. Это переносит работу на уровень СУБД (или механизма запросов платформы), где соединения оптимизируются автоматически.

Идея состоит в полном соединении двух временных таблиц по ключу: строки, у которых одна из сторон равна NULL, отсутствуют в одной из таблиц, а строки с несовпадающими значениями полей — изменены. Пример текста запроса:

ВЫБРАТЬ

ЕСТЬNULL(Т1.Номенклатура, Т2.Номенклатура) КАК Номенклатура,

ВЫБОР

КОГДА Т1.Номенклатура ЕСТЬ NULL ТОГДА "ТолькоВо2"

КОГДА Т2.Номенклатура ЕСТЬ NULL ТОГДА "ТолькоВ1"

ИНАЧЕ "Изменена"

КОНЕЦ КАК Статус

ИЗ

Таблица1 КАК Т1

ПОЛНОЕ СОЕДИНЕНИЕ Таблица2 КАК Т2

ПО Т1.Номенклатура = Т2.Номенклатура

ГДЕ

Т1.Номенклатура ЕСТЬ NULL

ИЛИ Т2.Номенклатура ЕСТЬ NULL

ИЛИ Т1.Количество <> Т2.Количество

ИЛИ Т1.Цена <> Т2.Цена

Таблицы значений передаются в запрос через параметры с использованием менеджера временных таблиц: сначала два запроса вида ВЫБРАТЬ ... ПОМЕСТИТЬ Таблица1 ИЗ &ТЗ1 КАК ТЗ, затем основной запрос с соединением. Учтите, что у полного соединения есть ограничение: в некоторых режимах совместимости и на файловых базах оно может работать медленнее, чем два левых соединения с объединением.

bsl
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ Таблица1 ИЗ &ТЗ КАК Т";
Запрос.УстановитьПараметр("ТЗ", Таблица1);
Запрос.Выполнить();

Быстрая проверка факта равенства таблиц

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

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

Ещё один вариант — сериализация таблицы в строку (например, через ЗначениеВСтрокуВнутр или формирование JSON) и сравнение хеш-сумм. Способ быстрый, но чувствителен к порядку строк и колонок, поэтому перед хешированием таблицы нужно отсортировать по ключу.

Сравнение способов

Цикл + НайтиСтроки

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

Запрос с полным соединением

Лучший выбор для больших таблиц. Требует передачи ТЗ во временные таблицы и аккуратной работы с NULL

Сортировка + обход по индексу

Быстро для проверки равенства, но требует гарантии уникальности ключа и одинаковой сортировки

Хеш сериализованной таблицы

Мгновенная проверка факта равенства, не даёт детализации расхождений

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

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

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

Для ссылочных типов сравнение работает корректно по ссылке, но если таблицы получены из разных информационных баз, сопоставлять объекты следует по реквизитам-ключам (код, ИНН, GUID), а не по внутренним ссылкам.

Типичные ошибки
01
Сравнение колонок с разными типами данных — строка '10' не равна числу 10, получаются ложные расхождения
02
Поиск по НайтиСтроки без индекса на большой таблице — обработка зависает на десятки минут
03
Сопоставление строк по порядковому номеру при разной сортировке — результат случайный
04
Игнорирование дублей ключа: НайтиСтроки возвращает несколько строк, а берётся только первая
05
Сравнение NULL и 0 в запросе как равных — полное соединение требует явной обработки ЕСТЬ NULL

Обработка дублей и неуникальных ключей

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

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

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

Маршрут решения

Как выбрать подход

Таблицы небольшие (до нескольких тысяч строк) и нужна детализация расхожденийОбход циклом с НайтиСтроки и индексом по ключевым полям
Таблицы большие или данные уже в СУБДПоместите ТЗ во временные таблицы и выполните запрос с полным соединением
Нужен только факт равенства без деталейСравните количество строк, итоги и хеш отсортированных таблиц
Ключ не уникален, а важны суммыСверните обе таблицы по ключу и сравнивайте агрегаты

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

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

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

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

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

Идентичные таблицы
Норма

Результат сравнения пуст

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

Найдены расхождения — проверьте типы колонок и пробелы в строках

Строка удалена из второй таблицы
Норма

Ровно одна запись со статусом отсутствия

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

Строка не найдена или продублирована в результате

Изменено одно поле в одной строке
Норма

Одна запись об изменении с указанием поля

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

Строка помечена как отсутствующая — ключ сравнения некорректен

Время выполнения
Норма

Секунды на рабочем объёме данных

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

Минуты — нет индекса или лишние обходы

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

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

Нет. Сравнение «Таблица1 = Таблица2» сравнивает ссылки на объекты, а не содержимое. Даже две полностью идентичные по данным таблицы будут «не равны», так как это разные объекты в памяти. Сравнивать нужно построчно или через запрос.

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

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

Почему НайтиСтроки не находит строку, которая точно есть?

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

Как сравнить табличные части двух документов?

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

Что быстрее: запрос или цикл?

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

Как получить только строки, общие для обеих таблиц?

Это внутреннее соединение по ключу. В запросе используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ двух временных таблиц, а при обходе циклом — добавляйте в результат только те строки, для которых НайтиСтроки вернул непустой массив.

Итог

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

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