Как найти повторяющиеся значения в DBF-файле большого объема средствами 1С

Программист IT и автоматизация бизнеса
← На главную

При интеграции со сторонними системами или миграции исторических данных программистам 1С часто приходится работать с файлами формата DBF. Одной из типовых задач при такой обработке является поиск дубликатов. Если файл содержит десятки тысяч записей, простой перебор строк в цикле с поиском каждого значения "в лоб" займет огромное количество времени. Стандартные методы Найти и НайтиПоКлючу объекта XBase используют индексный файл, но возвращают только первое найденное совпадение, игнорируя последующие дубликаты.

В этой статье мы подробно разберем несколько эффективных способов решения этой задачи средствами 1С (для версий 7.7 и 8.х), а также проанализируем альтернативные варианты решения проблемы вне платформы.

---

Метод 1. Загрузка данных в Таблицу Значений и использование метода Свернуть

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

Суть метода: мы считываем данные из DBF-файла во временную таблицу значений в памяти 1С. Затем добавляем вспомогательную числовую колонку, заполняем ее единицами, группируем таблицу по проверяемому полю и суммируем единицы. Строки, где сумма получилась больше единицы, и есть наши дубликаты.

Разберем этот алгоритм по шагам на примере кода для 1С 8.х:


// Создаем объект для работы с DBF
ФайлДБФ = Новый XBase;
ФайлДБФ.ОткрытьФайлы("C:\Data\clients.dbf");

