4

我看到了创建具有唯一行的备用临时 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”上有一个索引?

编辑:在运行之前有什么方法可以测试查询吗?据我所知,MySQL 不支持 DELETE 查询的“解释”。

4

3 回答 3

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不小心仍将真实数据库用作默认数据库时运行实验!


回复您的评论:

USE test是一个 mysql 客户端内置命令。它将test数据库设置为默认数据库。当您在查询中命名表而不用数据库名称限定它们时,这将是默认数据库。见http://dev.mysql.com/doc/refman/5.1/en/use.html

SET autocommit = 0隐式关闭为每个查询提交事务的默认行为。所以你必须明确给出COMMITorROLLBACK命令来完成一个事务。见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)
于 2010-07-21T19:22:11.660 回答
0

该查询应该有效。拥有索引会改变性能,但这实际上取决于表的大小。

至于对此进行测试,我会将数据的子集复制到临时表中,然后在临时表上运行命令,然后再在真实表上运行它。

请记住在执行任何主要批处理作业之前始终备份表,以便您始终可以回滚。

于 2010-07-21T19:20:37.587 回答
0

我使用的方法避免了一个JOIN条件,应该明显更快:

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

子选择收集您要保留的 id 列表。这将允许您为每个field_name. 然后该DELETE语句将删除所有额外的重复行。

此外,是的,field_name字段上的索引将提高查询的性能。

于 2010-07-21T19:21:48.820 回答