Как проверить строки таблицы значений на дубли по нескольким колонкам и удалить их в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке алгоритмов обработки данных в 1С часто возникает задача поиска и удаления дублирующихся строк в объекте ТаблицаЗначений. Если проверка осуществляется по одной колонке, задача решается достаточно просто. Однако при работе с составным ключом (когда уникальность определяется совпадением значений в двух и более реквизитах, например, Сотрудник и Должность) требуется выбрать оптимальный технический подход.

Рассмотрим подробнее основные способы решения этой задачи, их особенности, плюсы и возможные ошибки при реализации.

Способ 1. Использование копии таблицы со сверткой (Свернуть и НайтиСтроки)

Данный подход считается классическим и лаконичным для программной обработки небольших таблиц значений. Алгоритм заключается в следующем:

  1. Создаем копию исходной таблицы значений вызовом метода Скопировать().
  2. Добавляем в копию служебную числовую колонку (например, Кол) и заполняем её единицами.
  3. Сворачиваем копию таблицы по проверяемым колонкам, суммируя служебную колонку. После этого значение колонки Кол покажет, сколько раз данная комбинация ключей встречалась в исходной таблице.
  4. Обходим исходную таблицу значений с конца (чтобы индексы строк не сбивались при удалении) и ищем совпадение в свернутой копии с помощью метода НайтиСтроки(). Если счетчик превышает 1, мы удаляем текущую строку из исходной таблицы и уменьшаем счетчик в свернутой таблице на единицу.

Проанализируем пример готового кода, реализующего данный метод:


// Создаем копию таблицы для анализа дублей
тзкВознагражденияБезнал = тзВознагражденияБезнал.Скопировать();

// Добавляем числовую колонку-счетчик
ОписаниеТиповЧ = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0));
тзкВознагражденияБезнал.Колонки.Добавить("Кол", ОписаниеТиповЧ);
тзкВознагражденияБезнал.ЗаполнитьЗначения(1, "Кол");

// Сворачиваем по ключевым колонкам
тзкВознагражденияБезнал.Свернуть("Сотрудник, Должность", "Кол");

// Обходим исходную таблицу с конца к началу
ит = тзВознагражденияБезнал.Количество() - 1;
Пока ит >= 0 Цикл
    стр = тзВознагражденияБезнал[ит];
    
    // Ищем соответствие в свернутой таблице
    ПараметрыОтбора = Новый Структура("Сотрудник, Должность", стр.Сотрудник, стр.Должность);
    масСтрТЗК = тзкВознагражденияБезнал.НайтиСтроки(ПараметрыОтбора);
    
    стрТЗК = масСтрТЗК[0];
    Если стрТЗК.Кол > 1 Тогда
        // Удаляем дублирующую строку и уменьшаем счетчик
        тзВознагражденияБезнал.Удалить(стр);
        стрТЗК.Кол = стрТЗК.Кол - 1;
    КонецЕсли;
    
    ит = ит - 1;
КонецЦикла;

Важный нюанс производительности: при поиске строк с помощью НайтиСтроки() платформа по умолчанию выполняет линейный обход. Для ускорения поиска при большом объеме данных рекомендуется добавить индекс к служебной свернутой таблице вызовом тзкВознагражденияБезнал.Индексы.Добавить("Сотрудник, Должность").

Способ 2. Использование запроса (ВЫБРАТЬ РАЗЛИЧНЫЕ)

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

Разберем алгоритм выполнения через запрос:

  1. Передаем исходную таблицу значений в запрос как параметр.
  2. Помещаем таблицу во временную таблицу в менеджере временных таблиц.
  3. Используем ключевое слово ВЫБРАТЬ РАЗЛИЧНЫЕ по нужным колонкам или применяем группировку СГРУППИРОВАТЬ ПО.
  4. Выгружаем результат запроса обратно в новую таблицу значений.

Посмотрим на пример кода запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	ТЗ.Сотрудник КАК Сотрудник,
    |	ТЗ.Должность КАК Должность,
    |	ТЗ.Сумма КАК Сумма
    |ИЗ
    |	&ИсходнаяТЗ КАК ТЗ";

Запрос.УстановитьПараметр("ИсходнаяТЗ", тзВознагражденияБезнал);
тзВознагражденияБезнал = Запрос.Выполнить().Выгрузить();

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

Способ 3. Предварительная сортировка таблицы значений

Ещё один простой способ обработки таблицы значений — сортировка по проверяемым колонкам с помощью метода Сортировать().

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


// Сортируем таблицу по ключевым колонкам
тзВознагражденияБезнал.Сортировать("Сотрудник Возр, Должность Возр");

Индекс = тзВознагражденияБезнал.Количество() - 1;
Пока Индекс > 0 Цикл
    ТекущаяСтрока = тзВознагражденияБезнал[Индекс];
    ПредыдущаяСтрока = тзВознагражденияБезнал[Индекс - 1];
    
    // Сравниваем значения ключевых полей с предыдущей строкой
    Если ТекущаяСтрока.Сотрудник = ПредыдущаяСтрока.Сотрудник 
       И ТекущаяСтрока.Должность = ПредыдущаяСтрока.Должность Тогда
        тзВознагражденияБезнал.Удалить(ТекущаяСтрока);
    КонецЕсли;
    
    Индекс = Индекс - 1;
КонецЦикла;

Распространенная ошибка при использовании коллекций (Структуры и Соответствия)

При поиске дублей разработчики часто пытаются использовать объект Структура или Соответствие для хранения составного ключа и проверки его наличия в массиве вызовом Массив.Найти().

Обратите внимание: в платформе 1С объекты типа Структура при поиске в массиве или при сравнении проверяются по ссылке на объект в памяти, а не по содержимому полей! При вызове Новый Структура(...) на каждом шаге цикла создается новый объект, поэтому платформа никогда не найдет совпадения с ранее добавленной структурой.

Если требуется использовать коллекцию Соответствие или Массив для проверки дубликатов, формируйте составной строковый ключ-хэш из уникальных идентификаторов ссылок или их строковых представлений:


КлючХэш = Строка(Стр.Сотрудник.УникальныйИдентификатор()) + "_" + Строка(Стр.Должность.УникальныйИдентификатор());

Подводя итог: для таблиц небольшого размера (до нескольких десятков строк) отлично подойдет вариант с предварительной сортировкой или метод свертки со сбором дублей. Для работы с большими объемами данных наилучшим выбором станет выборка через запрос.

← На главную