// Создаем Таблицу Значений для анализа
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("КодКлиента", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Счетчик", Новый ОписаниеТипов("Число"));

// Загружаем данные из DBF в ТЗ
Если ФайлДБФ.Открыта() Тогда
    ФайлДБФ.Первая();
    Пока Не ФайлДБФ.В Конце() Цикл
        НоваяСтрока = ТЗ.Добавить();
        НоваяСтрока.КодКлиента = СокрЛП(ФайлДБФ.CLIENT_ID);
        НоваяСтрока.Счетчик = 1; // Заполняем вспомогательную колонку
        ФайлДБФ.Следующая();
    КонецЦикла;
    ФайлДБФ.ЗакрытьФайлы();
КонецЕсли;

// Сворачиваем таблицу по анализируемому полю, суммируя счетчик
ТЗ.Свернуть("КодКлиента", "Счетчик");

// Сортируем по убыванию счетчика, чтобы дубли оказались вверху
ТЗ.Сортировать("Счетчик УБЫВ");

// Выводим результаты анализа
Для Каждого СтрокаТЗ Из ТЗ Цикл
    Если СтрокаТЗ.Счетчик > 1 Тогда
        Сообщить("Значение '" + СтрокаТЗ.КодКлиента + "' повторяется " + СтрокаТЗ.Счетчик + " раз(а).");
    Иначе
        // Так как таблица отсортирована, при первом значении <= 1 можно прервать цикл
        Прервать;
    КонецЕсли;
КонецЦикла;

Преимущества: простота написания кода, отсутствие необходимости создавать внешние индексы, высокая скорость работы на средних объемах данных.

Недостатки: вся таблица загружается в оперативную память. Если в DBF-файле очень много полей и миллионы записей, это может привести к избыточному потреблению ресурсов.

---

Метод 2. Последовательный обход дубликатов по индексу XBase

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

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

Проанализируем пример реализации такого обхода:


ФайлДБФ = Новый XBase;
// Открываем файл вместе с индексным файлом (CDX или NDX)
ФайлДБФ.ОткрытьФайлы("C:\Data\clients.dbf", "C:\Data\clients.cdx");

// Устанавливаем активный индекс (например, по полю CLIENT_ID)
ФайлДБФ.ТекущийИндекс = ФайлДБФ.Индексы.ID_INDEX;

// Определяем искомое значение
ИскомыйКлюч = "12345";

// Находим первую запись по ключу
Если ФайлДБФ.НайтиПоКлючу(ИскомыйКлюч, "=") Тогда
    КоличествоДублей = 0;
    
    // Читаем подряд все записи, соответствующие нашему ключу
    Пока СокрЛП(ФайлДБФ.CLIENT_ID) = ИскомыйКлюч И Не ФайлДБФ.В Конце() Цикл
        КоличествоДублей = КоличествоДублей + 1;
        
        // Здесь можно обработать дублирующуюся строку (например, вывести в лог или пометить на удаление)
        Сообщить("Найдена запись: " + ФайлДБФ.CLIENT_NAME);
        
        ФайлДБФ.Следующая();
    КонецЦикла;
    
    Если КоличествоДублей > 1 Тогда
        Сообщить("Всего найдено дубликатов для ключа " + ИскомыйКлюч + ": " + КоличествоДублей);
    КонецЕсли;
Иначе
    Сообщить("Записи с таким ключом не найдены.");
EndIf;

ФайлДБФ.ЗакрытьФайлы();

Преимущества: минимальное потребление оперативной памяти, высокая скорость позиционирования на диске благодаря использованию индексов.

Недостатки: требуется наличие корректного и актуального индексного файла (CDX/NDX). Если файл поврежден, результаты поиска будут неверными.

---

Метод 3. Запрос к Таблице Значений (для 1С v8.3)

Для разработчиков на управляемой платформе 1С 8.3 доступен продвинутый инструмент — использование языка запросов 1С для анализа таблиц в памяти. Это гораздо гибче, чем обычный метод Свернуть, так как позволяет использовать условия связи и получать детальные строки.

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


// Предположим, что ТЗ_Исходная уже заполнена данными из DBF-файла
// (как показано в первом методе)

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Передаем заполненную таблицу значений в параметры запроса
Запрос.УстановитьПараметр("ТаблицаДБФ", ТЗ_Исходная);

Запрос.Текст = 
"ВЫБРАТЬ
|	ТаблицаДБФ.КодКлиента КАК КодКлиента,
|	ТаблицаДБФ.ИмяКлиента КАК ИмяКлиента,
|	ТаблицаДБФ.Адрес КАК Адрес
|ПОМЕСТИТЬ ВТ_ИсходныеДанные
|ИЗ
|	&ТаблицаДБФ КАК ТаблицаДБФ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|// Шаг 1: Находим только те ключи, которые дублируются
|ВЫБРАТЬ
|	ВТ_ИсходныеДанные.КодКлиента КАК КодКлиента,
|	КОЛИЧЕСТВО(ВТ_ИсходныеДанные.КодКлиента) КАК Количество
|ПОМЕСТИТЬ ВТ_ДублирующиесяКлючи
|ИЗ
|	ВТ_ИсходныеДанные КАК ВТ_ИсходныеДанные
|
|СГРУППИРОВАТЬ ПО
|	ВТ_ИсходныеДанные.КодКлиента
|
|ИМЕЮЩИЕ
|	КОЛИЧЕСТВО(ВТ_ИсходныеДанные.КодКлиента) > 1
|;
|
|////////////////////////////////////////////////////////////////////////////////
|// Шаг 2: Соединяем исходные данные с дублями, чтобы получить полную информацию
|ВЫБРАТЬ
|	Исходные.КодКлиента КАК КодКлиента,
|	Исходные.ИмяКлиента КАК ИмяКлиента,
|	Исходные.Адрес КАК Адрес
|ИЗ
|	ВТ_ИсходныеДанные КАК Исходные
|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДублирующиесяКлючи КАК Дубли
|		ПО Исходные.КодКлиента = Дубли.КодКлиента
|ОБЪЕДИНИТЬ ВСЕ
|СОРТИРОВАТЬ ПО
|	КодКлиента";

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующая() Цикл
    // Мы получили полную информацию по каждой дублирующейся строке
    Сообщить("Дубликат: " + Выборка.КодКлиента + " | " + Выборка.ИмяКлиента);
КонецЦикла;

Преимущества: мощный инструмент анализа. С помощью SQL-подобного синтаксиса 1С можно накладывать сложные условия отбора дубликатов по нескольким полям одновременно.

---

Метод 4. Использование технологии ADO и прямых SQL-запросов (для 1С 7.7 и 8.х)

Этот метод по праву считается самым быстрым при работе в среде Windows. Мы не загружаем данные в память 1С построчно и не используем внутренние механизмы платформы для перебора. Вместо этого мы делегируем задачу поиска дубликатов драйверу базы данных (например, Microsoft Jet OLEDB или VFPOLEDB).

Выясним, как составить и выполнить такой запрос:


// Определяем путь к каталогу с файлом (в ADO каталогом является сама "База Данных")
КаталогФайла = "C:\Data\";
ИмяФайлаDBF = "clients.dbf";

Connection = Новый COMОбъект("ADODB.Connection");
Recordset = Новый COMОбъект("ADODB.Recordset");

// Строка подключения для драйвера Visual FoxPro (рекомендуется для DBF)
ConnectionString = "Provider=VFPOLEDB.1;Data Source=" + КаталогФайла + ";Mode=ReadWrite;Collating Sequence=MACHINE";

Попытка
    Connection.Open(ConnectionString);
Исключение
    Сообщить("Не удалось подключиться к базе данных DBF через ADO: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// Составляем классический SQL-запрос для поиска дубликатов
ТекстЗапроса = "SELECT CLIENT_ID, COUNT(*) as cnt FROM " + ИмяФайлаDBF + " GROUP BY CLIENT_ID HAVING COUNT(*) > 1";

Попытка
    Recordset.Open(ТекстЗапроса, Connection, 3, 1); // adOpenStatic, adLockReadOnly
Исключение
    Сообщить("Ошибка выполнения запроса ADO: " + ОписаниеОшибки());
    Connection.Close();
    Возврат;
КонецПопытки;

// Считываем результаты напрямую в 1С
Пока Recordset.EOF() = 0 Цикл
    КодКлиента = Recordset.Fields("CLIENT_ID").Value;
    Количество = Recordset.Fields("cnt").Value;
    
    Сообщить("Значение '" + СокрЛП(КодКлиента) + "' дублируется. Найдено строк: " + Количество);
    
    Recordset.MoveNext();
КонецЦикла;

Recordset.Close();
Connection.Close();

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

Недостатки: требуется наличие зарегистрированного в операционной системе провайдера данных (драйвера OLEDB). В современных 64-битных операционных системах иногда требуется ручная настройка и установка соответствующих драйверов.

---

Альтернативные методы решения задачи вне рамок 1С

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

  1. Использование табличного редактора MS Excel и надстройки Power Query.

    Если размер DBF-файла не превышает лимиты Excel (1 048 576 строк), файл можно легко открыть непосредственно в Excel. Далее, перейдя на вкладку "Данные", вы можете импортировать файл в редактор Power Query, выделить нужный столбец, кликнуть по нему правой кнопкой мыши и выбрать инструмент "Удалить дубликаты" (или предварительно сгруппировать строки по ключевому столбцу с операцией подсчета строк, чтобы увидеть статистику по дубликатам).

  2. Специализированные утилиты управления базами данных (DBF Viewer 2000, DBF Commander).

    Эти программные продукты разработаны специально для администрирования файлов dBase. В них по умолчанию встроены графические мастера для работы с дублями. Достаточно открыть файл, выбрать в меню пункт "Инструменты" -> "Поиск дубликатов", отметить галочками ключевые поля, после чего утилита мгновенно выделит цветом, отфильтрует или удалит лишние строки.

Выбор конкретного метода зависит от регулярности выполнения задачи, требований к автоматизации процесса и квалификации разработчика. Для регулярных интеграционных процедур в коде 1С мы рекомендуем использовать комбинацию загрузки в ТаблицуЗначений для небольших файлов или прямой SQL-запрос через ADO для работы с базами данных объемом в миллионы строк.

← На главную