从MySql表中删除重复行

时间:2012-08-19 20:27:07

标签: php mysql

我有一个脚本可以在我的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]'}

         }

6 个答案:

答案 0 :(得分:6)

您有很多选择。

让DB完成工作

使用唯一索引创建表的副本 - 然后从源表中将数据插入其中:

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;

以这种方式执行操作的优点是您可以在删除源表之前验证新表是否正确。缺点是它占用了两倍的空间并且(相对)执行速度慢。

让DB完成工作#2

您也可以通过以下方式获得所需的结果:

set session old_alter_table=1;
ALTER IGNORE TABLE pst_nw ADD UNIQUE INDEX (add1, add2, add3, add4);

第一个命令是the ignore flag being .. ignored

的变通方法

这里的优点是没有关于临时表的问题 - 缺点是你不能检查你的更新是否完全符合你的预期,然后运行它。

示例:

 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)

不要在DB之外做这种事情

特别是在数据库外部有4000万行做这样的事情可能需要花费大量时间,而且可能根本无法完成。保留在数据库中的任何解决方案都会更快,更强大。

答案 1 :(得分:2)

通常在这样的问题中,问题是“我有重复的行,只想保留一行,任何一行”。

但从代码判断,你想要的是:“如果一组add1,add2,add3,add4重复,则删除所有ID为ID <1000的副本”。在这种情况下,使用INSERT IGNORE从表复制到另一个表将无法执行您想要的操作 - 甚至可能保留ID较低的行并丢弃后续的行。

我相信你需要运行这样的东西来收集所有“坏ID”(带有重复的ID,高于1000的副本;在这段代码中我使用了“AND bad.ID&lt; good.ID”,所以如果如果ID 777与ID 888重复,ID 777仍然会被删除。如果这不是您想要的,您可以在“AND bad.ID&lt; 1000 AND good.ID&gt; 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 );

然后,一旦将所有错误的ID放入表格中,

DELETE pst_nw.* FROM pst_nw JOIN bad_ids ON (pst_nw.ID = bad_ids.ID);

此顺序对add1,add2,add3,add4和ID的(非唯一的,可能只是临时的)索引将大大受益。

答案 2 :(得分:2)

使用“分组依据”运算符获取重复的行。以下是您可以尝试的示例:

select id
 from table
group by matching_field1,matching_field2....
having count(id) > 1

所以,你得到了所有重复的id。现在使用删除查询删除它们。 而不是使用“IN”,使用“OR”运算符作为“IN”与“OR”相比较慢。

答案 3 :(得分:1)

当然有。但请注意,有4000万条记录你最有可能超过最大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;

答案 4 :(得分:0)

尝试创建具有相同定义的新表。即“my_table_two”,然后执行:

  

SELECT DISTINCT unique_col1,col2,col3 [...] FROM my_table INTO   my_table_two;

也许这会解决它。

答案 5 :(得分:0)

如果您不使用@Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.image); dest.writeString(this.price); dest.writeString(this.credit); dest.writeString(this.title); dest.writeString(this.description); dest.writeString(this.id); } getIntent().getParcelableArrayListExtra("product"); // get the list ,只需选择要比较的列(4个地址),您的代码会更好。它应该在我的sql中有限制子句。当你有太大的nums行时,它可以避免状态不响应。