2010-07-21 2 views
4

Я видел решение создать альтернативную временную таблицу MySQL с уникальными строками, но мне не понравилась эта идея, так как мои таблицы очень большие и были бы трудными для их перемещения (и создавали бы огромные проблемы, если бы ошибки во время перемещения).Является ли это хорошим решением для удаления повторяющихся строк MySQL?

Я, однако, нашел следующее. Что вы думаете об этом (где дубликаты для проверки - «field_name»)?

DELETE FROM table1 
USING table1, table1 as vtable 
WHERE (NOT table1.ID=vtable.ID) 
AND (table1.field_name=vtable.field_name) 

Кто-то сказал, что это должно работать, но я не совсем уверен. Как вы думаете? Кроме того, будут ли индексы вообще изменять производительность этой команды, скажем, имея индекс на «field_name»?

EDIT: Был ли какой-либо способ протестировать запрос перед его запуском? Насколько мне известно, MySQL не поддерживает «объяснять» запросы DELETE.

+0

Я собирался послать еще один пример запроса, но вы проверили это еще? Мне кажется, что обе записи будут удалены. – Fosco

+0

Вы можете заменить «delete» на «select», чтобы проверить ваш запрос. –

ответ

4

Обратите внимание, что указанный вами запрос удалит и дубликаты. Я бы предположил, что вы хотите сохранить то или другое.

Вот как я бы написать этот запрос:

DELETE t1 FROM table1 AS t1 JOIN table1 AS t2 
    ON t1.id > t2.id AND t1.field_name = t2.field_name; 

Используя больше чем вместо не-равно-то, вы только удалить одну строку (позже один), вместо обоих.

Может помочь комплексный индекс (id, field_name). Вы должны подтвердить это с помощью MySQL EXPLAIN, чтобы получить отчет об оптимизации. Но EXPLAIN поддерживает только SELECT запросы, так что вы должны выполнить эквивалентную SELECT подтвердить оптимизацию:

EXPLAIN SELECT * FROM table1 AS t1 JOIN table1 AS t2 
    ON t1.id > t2.id AND t1.field_name = t2.field_name; 

Вы также спросили о тестировании. Я рекомендовал бы скопировать образец строк, содержащих дубликаты в таблицу в базе данных test:

CREATE TABLE test.table1test SELECT * FROM realdb.table1 LIMIT 10000; 

Теперь вы можете проводить эксперименты на данных образцов, пока вы не удовлетворили DELETE решение является правильным.

USE test; 
SET autocommit = 0; 
DELETE ... 
ROLLBACK; 

Я бы рекомендовал именование царапание таблицы в базе данных test нечто отличное от вашей реальной таблицы в реальной базе данных. На всякий случай вы запускаете экспериментальный DELETE, пока вы случайно используете свою реальную базу данных в качестве базы данных по умолчанию!


Re ваших комментариев:

USE test является встроенной командой MySQL клиента. Он устанавливает базу данных test в качестве базы данных по умолчанию. Это будет база данных по умолчанию, когда вы называете таблицы в своих запросах, не указывая их с именем базы данных. См. http://dev.mysql.com/doc/refman/5.1/en/use.html

SET autocommit = 0 отключает поведение по умолчанию для совершения транзакции для каждого запроса неявно. Поэтому вы должны явно предоставить команду COMMIT или ROLLBACK для завершения транзакции. См. http://dev.mysql.com/doc/refman/5.1/en/commit.html

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

DELETE t1 не является опечаткой. DELETE удаляет строки, а не целые таблицы. t1 является псевдонимом для каждого строки, который удовлетворяет условиям оператора (хотя возможно, что условия включают в себя каждую строку в таблице). См описание нескольких столов удалить при http://dev.mysql.com/doc/refman/5.1/en/delete.html

Рода, как при запуске цикла в PHP и использовать переменный для итерации цикла: for ($i=0; $i<100; ++$i) ... Переменная $i принимает ряд значений, и каждая время через цикл имеет другое значение.

Вот демонстрация того, как мое решение удаляет несколько дубликатов. Я побежал это в моей базе данных test и я вставить результат непосредственно из окна командной:

mysql> create table table1 (id serial primary key, field_name varchar(10)); 
Query OK, 0 rows affected (0.45 sec) 

mysql> insert into table1 (field_name) 
     values (42), (42), (42), (42), (42), (42); 
Query OK, 6 rows affected (0.00 sec) 
Records: 6 Duplicates: 0 Warnings: 0 

mysql> select * from table1; 
+----+------------+ 
| id | field_name | 
+----+------------+ 
| 1 | 42   | 
| 2 | 42   | 
| 3 | 42   | 
| 4 | 42   | 
| 5 | 42   | 
| 6 | 42   | 
+----+------------+ 
6 rows in set (0.00 sec) 

mysql> delete t1 from table1 t1 join table1 t2 
     on t1.id > t2.id and t1.field_name = t2.field_name; 
Query OK, 5 rows affected (0.00 sec) 

mysql> select * from table1; 
+----+------------+ 
| id | field_name | 
+----+------------+ 
| 1 | 42   | 
+----+------------+ 
1 row in set (0.00 sec) 
+0

Спасибо за помощь! Вы получили ответ на свой ответ, поэтому я просто предполагаю, что ваш ответ - лучшее решение (не сказать, что другие плохие). Не могли бы вы объяснить, пожалуйста, что означает блок кода, который начинается с «USE test; SET ...»? Кроме того, чтобы убедиться, что использование большего знака гарантирует, что ВСЕ дубликаты будут удалены, даже если есть более одного дубликата (скажем, 5)? Большое спасибо. –

+0

Также забыл спросить: ваш первый блок кода с решением не опечатка, правильно? Вы помещаете «DELETE t1». Разве это не означает, что он удалит всю таблицу или что-то еще? Извините за все вопросы, это немного сложно для меня =) –

+1

Просто хотел зайти и сказать еще раз спасибо за эту блестящую рецензию. Я все еще упоминал об этом более двух лет спустя! –

0

Этот запрос должен работать. Наличие индексов изменит производительность, но это действительно зависит от размера таблицы.

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

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

0

Метод, который я использую избегает JOIN состояния и должен быть значительно быстрее:

DELETE FROM table1 WHERE id NOT IN (SELECT MIN(x.id) FROM table1 AS x GROUP BY x.field_name); 

подвыборки собирает список идентификатора, который вы хотите сохранить. Это позволит вам сохранить уникальную строку для каждого field_name. Оператор DELETE затем удалит все лишние повторяющиеся строки.

Кроме того, да, индекс на поле field_name улучшит производительность в вашем запросе.

+0

Практически любое использование 'GROUP BY' в MySQL вызывает временную таблицу, которая серьезно вредит производительности. –

+0

@Bill - просто сделал быструю проверку на одной из моих тестовых баз данных. Конечно, всего 30 000 строк, но «GROUP BY» значительно превосходит «JOIN» в тех случаях, когда я его использую. – thetaiko

+0

Хорошо, это хорошо. Я надеюсь, что это также относится к базе данных OP. –

Смежные вопросы