Разработка и Конфигурация · 11 мин
Как сравнить табличные части документов в 1С
Способы сравнения табличных частей документов 1С: запросы, циклы, соединения таблиц значений. Готовые примеры кода, типичные ошибки и проверка результата.
Сравнение табличных частей документов — типовая задача при разработке и доработке конфигураций 1С. Она возникает при проверке корректировок, контроле изменений между версиями документа, сверке заказа с реализацией или при реализации механизмов согласования, где нужно зафиксировать, что именно поменял пользователь.
Универсальной кнопки «сравнить» в платформе нет, поэтому задачу решают программно: через запросы к базе, выгрузку табличных частей в таблицы значений или построчный обход в цикле. Выбор метода зависит от объёма данных, места выполнения кода (клиент или сервер) и того, что именно нужно получить на выходе — факт расхождения или детальный список различий.
Ниже разобраны рабочие подходы с примерами кода, условия их применения, ограничения и способы проверить корректность результата. Примеры ориентированы на встроенный язык 1С 8.3, но логика применима и к более ранним версиям платформы.
Определите, что именно нужно сравнить
Прежде чем писать код, зафиксируйте три вещи: какие документы сравниваются, по каким полям строки считаются «одинаковыми» и какой результат нужен. От этого зависит вся дальнейшая реализация.
Ключ строки — это набор полей, однозначно идентифицирующих строку. Для товарной табличной части это обычно Номенклатура, Характеристика, Серия; для расчётов — Контрагент и Договор. Номер строки как ключ использовать нельзя: при вставке или удалении строки нумерация сдвигается, и сравнение станет бессмысленным.
Результат сравнения бывает трёх видов: булево «есть ли расхождения», список расходящихся строк или полный протокол «добавлено / удалено / изменено» с указанием старых и новых значений. Первый вариант самый дешёвый по производительности, третий нужен для журналов аудита и согласований.
Сравнение через запрос к базе данных
Если оба документа уже записаны в базу, самый производительный способ — запрос с полным соединением табличных частей по ключевым полям. Вся выборка выполняется на стороне СУБД, без перебора строк в коде.
Полное соединение возвращает строки обоих документов. Если строка есть только в первом документе — поля второго будут NULL (она удалена), если только во втором — добавлена. Если строка есть в обоих, но отличаются сравниваемые поля (количество, цена, сумма) — она изменена.
Важная тонкость: условие на ссылки нужно помещать в секцию ГДЕ аккуратно — при полном соединении фильтрация может отсечь строки с NULL. Надёжнее перенести условия на ссылки в параметры виртуальных таблиц или в секцию соединения, либо обернуть каждую табличную часть во вложенный запрос с отбором.
Если ключевых полей несколько (например, Номенклатура + Характеристика), все они перечисляются в условии соединения через И. Пропуск одного поля ключа приведёт к декартову перемножению строк и ложным расхождениям.
Сравнение через выгрузку в таблицы значений
Когда один из документов ещё не записан (например, сравнивается текущая редактируемая версия с сохранённой в базе), запрос не подойдёт — незаписанных данных в базе нет. В этом случае обе табличные части выгружаются в таблицы значений методом Выгрузить().
Для ускорения поиска по таблице значений создайте индекс по ключевым полям: ТЗ.Индексы.Добавить("Номенклатура,Характеристика"). Затем обойдите первую таблицу в цикле и ищите соответствие во второй через НайтиСтроки() с отбором по ключу. Строки, для которых соответствие не найдено, — удалённые. Второй проход по второй таблице выявит добавленные строки.
Стандартный метод Найти() ищет только по одному полю, поэтому для составного ключа используйте НайтиСтроки() со структурой отбора. Без индекса поиск по таблице в несколько тысяч строк будет заметно медленнее, хотя для типовых документов с десятками строк разница неощутима.
Быстрая проверка «изменилось ли что-то вообще»
Если нужен только факт расхождения без детализации — например, чтобы решить, задавать ли вопрос о перепроведении, — полное сравнение избыточно. Достаточно сравнить контрольные итоги или сериализованное представление табличной части.
Рабочий приём: выгрузить табличную часть в таблицу значений и сравнить через ЗначениеВСтрокуВнутр() — если строковые представления совпали, табличные части идентичны с точностью до порядка строк. Если порядок строк не важен, предварительно отсортируйте обе таблицы по ключу методом Сортировать().
Для больших табличных частей дешевле сначала сравнить количество строк и сумму по числовому полю (например, итог по Количество или Сумма): если итоги различаются, расхождение есть, и дальше уже можно запускать детальное сравнение. Это существенно экономит ресурсы в массовых обработках.
Сравнение на клиенте и в управляемых формах
В управляемом приложении табличная часть на клиенте представлена коллекцией данных формы, а не объектом документа. Методы Выгрузить() и запросы на клиенте недоступны, поэтому логику сравнения выносят в серверную процедуру.
Типовой шаблон: на клиенте собирается команда, вызывается серверная процедура СравнитьНаСервере(), в которой данные формы преобразуются в объект через РеквизитФормыВЗначение("Объект"), выполняется сравнение, а результат возвращается в виде таблицы значений или структуры. Результат выводят в табличное поле или сообщения пользователю.
В тонком клиенте учитывайте, что передача больших таблиц значений между клиентом и сервером тоже стоит времени. Если табличная часть содержит тысячи строк, лучше возвращать только список расхождений, а не обе таблицы целиком.
Как проверить корректность сравнения
После реализации обязательно протестируйте механизм на контрольных примерах. Возьмите два одинаковых документа — сравнение должно показать отсутствие расхождений. Затем внесите по одному изменению каждого типа: добавьте строку, удалите строку, измените количество в существующей — и убедитесь, что каждое изменение определяется правильным статусом.
Отдельно проверьте граничные случаи: пустая табличная часть с одной стороны, дублирующиеся строки с одинаковым ключом (две строки с одной номенклатурой), строки с незаполненными ключевыми полями. Дубли по ключу — самый коварный случай: простое сравнение по ключу их «склеит», и расхождения в количестве будут интерпретированы неверно.
Как проверить результат
Сравнение возвращает пустой список расхождений
Находятся ложные различия — проверьте состав ключевых полей и сортировку
Строка помечена как добавленная, остальные не затронуты
Все строки после вставленной помечены изменёнными — значит, сравнение идёт по номеру строки
Обработчик либо корректно сворачивает дубли, либо явно сообщает о них
Дубли молча склеиваются и искажают результат
Все строки второй стороны определены как добавленные или удалённые
Ошибка обращения к NULL или пустой результат без пояснений
Типичные ошибки и ограничения
Самая частая ошибка — сравнение по номеру строки. Любая вставка или удаление сдвигает нумерацию, и отчёт о различиях показывает изменёнными все строки ниже точки вставки. Всегда используйте содержательный ключ из реквизитов.
Вторая группа ошибок связана с типами данных: сравнение чисел с разной точностью, строк с концевыми пробелами, ссылок из разных информационных баз (GUID одинаковых по смыслу объектов различаются). При обмене между базами сравнивайте ссылки по реквизитам-идентификаторам (код, артикул, GUID), а не по самой ссылке.
Учитывайте и ограничение по производительности: полное соединение двух табличных частей по несколько тысяч строк каждая — допустимая операция для разовой сверки, но в массовых обработках и при проведении документов такие проверки нужно оптимизировать или выполнять асинхронно.
Что важно запомнить
Сравнивайте табличные части по содержательному ключу, а не по номеру строки. Для записанных документов используйте запрос с полным соединением, для незаписанных — выгрузку в таблицы значений с индексами. Для простой проверки факта изменений достаточно сравнения контрольных итогов или сериализованного представления. Всегда тестируйте механизм на дублях, пустых табличных частях и добавлении/удалении строк.
Частые вопросы
Можно ли сравнить табличные части разных видов документов, например заказа и реализации?
Да, если у табличных частей есть совпадающие по смыслу поля. В запросе или при выгрузке приведите поля к общим именам через псевдонимы (например, Товары.Номенклатура в обоих случаях), после чего сравнение выполняется теми же методами. Учтите, что наборы полей различаются, поэтому сравнивайте только общую часть — номенклатуру, количество, цену.
Как сравнить табличные части, если строки с одинаковым ключом повторяются?
Предварительно сверните обе таблицы значений методом Свернуть() по ключевым полям с суммированием числовых колонок — тогда дубли объединятся в одну строку. Если свёртка недопустима по смыслу (строки различаются, например, сериями), включите различающее поле в состав ключа.
Есть ли способ сравнить табличные части без программирования?
Частично: можно открыть оба документа, вывести табличные части в табличный документ и визуально сверить, либо использовать универсальные отчёты и консоль запросов из типовых инструментов разработчика. Для регулярной сверки по заданным правилам без доработки кода не обойтись.
Как зафиксировать, что изменил пользователь в табличной части перед записью?
В обработчике ПередЗаписью получите сохранённую версию документа из базы через Ссылка.ПолучитьОбъект() или запросом, сравните её табличную часть с текущей (объект ещё не записан) и запишите расхождения в регистр сведений или журнал. Это стандартная схема аудита изменений.
Почему запрос с полным соединением возвращает лишние строки?
Чаще всего причина в неполном ключе соединения: если по выбранным полям в одной табличной части есть дубли, каждая строка соединится с каждой, и результат умножится. Проверьте дубли по ключу запросом с группировкой и СУММА(1), расширьте ключ или предварительно сверните данные.
Как сравнить табличные части документов из разных баз 1С?
Прямое сравнение ссылок не сработает — одинаковые объекты имеют разные внутренние идентификаторы. Выгрузите табличные части в промежуточный формат (таблица значений, XML, JSON), сопоставляйте строки по смысловым ключам (код, артикул, номенклатура как строка), а ссылки сравнивайте через реквизиты-идентификаторы или соответствия, построенные при обмене.