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

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

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

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

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

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

Ниже разобраны рабочие подходы с примерами кода, условия их применения, ограничения и способы проверить корректность результата. Примеры ориентированы на встроенный язык 1С 8.3, но логика применима и к более ранним версиям платформы.

Определите, что именно нужно сравнить

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

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

Результат сравнения бывает трёх видов: булево «есть ли расхождения», список расходящихся строк или полный протокол «добавлено / удалено / изменено» с указанием старых и новых значений. Первый вариант самый дешёвый по производительности, третий нужен для журналов аудита и согласований.

Сравнение через запрос к базе данных

Если оба документа уже записаны в базу, самый производительный способ — запрос с полным соединением табличных частей по ключевым полям. Вся выборка выполняется на стороне СУБД, без перебора строк в коде.

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

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

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

Сравнение через выгрузку в таблицы значений

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

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

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

Быстрая проверка «изменилось ли что-то вообще»

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

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

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

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

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

Типовой шаблон: на клиенте собирается команда, вызывается серверная процедура СравнитьНаСервере(), в которой данные формы преобразуются в объект через РеквизитФормыВЗначение("Объект"), выполняется сравнение, а результат возвращается в виде таблицы значений или структуры. Результат выводят в табличное поле или сообщения пользователю.

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

Как проверить корректность сравнения

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

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

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

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

Идентичные документы
Норма

Сравнение возвращает пустой список расхождений

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

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

Документ с добавленной строкой
Норма

Строка помечена как добавленная, остальные не затронуты

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

Все строки после вставленной помечены изменёнными — значит, сравнение идёт по номеру строки

Дубли по ключевому полю
Норма

Обработчик либо корректно сворачивает дубли, либо явно сообщает о них

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

Дубли молча склеиваются и искажают результат

Пустая табличная часть
Норма

Все строки второй стороны определены как добавленные или удалённые

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

Ошибка обращения к NULL или пустой результат без пояснений

Типичные ошибки и ограничения

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

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

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

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

Итог

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

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

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

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

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

Как сравнить табличные части, если строки с одинаковым ключом повторяются?

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

Есть ли способ сравнить табличные части без программирования?

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

Как зафиксировать, что изменил пользователь в табличной части перед записью?

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

Почему запрос с полным соединением возвращает лишние строки?

Чаще всего причина в неполном ключе соединения: если по выбранным полям в одной табличной части есть дубли, каждая строка соединится с каждой, и результат умножится. Проверьте дубли по ключу запросом с группировкой и СУММА(1), расширьте ключ или предварительно сверните данные.

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

Прямое сравнение ссылок не сработает — одинаковые объекты имеют разные внутренние идентификаторы. Выгрузите табличные части в промежуточный формат (таблица значений, XML, JSON), сопоставляйте строки по смысловым ключам (код, артикул, номенклатура как строка), а ссылки сравнивайте через реквизиты-идентификаторы или соответствия, построенные при обмене.