У меня есть скрипт для поиска повторяющихся строк в моей таблице MySql, таблица содержит 40 000 000 строк. но это очень медленный ход, есть ли более простой способ найти дубликаты записей без входа и выхода из php?
Это сценарий, который я использую в настоящее время
$find = mysql_query("SELECT * FROM pst_nw ID < '1000'"); while ($row = mysql_fetch_assoc($find)) { $find_1 = mysql_query("SELECT * FROM pst_nw add1 = '$row[add1]' AND add2 = '$row[add2]' AND add3 = '$row[add3]' AND add4 = '$row[add4]'"); if (mysql_num_rows($find_1) > 0) { mysql_query("DELETE FROM pst_nw WHERE ID ='$row[ID]'} }
У вас есть несколько вариантов.
Создайте копию таблицы с уникальным индексом – и затем вставьте в нее данные из исходной таблицы:
CREATE TABLE clean LIKE pst_nw; ALTER IGNORE TABLE clean ADD UNIQUE INDEX (add1, add2, add3, add4); INSERT IGNORE INTO clean SELECT * FROM pst_nw; DROP TABLE pst_nw; RENAME TABLE clean pst_nw;
Преимущество таких действий заключается в том, что вы можете убедиться, что ваша новая таблица верна, прежде чем отбрасывать исходную таблицу. Недостатком является то, что он занимает в два раза больше места и (относительно) медленный для выполнения.
Вы также можете добиться желаемого результата:
set session old_alter_table=1; ALTER IGNORE TABLE pst_nw ADD UNIQUE INDEX (add1, add2, add3, add4);
Первая команда требуется в качестве обходного пути для игнорирования флагом игнорирования.
Преимущество здесь в том, что нет бесполезной работы с временной таблицей – недостатком является то, что вы не можете проверить, что ваше обновление делает именно то, что вы ожидаете, прежде чем запускать его.
Пример:
CREATE TABLE `foo` ( `id` int(10) NOT NULL AUTO_INCREMENT, `one` int(10) DEFAULT NULL, `two` int(10) DEFAULT NULL, PRIMARY KEY (`id`) ) insert into foo values (null, 1, 1); insert into foo values (null, 1, 1); insert into foo values (null, 1, 1); select * from foo; +----+------+------+ | id | one | two | +----+------+------+ | 1 | 1 | 1 | | 2 | 1 | 1 | | 3 | 1 | 1 | +----+------+------+ 3 row in set (0.00 sec) set session old_alter_table=1; ALTER IGNORE TABLE foo ADD UNIQUE INDEX (one, two); select * from foo; +----+------+------+ | id | one | two | +----+------+------+ | 1 | 1 | 1 | +----+------+------+ 1 row in set (0.00 sec)
Особенно с 40 миллионами строк, которые делают что-то подобное вне db, вероятно, потребуется огромное количество времени, и может не закончиться вообще. Любое решение, которое остается в db, будет быстрее и более надежным.
Обычно в таких вопросах проблема заключается в том, что «у меня есть повторяющиеся строки, вы хотите сохранить только одну строку, любую».
Но, судя по коду, вы хотите: «если набор add1, add2, add3, add4 дублируется, УДАЛИТЕ ВСЕ КОПИИ С ID <1000». В этом случае копирование из таблицы в другую с помощью INSERT IGNORE не будет делать то, что вы хотите – может даже содержать строки с более низкими идентификаторами и отбрасывать последующие.
Я считаю, что вам нужно запустить что-то вроде этого, чтобы собрать все «плохие идентификаторы» (идентификаторы с дубликатом, дубликат выше 1000, в этом коде я использовал «AND bad.ID <good.ID», поэтому, если у вас есть ID 777 который дублирует ID 888, ID 777 по-прежнему будет удален. Если это не то, что вы хотите, вы можете изменить это в «И bad.ID <1000 AND good.ID> 1000» или что-то в этом роде).
CREATE TABLE bad_ids AS SELECT bad.ID FROM pst_nw AS bad JOIN pst_nw AS good ON ( bad.ID < 1000 AND bad.ID < good.ID AND bad.add1 = good.add1 AND bad.add2 = good.add2 AND bad.add3 = good.add3 AND bad.add4 = good.add4 );
Затем, когда у вас есть все плохие идентификаторы в таблице,
DELETE pst_nw.* FROM pst_nw JOIN bad_ids ON (pst_nw.ID = bad_ids.ID);
Выступления в значительной степени выиграют от (non_unique, возможно только временного) индекса на add1, add2, add3, add4 и ID в этом порядке.
Конечно, есть. Обратите внимание, однако, что с 40 миллионами записей вы, скорее всего, превысите максимальное время выполнения php. Попробуйте следующее
Create table temp_pst_nw like pst_nw; Insert into temp_pst_nw select * from pst_nw group by add1,add2,add3,add4;
Подтвердите, что все в порядке!
Drop table pat_nw; Rename table temp_pst_nw to pst_nw;
Получите дубликаты строк, используя оператор «Группировать по». Вот пример, который вы можете попробовать:
select id from table group by matching_field1,matching_field2.... having count(id) > 1
Итак, вы получаете все дубликаты идентификаторов. Теперь удалите их с помощью запроса на удаление. Вместо использования оператора IN пользователя OR, поскольку IN очень медленный по сравнению с OR.
Попробуйте создать новую таблицу с теми же определениями. т.е. «my_table_two», тогда выполните:
SELECT DISTINCT unique_col1, col2, col3 […] FROM my_table INTO my_table_two;
Может быть, это разобратся.
Ваш код будет лучше, если вы не используете select *
, выберите только столбцы (4 адреса), которые вы хотите сравнить. Он должен иметь ограничение в моем sql. Он может избегать состояния не реагировать, когда у вас слишком много таких строк.