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