MYSQL去重并删除数据最少的重复行

Posted

技术标签:

【中文标题】MYSQL去重并删除数据最少的重复行【英文标题】:MYSQL Deduplicate and remove the duplicate row with least data 【发布时间】:2013-08-01 09:16:10 【问题描述】:

我正在开发一个 mysql 数据库,它包含以下三列:电子邮件、姓名、姓氏。

我需要做的是对我知道我可以使用这样一个功能的电子邮件进行重复数据删除(这个查询只是为了排序而不是删除):

select distinct emails, name, surname from emails;

select emails, name, surname from emails group by emails having count(*) >= 2;

但是,我还需要确保当发现重复的电子邮件地址时,保留的是具有姓名和/或姓氏值的电子邮件地址。

例如:

|标识 |电子邮件              |姓名 |姓氏 | |1 | bob@bob.com |鲍勃    |保尔森   | |2 | bob@bob.com | | |

在这种情况下,我想保留第一个结果并删除第二个结果。

我一直在研究使用“case”或“if”语句,但没有使用这些语句的经验。我尝试用这些语句扩展上述功能,但无济于事。

谁能指出我正确的方向?

PS:表中的第一列是一个自动递增的 id 值,以防万一

更新 1:到目前为止,@Bohemian 下面的答案效果很好,但在一种情况下失败了,其中有一个重复的电子邮件地址,其中一行有一个名字但没有姓氏,而在下一行它没有名字但有一个姓。它将保留这两个记录。需要编辑的只是删除这两条记录中的一条,不管是哪一条。

更新 2:@Bohemian 的答案很棒,但经过更多测试后,我发现它有一个根本缺陷,即它仅在名称和姓氏字段包含数据的重复电子邮件行(如第一个上表中的条目)。如果有重复的电子邮件,但没有一行填写姓名和姓氏字段,则所有这些行都将被忽略且不会重复数据删除。

此查询的最后一步是确定如何删除不满足当前必要条件的重复项。如果一行只有名字,而另一行只有姓氏,那么删除哪个并不重要,因为要保留电子邮件是很重要的。

【问题讨论】:

一行有名字,下一行有姓怎么办? 这是您要定期做的事情,还是一次性修复? 在这种情况下,我认为最好的解决方案是将两者连接起来,尽管我认为这会使事情复杂化很多。 这是一次性的,但可能每月使用一次以在数据库更新时清除数据库。 【参考方案1】:

您可以使用这个 DELETE 查询,它是通用的,可以轻松调整以支持更多字段:

DELETE tablename.*
FROM
  tablename LEFT JOIN (
    SELECT MIN(id) min_id
    FROM
      tablename t INNER JOIN (
        SELECT
          emails, MAX((name IS NOT NULL) + (surname IS NOT NULL)) max_non_nulls
        FROM
          tablename
        GROUP BY
          emails) m
      ON t.emails=m.emails
         AND ((t.name IS NOT NULL) + (t.surname IS NOT NULL))=m.max_non_nulls
    GROUP BY
      t.emails) ids
  ON tablename.id=ids.min_id
WHERE
  ids.min_id IS NULL

请看小提琴here。

此查询返回每封电子邮件的最大非空字段数:

SELECT
  emails,
  MAX((name IS NOT NULL) + (surname IS NOT NULL)) max_non_nulls
FROM
  tablename
GROUP BY
  emails

然后我将这个查询与表名连接起来,以获取每封具有最大数量非空字段的电子邮件的最小 ID:

SELECT MIN(id) min_id
FROM
  tablename t INNER JOIN (
    SELECT
      emails, MAX((name IS NOT NULL) + (surname IS NOT NULL)) max_non_nulls
    FROM
      tablename
    GROUP BY
      emails) m
  ON t.emails=m.emails
     AND ((t.name IS NOT NULL) + (t.surname IS NOT NULL))=m.max_non_nulls
GROUP BY
  t.emails

然后我将删除所有具有此查询未返回的 ID 的行。

【讨论】:

非常感谢您,查询运行良好。也感谢您所采取的步骤的解释,它有很大帮助。我可以给你一杯啤酒吗?【参考方案2】:

这很容易用 mysql 的multiple-table delete syntax:

delete b
from mytable a
join mytable b
  on a.email = b.email
  and a.id != b.id
where a.name is not null
and a.surname is not null

【讨论】:

非常感谢@Bohemian 查询运行良好。唯一不起作用的情况是,一行中的重复电子邮件有名字但没有姓氏,而另一行没有名字但有用户名。在这种情况下,两者都被保留。在这种情况下,保留哪个并不重要,但必须离开。 是的,有一点逻辑错误 - 但很容易修复 ;-) @Strawberry 您愿意分享您的想法吗? 我也刚刚注意到,当有重复并且两行都没有名字或姓氏时,它们也会被保留。【参考方案3】:

删除电子邮件 ID 重复的记录

delete 
    from duplicate_email where id in(
        select id from (
            select id, email from duplicate_email group by email having count(id) > 1) as id
    )

但是有一个问题,您可以删除那些只有一封重复电子邮件的记录,即两封相同的电子邮件,但如果有三个或更多,您可以重复此查询,直到您删除零记录

【讨论】:

以上是关于MYSQL去重并删除数据最少的重复行的主要内容,如果未能解决你的问题,请参考以下文章

VBA自动过滤复制值,去重并粘贴到其他工作表中

excel怎么去重

mysql之数据去重并记录总数

mysql之数据去重并记录总数

SQL按照某一列数据去重并显示整行信息

navicat中查重并删除