根据另一个 table 的内容删除 MySQL 中的重复行

Delete duplicate rows in MySQL based on contents of another table

我有一个 MySQL (5.4) table 有一些行有重复的字段(有时 2-5 个副本),我想删除,只留下一个。但这并不只是选择最高或最低的 id 那么简单。我要删除的重复项是那些在另一个 table.

中没有相应条目的重复项

Table tb_email_to_members 具有 email_id(自动递增)和 email_address(以及其他不相关的字段)。例如:

email_id    email_address
-------------------------
1           arnold@foo.com
2           foo@foo.com
3           foo@foo.com
4           foo@foo.com
5           jeanluc@foo.com

Table tb_tx 具有 tx_id(自动递增)和 frn_email_id(以及其他不相关的字段),其中 tb_tx.frn_email_id 匹配达到 tb_email_to_members.email_id。例如:

tx_id         frn_email_id
--------------------------
100           5
101           2
102           19
103           19
104           19
105           1

我想删除 email_addresstb_email_to_members 中重复一次或多次的行,但前提是 tb_tx 中没有包含 frn_email_id 的行email_id 来自 tb_email_to_members。我需要确保保留一行重复项,即使其中 none 在 tb_tx 中有相应的条目。在上面的示例中,我想从 tb_email_to_members 中删除第 3 行和第 4 行,因为 tb_tx.

中只存在第 2 行

(本质上,tb_email_to_members 将电子邮件地址映射到另一个 table 中的用户帐户,而 tb_tx 将订单映射到来自 tb_email_to_members 的那些电子邮件地址。)

我可以很容易地找到重复项,并且我看到了很多用于删除重复项的代码,但没有根据另一个 table 的查找失败而只需要删除某些重复项的调整。建议?

这应该可以回答您的问题:

DELETE FROM tb_email_to_members WHERE email_id NOT IN (select frn_email_id FROM tb_tx);

我认为这正是您想要的。它仅删除 tb_email_to_members 中的重复条目,其中 tb_tx 中没有相关行,并保留所有原始条目。

请注意,您没有提及从 tb_tx 中删除条目,因此 table 中的重复项将被单独保留(在您的示例内容中,第 102-104 行)。

我在这里使用的方法基本上是用伪代码实现的:

从 table 的 id_col 中删除 ( SUBQUERY selects 一个 id 列并应用 WHERE 过滤器确保每个 id NOT in ( 另一个 SUBQUERY,每个分组中只有 select 的第一项,与第一个 SUBQUERY 非常相似 ) )

那里(第 2 行)还有另一个 SUBQUERY 将整个事情包装起来,这可以防止 MySQL 抱怨你不能 select 从和修改 table同时.

注意:如果您的数据集很大,这可能会很慢。在手动删除大量数据之前备份您的 table!

我知道这是一个相当复杂的查询,但它确实有效。

DELETE FROM tb_email_to_members WHERE email_id IN (
  SELECT * FROM (
    SELECT ids.eid FROM (
      SELECT tb_email_to_members.email_id AS eid, dup.email_id AS eid2, dup.email_address, frn_email_id
      FROM tb_email_to_members
      LEFT JOIN (
        SELECT email_id, email_address FROM tb_email_to_members
        GROUP BY email_address
        HAVING count(email_id) > 1) AS dup
        ON tb_email_to_members.email_address = dup.email_address
      INNER JOIN tb_tx tx ON dup.email_id = tx.frn_email_id
    ) AS ids
    WHERE ids.eid NOT IN (
      SELECT tb_email_to_members.email_id AS eid FROM tb_email_to_members
      LEFT JOIN (
        SELECT email_id, email_address FROM tb_email_to_members
        GROUP BY email_address
        HAVING count(email_id) > 1) AS dup
        ON tb_email_to_members.email_address = dup.email_address
      INNER JOIN tb_tx tx ON dup.email_id = tx.frn_email_id
      GROUP BY dup.email_id
    )
  ) AS foo
)

@MHardwick 和@ShadowRay 几乎做对了。以下内容还检查以确保电子邮件在 tb_email_to_members

中存在 more tan once
DELETE FROM tb_email_to_members
  WHERE email_id NOT IN (SELECT frn_email_id FROM tb_tx)
    AND email_address IN (SELECT email_address FROM tb_email_to_members GROUP BY email_address HAVING COUNT(email_address) > 1);

显然,将 DELETE 更改为 SELECT * 将向您显示您要删除的内容。

了解 tb 是花絮的缩写?

加分