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

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

Постановка задачи и выбор стратегии сравнения

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

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

⚠️ Внимание: При сравнении всегда учитывайте тип данных. Строковое представление числа «10.00» и число 10 могут быть не равны при прямом сравнении типов, что приведет к ложным срабатываниям детектора изменений.

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

📊 Какой объем данных вы чаще всего обрабатываете?
До 100 строк
От 100 до 1000 строк
Более 1000 строк
Только справочники

Алгоритм прямого перебора и его ограничения

Самый интуитивно понятный способ — это вложенные циклы. Вы берете строку из первой табличной части и ищете ей пару во второй. Этот метод легко реализовать, но его производительность падает экспоненциально с ростом количества строк. Для массива из N строк сложность составит O(N²), что недопустимо для серьезных задач.

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

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

💡

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

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

Использование временных таблиц для быстрого сравнения

Наиболее производительный способ сравнить 2 табличные части в 1С — это выгрузка данных во временные таблицы с последующим объединением (UNION ALL) и группировкой. Платформа 1С обладает мощным движком работы с таблицами, который выполняет такие операции на стороне СУБД или в оптимизированном коде сервера.

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

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| ТЧ.Номенклатура КАК Номенклатура,

| ТЧ.Количество КАК Количество,

| &Источник КАК Источник

|ИЗ

| ТаблицаЧасти ТЧ

|ОБЪЕДИНИТЬ ВСЕ

|ВЫБРАТЬ

| ТЧ2.Номенклатура,

| ТЧ2.Количество,

| &Источник2

|ИЗ

| ТаблицаЧасти2 ТЧ2";

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

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

Сравнение через объекты НаборЗаписей и ПланСчета

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

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

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

Почему не стоит использовать Сериализацию для сравнения?

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

Оптимизация производительности при больших объемах

Когда количество строк исчисляется тысячами, каждая лишняя операция становится заметной. Главным врагом производительности является частое обращение к свойствам объектов в цикле. Рекомендуется сначала выгрузить данные в ТаблицуЗначений, так как работа с ней происходит быстрее, чем с объектами метаданных.

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

Метод сравнения Сложность Рекомендуемый объем Сложность реализации
Вложенные циклы O(N²) до 100 строк Низкая
Сортировка + линейный проход O(N log N) до 1000 строк Средняя
Временные таблицы (Запрос) O(N) Любой объем Высокая
Словари (Соответствия) O(N) до 5000 строк Средняя

Использование объекта Соответствие (Map) позволяет свести сложность поиска к константе O(1). Вы загружаете одну табличную часть в соответствие, где ключом является идентификатор строки, а значением — сама строка или массив значений. Затем проходите по второй таблице и мгновенно проверяете наличие и равенство данных.

☑️ Чек-лист оптимизации сравнения

Выполнено: 0 / 5

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

При сравнении числовых значений всегда возникает проблема точности. В 1С тип Число имеет высокую точность, но при вычислениях могут возникать погрешности или различия в количестве знаков после запятой. Прямое сравнение 10.500 и 10.5 может вернуть Истина, но сравнение результатов вычислений иногда требует округления до заданной точности перед сопоставлением.

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

⚠️ Внимание: Интерфейс и поведение некоторых методов могут меняться в новых версиях платформы. Всегда проверяйте документацию к конкретной версии 1С:Предприятие, если вы используете новые возможности языка запросов или методы объектов.

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

💡

Самый универсальный и производительный способ для любых объемов данных — это выгрузка в временные таблицы и использование языка запросов с оператором РАЗЛИЧИЕ или группировкой.

Готовые примеры кода и сниппеты

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

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

Соответствие = Новый Соответствие;

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

Ключ = СтрокаТЧ1[КлючевоеПоле];

Соответствие.Вставить(Ключ, СтрокаТЧ1);

КонецЦикла;

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

Для Каждого СтрокаТЧ2 Из ТЧ2 Цикл

Ключ = СтрокаТЧ2[КлючевоеПоле];

Если Не Соответствие.СодержитКлюч(Ключ) Тогда

// Строка новая

Продолжить;

КонецЕсли;

СтрокаТЧ1 = Соответствие[Ключ];

// Здесь логика сравнения реквизитов

КонецЦикла;

Возврат Различия;

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

Этот пример демонстрирует базовую структуру. В реальном проекте внутрь цикла нужно добавить проверку конкретных реквизитов (Количество, Цена, Сумма) и зафиксировать тип изменения: «Изменено», «Удалено» или «Добавлено». Такой подход делает код гибким и переиспользуемым.

Как сравнить табличные части, если в них нет уникального ключа?

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

Можно ли использовать метод Найти для поиска строк?

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

Влияет ли порядок строк на результат сравнения запросом?

Нет, при использовании группировки или объединения в запросе порядок строк в исходных табличных частях не важен. Результат будет зависеть только от наличия одинаковых наборов значений.

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

Всегда быстрее запрос на сервере, особенно при больших объемах. Клиентские циклы передают много данных по сети и нагружают клиентское приложение, тогда как запрос выполняется оптимизированным движком СУБД.