Mysql - 删除重复项

时间:2012-03-20 11:05:50

标签: mysql sql duplicates

我有一个带有唯一索引的条形码列的表。数据已经在每个条形码的末尾加载了额外的字符(-xx)以防止重复,但是一旦我去掉后缀,就会有很多重复。以下是数据样本:

itemnumber  barcode

17912       2-14
18082       2-1
21870       2-10
29219       2-8

然后我创建了两个临时表,marty和manny,两者都带有itemnumber和剥离的条形码。因此,两个表都包含

itemnumber  barcode

17912       2
18082       2
21870       2
29219       2

我尝试删除除marty表中条形码“2”的​​第一个条目(以及其他所有条形码)以外的所有条目。我希望然后用正确的第一个条目更新原始表,用户可以在应用程序中及时修复重复项。

所以,这是我的查询,除了每个条形码的marty表中的第一个条目

DELETE FROM marty
  WHERE itemnumber NOT IN
    (SELECT MIN(itemnumber) FROM manny GROUP BY barcode)

marty和manny有130,000行。查询花了24个小时,然后没有正确完成。与服务器的连接崩溃,查询未执行所有更新。

有没有更好的方法来解决这个问题,而不是我们的子查询,我认为这会导致延迟?而且,如果有这么多记录,那么小组也可能会减慢速度。

由于

4 个答案:

答案 0 :(得分:2)

另一个变体:此变体在没有任何临时表的情况下可以删除重复项:

 Delete m1
 From Marty m1
 join Marty m2 
    on m1.barcode = m2.barcode 
    and m1.itemnumber > m2.itemnumber

答案 1 :(得分:1)

使用IN非常大的集合时,MySQL的速度非常慢。脚本替代:

使用脚本构建一个长itemnumber = X OR itemnumber = y OR itemnumber = z子句(块大小~1000)和INSERT匹配的行(即前一个查询中不会DELETE d的行)进入一个新表,TRUNCATE现有的并将新表的内容加载回旧INSERT INTO marty SELECT * FROM marty_tmp

您可能希望锁定该表或在最终TRUNCATEINSERT的交易中运行。

修改

  • 从脚本中查询SELECT MIN(itemnumber) FROM manny GROUP BY barcode,将结果存储在desiredItemNumbers数组
  • 获取1000个desiredItemNumbers批次并构建此查询:INSERT INTO manny_tmp SELECT * FROM manny WHERE itemnumber = desiredItemNumbers[0] OR itemnumber = desiredItemNumbers[1] ...。重新运行此查询,直到您用尽了desiredItemNumbers数组(n.b.最后一个查询可能少于1000个desiredItemNumbers)。
  • 您现在有一个表格,其中包含您剩下的DELETE剩余的结果,因此交换martymarty_tmp表的内容。
  • TRUNCATE marty
  • INSERT INTO marty SELECT * FROM marty_tmp

答案 2 :(得分:1)

这是一个避免使用NOT IN的两阶段方法。它也不使用临时表“manny”。首先,将“marty”加入到自身中以选择itemnumber!= min(itemnumber)的行。使用UPDATE将这些行的barcode设置为NULL。第二次使用DELETE然后删除在第一阶段标记的所有行。

对于此示例,我将“marty”的barcode列拆分为两列;可以使用原始格式的表进行一些修改(需要动态分割列值)。

select * from marty;
+------------+---------+---------+
| itemnumber | barcode | subcode |
+------------+---------+---------+
|      17912 |       2 |      14 |
|      18082 |       2 |       1 |
|      21870 |       2 |      10 |
|      29219 |       2 |       8 |
|      30133 |       3 |       5 |
|      30134 |       3 |       7 |
|      30139 |       3 |       9 |
|      30142 |       3 |      12 |
+------------+---------+---------+
8 rows in set (0.00 sec)

UPDATE
  (marty m1
   JOIN
     (SELECT barcode,
             MIN(itemnumber) AS itemnumber
      FROM marty
      GROUP BY barcode) m2
   USING(barcode))
SET m1.barcode = NULL WHERE m1.itemnumber != m2.itemnumber;

mysql> select * from marty;
+------------+---------+---------+
| itemnumber | barcode | subcode |
+------------+---------+---------+
|      17912 |       2 |      14 |
|      18082 |    NULL |       1 |
|      21870 |    NULL |      10 |
|      29219 |    NULL |       8 |
|      30133 |       3 |       5 |
|      30134 |    NULL |       7 |
|      30139 |    NULL |       9 |
|      30142 |    NULL |      12 |
+------------+---------+---------+
8 rows in set (0.00 sec)

DELETE FROM marty WHERE barcode IS NULL;

答案 3 :(得分:0)

如果您正在创建临时表,那么如何基于以下内容使用“INSERT INTO”或“CREATE TABLE ... AS ...”构建表:

SELECT MIN(itemnumber) AS itemnumber, barcode
  FROM marty
  GROUP BY barcode