MySQL重复行

时间:2009-03-20 16:58:12

标签: sql mysql

我有一张包含重复信息的表:Id,姓名,姓氏,出生,PersonalKey,个人信息,方向,来源。

来源告诉我信息的来源。

重复的信息具有唯一ID,我需要删除欺骗信息。 但是,我优先考虑一些源信息,我需要保留这些信息,另一个信息将被删除。

其他的事情是,另一个Source信息有一些信息,我想要留下的信息没有,所以我需要将PersonalKey重新填充到剩下的那个并删除重复的那个。

表名为Pruebas

---Id, Name, Firstname, Lastname, Birth, RFC, Source, PersonalKey---
---2,Juan,Garcia,Escobeddo,1983-08-04,GAED87393, DRV484930, 34233--
---3,Juan,Garcia,Escobedo,1987-08-04,GAED87393, FIN484930, --
---4,Juan,Garcia,Escobedo,1987-08-04,GAED87393, SA484930, --

如你所见:

  • ID是唯一的
  • 重复名称,名字和姓氏
  • id 2具有PersonalKey值,但3和4不具有
    • 我希望保留带有'FIN%'源的那个并删除其他的源,但首先我需要确保剩下的行获取PersonalKey值(IOW,我不想丢失PersonalKey值)。

提前致谢。

4 个答案:

答案 0 :(得分:3)

我会在这个查询上运行一个游标(使用MySQL SP编程语言,Java,Python,.NET):

select Name, Firstname, Lastname, count(1)
  from Pruebas
 group by Name, Firstname, Lastname
having count(1) > 1

然后,在光标返回的行上,只需执行以下操作:检查FIN%实例,检查PersonalKey的存在,并相应地更新。

对于光标上的每一行,您可以使用以下命令打开另一个光标:

select *
  from Pruebas
 where Name = the_Name
   and Firstname = the_Firstname
   and Lastname = the_Lastname

现在,您将拥有一个内部光标,其中包含您要修改的所有行。如果它是您需要的,请保留它并使用您提到的KEY值更新它。否则,删除它。

在Oracle中,您可以在一个查询中完成您想要的任务,但我不认为您将获得与此方法相同的性能。

希望它有所帮助。

答案 1 :(得分:3)

我能想到的最直接的解决方案是将PersonalKey复制到其他重复行,然后删除所有与'FIN%'不匹配的行。

UPDATE Pruebas p1 JOIN Pruebas p2
 ON (SOUNDEX(CONCAT(p1.Name, p2.Firstname, p3.Lastname)) 
   = SOUNDEX(CONCAT(p2.Name, p2.Firstname, p2.Lastname)))
SET p1.PersonalKey = p2.PersonalKey
WHERE p2.PersonalKey IS NOT NULL;

DELETE FROM Pruebas WHERE Source NOT LIKE 'FIN%';

我正在使用SOUNDEX()显示联接的近似匹配表达式。


我从其他评论中看到,你已经离开了很多变化和不确定性。在这种情况下,没有办法自动清理和重复数据删除 - 或者至少自动清理将比手动执行更复杂,更难做到。


请注意,查询需要花费很多时间:是的,实际上预计不会有效。 JOIN表达式不是 sargable - 也就是说,它无法利用索引。您可以通过添加额外的列来物理存储名称,名字,姓氏的SOUNDEX()值,从而提高效率。然后在该列上创建索引。

SOUNDEX()无法保证找到所有可能的拼写错误。您正面临无法完全自动执行的数据清理任务。 任何数据清理解决方案都需要手动操作。

答案 2 :(得分:2)

我会做这样的事情:

  

创建表Pruebas_new
           SELECT * FROM Pruebas
           GROUP BY名称,名字,姓氏
           拥有像'FIN%'的来源;

如果您需要更快,可以使用临时表重写并覆盖原始表中的内容,但这样可以以最简单的方式获取所需的数据。

答案 3 :(得分:1)

