Обнаружение и удаление дублирующихся записей
В данной заметке я хочу затронуть вопрос нахождения и удаления дублирующихся записей в MySQL.
И это касается не только вопроса сбора информации. Предлагаемые методы будут очень полезны начинающих программистам, которые ищут оптимальное решение в данном вопросе.
Для того, чтобы было проще объяснять код я создам тестовую таблицу work с ней буду производить свои эксперименты.
Таблица work имеет следующую структуру:
CREATE TABLE work( id mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT, region varchar(255) NOT NULL, job_mask varchar(255) NOT NULL, job_group varchar(255) NOT NULL, job_info text, PRIMARY KEY(id));
Вдаваться в тонкости создания таблиц мы не будем, так как это не является целью данной статьи.
Предположим, что таблица work уже заполнена данными.
Теперь вам надо оставить в таблице только уникальные данные в поле job_info для каждого значения region, job_group, job_mask.
Первым делом вы должны определить есть ли у вас дублирующиеся данные и сколько их (ведь если их нет, то вам и делать ничего не надо). Для этого вам стоит выполнить следующий запрос:
SELECT COUNT(*) AS dub, region, job_group, job_mask, job_info FROM work GROUP BY region, job_group, job_mask, job_info HAVING dub>1;
В результате выполнения запроса вы увидите таблицу с уже знакомыми вам полями и с дополнительным полем dub, которое будет содержать количество дублирующихся записей.
Далее удаление можно организовать следующими путями:
- Циклический обход-1.
Данный метод я называю “циклический обход-1″, потому что одну запись мы всегда оставляем не тронутой.Реализуется данные метод следующим способом:
// соединение с БД $query='SELECT COUNT(*) AS dub, region, job_group, job_mask, job_info FROM work GROUP BY region, job_group, job_mask, job_info HAVING dub>1'; $res=$db->query($query); if(!$res) { echo $db->error(); } else { while($row=$db->fetchAssoc($res)) { $region=$row['region']; $job_mask=$row['job_mask']; $job_group=$row['job_group']; $job_info=$row['job_info']; $query="SELECT id FROM work WHERE region='$region' AND job_mask='$job_mask' AND job_group='$job_group' AND job_info='$job_info'"; $result=$db->query($query); if(!$result) { echo $db->error(); } else { $i=1; while ($subrow=$db->fetchArray($result)) { if ($i==1) { $i++; continue; } $query="DELETE FROM work WHERE id='{$subrow[0]}'"; $del_res=$db->query($query); if(!$del_res) { echo $db->error(); } else { $deleted++; } $i++; } } } }
Методы объекта
$db, которые используются по ходу выполнения скрипта, являются методами моего стандартного абстрактного класса для доступа к БД. Имена методов очень тесно переплетаются с именами стандартных функций для работы с MySQL, поэтому я не вижу смысла останавливаться на этом более детально.В своих скриптах вы можете использовать привычные вам инструменты.
Главным преимуществом данного решения есть простота. Однако есть и очень большой недостаток - низкая скорость выполнения. На больших таблицах с большим количеством записей данный скрипт будет выполняться очень долго. Так что не забудьте поставить лимит времени и лимит памяти побольше.
- Использование запроса SELECT.
Второй вариант решения поставленной проблемы также достаточно распространен и предполагает некоторые знания в области SQL.Далее я приведу несколько запросов направленных на удаление дублирующихся записей и дам детальное их описание.Выборка уникальных данных в отдельную таблицу. Реализуется данный метод следующим образом:
CREATE TABLE original SELECT DISTINCT * FROM work ORDER BY id;
Данный запрос создаст таблицу
originalс оригинальными значениями, выбранными из таблицы work.Также можно использовать следующую модификацию вышеуказанного выражения:
CREATE TABLE original SELECT * FROM work GROUP BY region, job_group, job_mask, job_info ORDER BY id;
Дополнительная информация:
P.S. Думаю, что для начала этого инструментария будет достаточно. Но я не оставляю данную тему и надеюсь в ближайшем будущем предоставить дополнительную информацию по обработке дублирующихся записей в MySQL.

M.C.A.U.:
немного извращенный вариант
CREATE TEMPORARY TABLE `tmp` SELECT `ID` FROM `work` GROUP BY `region`, `job_group`, `job_mask`, `job_info` HAVING COUNT(*)>1;
23 Июнь 2008, 3:20 ппDELETE FROM `work` WHERE `ID` NOT IN (SELECT `ID` FROM `tmp`);
DROP TABLE `tmp`;
M.C.A.U.:
- работает если запустить один раз.
23 Июнь 2008, 3:28 ппесли повторно запускать, то нужно вначале проверить не пуста ли таблица “tmp”, иначе он сотрет все из `work`
admin:
2 таблицы - не очень удобно. Но я думаю, что быстрее будет, чем тот скрипт, что предоставил я.
26 Июнь 2008, 12:17 ппЯ уже когда-то пробовал мастерить с двумя таблицами, но к сожалению постоянно надо контроллировать этот процесс, что не очень удобно. Со скриптом же все проще - врубил и можешь идти пить чай.