请注意,您显示的查询将删除两个重复项。我假设您想保留其中一个。
以下是我将如何编写此查询:
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
不小心仍将真实数据库用作默认数据库时运行实验!
回复您的评论:
USE test
是一个 mysql 客户端内置命令。它将test
数据库设置为默认数据库。当您在查询中命名表而不用数据库名称限定它们时,这将是默认数据库。见http://dev.mysql.com/doc/refman/5.1/en/use.html
SET autocommit = 0
隐式关闭为每个查询提交事务的默认行为。所以你必须明确给出COMMIT
orROLLBACK
命令来完成一个事务。见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)