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