很抱歉答案延迟了。过去几天我有点忙。

以下是基于以下假设的答案:

1)您将通过其他一些机制清理名称拼写问题(您提到过要在原始问题的评论中使用正则表达式清理它)。

2)可以使用名字,姓氏和出生来识别DUP集(您在对原始问题的评论中提到了这一点。)

3)Firstname,Lastname和Birth不能为NULL。

4)你不能在DUP集中拥有多条FIN记录(你在对原始问题的评论中提到过这一点)。

如果上述任何假设无效,则必须修改我的答案。

以下是要采取的步骤:

1)更新所有FIN记录以通过非FIN记录复制PersonalKey:

    UPDATE Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
       SET p1.PersonalKey = p2.PersonalKey
     WHERE p1.Source like 'FIN%'
       AND p1.PersonalKey is null
       AND p2.PersonalKey is not null;

2)删除我们有FIN记录的所有非FIN记录:

    DELETE p2
      FROM Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
     WHERE p1.Source like 'FIN%'
       AND p2.Source not like 'FIN%';

此时所有带有FIN记录的DUP都已清除,因此只剩下FIN记录。

3)如果我们决定继续使用所有其他病例的DRV记录。我们需要将PersonalKey从另一条记录复制到DRV记录中:

    UPDATE Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
       SET p1.PersonalKey = p2.PersonalKey
     WHERE p1.Source like 'DRV%'
       AND p1.PersonalKey is null
       AND p2.PersonalKey is not null;

4)删除我们拥有DRV记录的所有非DRV记录:

    DELETE p2
      FROM Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
     WHERE p1.Source like 'DRV%'
       AND p2.Source not like 'DRV%';

此时,所有具有DRV记录的DUP都已清除,因此只剩下DRV记录。

如果唯一的其他记录类型是SA记录,则不应再有DUP,我们就完成了。

5)如果我们想要选择填写了最多信息的记录,或者如果我们完成了3和4并且还有多个记录类型仍然留下导致DUP。我们需要将具有DUP集的任何记录中的PersonalKey复制到任何没有所有非FIN记录的记录中:

    UPDATE Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
       SET p1.PersonalKey = p2.PersonalKey
     WHERE p1.Source not like 'FIN%'
       AND p1.PersonalKey is null
       AND p2.PersonalKey is not null;

6)删除除信息最多的记录以外的所有记录(由info_score计算列定义):

    DELETE p5
      FROM Pruebas p5
INNER JOIN (SELECT p3.Firstname
                 , p3.Lastname
                 , p3.Birth
                 , MIN(p3.Id) AS min_id
              FROM Pruebas p3
        INNER JOIN (SELECT p1.Firstname
                         , p1.Lastname
                         , p1.Birth
                         , count(*) AS c
                         , MAX((p1.Name is not null) + (p1.RFC is not null) + (p1.Source is not null) + (p1.PersonalKey is not null)) AS info_score
                      FROM Pruebas p1
                  GROUP BY p1.Firstname
                         , p1.Lastname
                         , p1.Birth 
                    HAVING count(*) > 1) p2
                ON p3.Firstname = p2.Firstname
               AND p3.Lastname = p2.Lastname
               AND p3.Birth = p2.Birth
               AND ((p3.Name is not null) + (p3.RFC is not null) + (p3.Source is not null) + (p3.PersonalKey is not null)) = p2.info_score
          GROUP BY p3.Firstname
                 , p3.Lastname
                 , p3.Birth) p4
        ON p4.Firstname = p5.Firstname
       AND p4.Lastname = p5.Lastname
       AND p4.Birth = p5.Birth
       AND p4.min_id <> p5.Id;

此时所有DUP都已折叠,如果可用,则保存PersonalKey,如果存在则保存FIN记录,否则保存DRV记录或信息最多的记录。

如果您对上述任何问题有任何疑问,请与我们联系。

希望它有所帮助,

-Dipin