【问题标题】:Delete duplicate entries while keeping one删除重复条目,同时保留一个
【发布时间】:2022-01-17 08:38:35
【问题描述】:

我有一个表,但它没有唯一的 ID 或主键。

总共有 3 列。

name user_id role_id
ben 1 2
ben 1 2
sam 1 3

我想删除一个名为 Ben 的条目。

所以输出应该是这样的

name user_id role_id
ben 1 2
sam 1 3

大多数示例显示删除具有 ID 或主键的重复条目。但是,如何在删除其他条目的同时保留一个条目?

使用以下查询我能够得到重复的行

SELECT name, user_id, role_id, count(*) FROM some_table
GROUP BY name, user_id, role_id
HAVING count(*) > 1

为了澄清,我希望删除这些行。

最好不要创建新表。

【问题讨论】:

  • 为所有列创建一个唯一键。
  • 请说明您是想从数据库中实际删除一行,还是不让它出现在选择中。请澄清是什么使一行重复:所有列都是相同的吗?如果不是,请说明您希望如何选择要保留的行。
  • 如果您只想选择没有重复的行,select distinct name, user_id, role_id from some_table
  • This might help。阅读使用 ROW_NUMBER() 函数的示例。
  • 更新了主要问题。我实际上想删除行本身。

标签: mysql sql


【解决方案1】:

请注意,没有主键并没有保存任何内容; mysql(至少使用innodb)需要一个主键,如果你没有主键,它将创建一个隐藏的主键。所以我会先添加一个主键:

alter table some_table add id serial primary key;

然后您可以使用以下方法轻松删除重复项:

delete a from some_table a join some_table b on a.name=b.name and a.user_id=b.user_id and a.role_id=b.role_id and b.id < a.id;

【讨论】:

    【解决方案2】:

    如果您不必担心其他用户访问该表 -

    CREATE TABLE `new_table` AS
    SELECT DISTINCT `name`, `user_id`, `role_id`
    FROM `old_table`;
    
    RENAME TABLE
        `old_table` TO `backup`,
        `new_table` TO `old_table`;
    

    或者您可以使用重复查询来输出大量单行删除查询 -

    SELECT
        `name`,
        `user_id`,
        `role_id`,
        COUNT(*),
        CONCAT('DELETE FROM some_table WHERE name=\'', `name`, '\' AND user_id=\'', `user_id`, '\' AND role_id=\'', `role_id`, '\' LIMIT 1;') AS `delete_stmt`
    FROM `some_table`
    GROUP BY `name`, `user_id`, `role_id`
    HAVING COUNT(*) > 1;
    

    或者您可以临时添加一个 SERIAL 列,然后在删除后将其删除 -

    ALTER TABLE `some_table` ADD COLUMN `temp_id` SERIAL;
    
    DELETE `t1`.* 
    FROM `some_table` `t1`
    LEFT JOIN (
        SELECT MIN(`temp_id`) `min_temp_id`
        FROM `some_table`
        GROUP BY `name`, `user_id`, `role_id`
    ) `t2` ON `t1`.`temp_id` = `t2`.`min_temp_id`
    WHERE `t2`.`min_temp_id` IS NULL;
    
    ALTER TABLE `some_table` DROP COLUMN `temp_id`;
    

    【讨论】:

    • 如果是大表,移动整个表或添加列是比从表中删除子集并重新插入更昂贵的操作
    • @DetroitMike 我完全同意。上下文就是一切。如果这是一个非常大的表,那么创建它的 DISTINCT 副本可能会带来很大的开销,特别是如果它是一个包含大量文本内容的表。我并没有建议您的回答有任何问题,只是提出了替代解决方案。
    【解决方案3】:

    我会把重复的记录放到另一个表中。

    SELECT  
       name, 
       user_id, 
       role_id
       INTO some_new_table
    FROM some_table
    GROUP BY name, user_id, role_id
    HAVING count(*) > 1
    
    

    然后您可以从源表中删除这些记录

    DELETE a
    FROM some_table a
    INNER JOIN some_new_table b
      ON a.name = b.name
        AND a.user_id = b.user_id
        AND a.role_id = b.role_id
    

    最后您可以将重复数据插入到您的表中。

    INSERT INTO some_table 
    SELECT 
      name, 
      user_id, 
      role_id
    FROM some_new_table 
    
    

    如果重复数据的数量非常大,您也可以使用重复数据创建一个新表。截断\删除旧表,然后从新表中插入\重命名。

    【讨论】:

    • 你不会在 some_new_table 中有重复的值吗?我认为您需要在第一个查询中使用 DISTINCT。
    • 否 - 因为您正在按表中的所有列进行分组,这与做 distinct 相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-18
    • 2014-06-23
    • 1970-01-01
    • 2013-12-30
    相关资源
    最近更新 更多