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