2

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

         }
4

6 回答 6

6

你有很多选择。

让数据库完成工作

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

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;

这样做的好处是您可以在删除源表之前验证您的新表是否正确。缺点是它占用了两倍的空间并且执行起来(相对)慢。

让数据库完成工作#2

您还可以通过执行以下操作来实现您想要的结果:

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)

不要在数据库之外做这种事情

尤其是 4000 万行在 db 之外执行此类操作可能需要大量时间,并且可能根本无法完成。任何留在数据库中的解决方案都会更快、更健壮。

于 2012-08-19T20:32:28.640 回答
2

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

但是从代码来看,你想要的是:“如果一组add1、add2、add3、add4重复,则DELETE ALL COPIES WITH ID < 1000”。在这种情况下,使用 INSERT IGNORE 从表复制到另一个表不会满足您的要求 - 甚至可能保留 ID 较低的行并丢弃后续行。

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

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

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

按此顺序在 add1、add2、add3、add4 和 ID 上的(非唯一,可能只是临时的)索引将极大地提高性能。

于 2012-08-19T20:43:28.933 回答
2

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

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

因此,您将获得所有重复的 ID。现在使用删除查询删除它们。不要使用“IN”,而是使用“OR”运算符,因为“IN”比“OR”慢。

于 2012-08-19T22:06:27.213 回答
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;
于 2012-08-19T20:35:17.917 回答
0

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

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

也许这样就可以解决了。

于 2012-08-19T20:32:27.143 回答
0

如果您不使用select *,您的代码会更好,只选择要比较的列(4 个地址)。它应该在我的 sql 中有限制子句。当你有太多这样的 nums 行时,它可以避免状态不响应。

于 2015-07-11T14:18:59.533 回答