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