【问题标题】:Delete all the duplicates except one删除除一个以外的所有重复项
【发布时间】:2012-09-19 03:34:04
【问题描述】:

我们有一个表 business_usersuser_idbusiness_id,我们有重复。 如何编写一个查询来删除除一个之外的所有重复项?

【问题讨论】:

  • 点击相关问题。几周前,当我在寻找这个问题时,我发现了很多想法可以尝试。我混合并匹配了一些以获得所需的结果。
  • 这张表上是否有任何主键或其他唯一约束?还是 user_idbusiness_id 是唯一的列,这样整个行都重复了?
  • 是的,有一个id 作为主键

标签: mysql sql


【解决方案1】:

完全相同的行

如果您想避免完全相同的行,正如我最初理解您的问题那样,那么您可以选择一个单独的表中的唯一行并从中重新创建表数据。

CREATE TEMPORARY TABLE tmp SELECT DISTINCT * FROM business_users;
DELETE FROM business_users;
INSERT INTO business_users SELECT * FROM tmp;
DROP TABLE tmp;

但是,如果有任何外键约束引用此表,请小心,因为临时删除行可能会导致其他地方的级联删除。

引入唯一约束

如果您只关心 user_idbusiness_id 对,您可能希望避免在将来引入重复项。您可以将现有数据移动到临时表中,添加约束,然后将表数据移回,忽略重复项。

CREATE TEMPORARY TABLE tmp SELECT * FROM business_users;
DELETE FROM business_users;
ALTER TABLE business_users ADD UNIQUE (user_id, business_id);
INSERT IGNORE INTO business_users SELECT * FROM tmp;
DROP TABLE tmp;

以上答案基于this answer。关于外键的警告与上一节一样适用。

一次性移除

如果您只想执行单个查询,而不以任何方式修改表结构,并且您有一个主键id 标识每一行,那么您可以尝试以下操作:

DELETE FROM business_users WHERE id NOT IN
    (SELECT MIN(id) FROM business_users GROUP BY user_id, business_id);

this answer 之前提出了类似的想法。

如果上述请求失败,由于不允许在同一步骤中读取和删除表,您可以再次使用临时表:

CREATE TEMPORARY TABLE tmp
SELECT MIN(id) id FROM business_users GROUP BY user_id, business_id;
DELETE FROM business_users WHERE id NOT IN (SELECT id FROM tmp);
DROP TABLE tmp;

如果您愿意,您仍然可以在以这种方式清理数据后引入唯一性约束。为此,请执行上一节中的 ALTER TABLE 行。

【讨论】:

  • 我喜欢最后一个,但我明白了你不能在 FROM 子句中指定目标表 'business_users' 进行更新
  • @Trace,我添加了一个可以避免这个问题的版本。
  • 只是出于好奇,对于一次性删除,为什么第一个示例有SELECT MIN(id) FROM,第二个有SELECT MIN(id) id FROM(第二个有两个'id')?
  • @Pete:第二个中的MIN(id) idMIN(id) AS id 的缩写:它指定了列的名称,因此结果表中的列不是字面意义上的MIN(id)这将非常混乱且难以输入。在第一个查询中,列名无关紧要,因为子查询只是作为一个集合使用。
【解决方案2】:

由于您有一个主键,您可以使用它来选择要保留的行:

delete from business_users
where id not in (
    select id from (
        select min(id) as id -- Make a list of the primary keys to keep
        from business_users
        group by user_id, business_id -- Group by your duplicated row definition
    ) as a -- Derived table to force an implicit temp table
);

这样,您就不需要创建/删除临时表等(implicit one 除外)。

您可能希望对 user_id, business_id 设置唯一约束,这样您就不必再担心这个问题了。

【讨论】:

  • 看起来不错,但我明白了你不能在 FROM 子句中指定目标表 'business_users' 进行更新
  • @Trace,对不起...我已经更新以使子查询在这种情况下在 mysql 中工作。
  • 注意:我读过关于使用子查询的相同建议,但在我自己的测试设置中失败了。似乎是因为我也创建了business_users 作为临时表来进行测试。在这种情况下,错误表述为Can't reopen table: 'business_users',这几乎是相同的问题(至少在我看来),但不能通过引入另一个子查询来避免。
  • 有趣。这是我的test sqlfiddle。你能给我们一个更好的定义你现有的抛出错误的模式吗?也许您需要将希望保留的主键放入临时表中。
猜你喜欢
  • 2020-04-12
  • 2021-01-28
  • 1970-01-01
  • 2013-03-16
  • 1970-01-01
  • 1970-01-01
  • 2012-02-24
  • 2018-12-04
  • 1970-01-01
相关资源
最近更新 更多