【问题标题】:MySQL delete duplicate records but keep latestMySQL删除重复记录但保持最新
【发布时间】:2011-08-31 17:16:00
【问题描述】:

我有独特的 idemail 字段。电子邮件被重复。我只想保留所有重复项的一个电子邮件地址,但使用最新的id(最后插入的记录)。

我怎样才能做到这一点?

【问题讨论】:

  • 为什么不直接防止重复项被插入到表中呢?使电子邮件成为唯一索引
  • @sra,伙计,我有 20 个线程仍然打开,但不是数据库专家,所有这些都有一些条件使得查询很难理解,因此我创建了一个新线程,其中包含很多道歉@tofutim:蒂姆,我们从第三方获得了这些数据,所以不能选择太多。因此,现在清理。 :-)

标签: mysql duplicates


【解决方案1】:

假设您的表 test 包含以下数据:

  select id, email
    from test;

ID                     EMAIL                
---------------------- -------------------- 
1                      aaa                  
2                      bbb                  
3                      ccc                  
4                      bbb                  
5                      ddd                  
6                      eee                  
7                      aaa                  
8                      aaa                  
9                      eee 

所以,我们需要找到所有重复的邮件并删除它们,但最新的 id。
在这种情况下,aaabbbeee 是重复的,所以我们要删除 ID 1、7、2 和 6。

要做到这一点,首先我们需要找到所有重复的电子邮件:

      select email 
        from test
       group by email
      having count(*) > 1;

EMAIL                
-------------------- 
aaa                  
bbb                  
eee  

然后,从这个数据集中,我们需要为这些重复的电子邮件中的每一个找到最新的 id:

  select max(id) as lastId, email
    from test
   where email in (
              select email 
                from test
               group by email
              having count(*) > 1
       )
   group by email;

LASTID                 EMAIL                
---------------------- -------------------- 
8                      aaa                  
4                      bbb                  
9                      eee                                 

最后,我们现在可以删除所有这些 ID 小于 LASTID 的电子邮件。所以解决办法是:

delete test
  from test
 inner join (
  select max(id) as lastId, email
    from test
   where email in (
              select email 
                from test
               group by email
              having count(*) > 1
       )
   group by email
) duplic on duplic.email = test.email
 where test.id < duplic.lastId;

我现在没有在这台机器上安装 mySql,但应该可以工作

更新

上面的delete可行,但我找到了一个更优化的版本:

 delete test
   from test
  inner join (
     select max(id) as lastId, email
       from test
      group by email
     having count(*) > 1) duplic on duplic.email = test.email
  where test.id < duplic.lastId;

您可以看到它删除了最旧的重复项,即 1、7、2、6:

select * from test;
+----+-------+
| id | email |
+----+-------+
|  3 | ccc   |
|  4 | bbb   |
|  5 | ddd   |
|  8 | aaa   |
|  9 | eee   |
+----+-------+

另一个版本,是Rene Limon提供的删除

delete from test
 where id not in (
    select max(id)
      from test
     group by email)

【讨论】:

  • 嗨,何塞,这很有教育意义。谢谢你。然而,MySQL 抛出了一个错误。它在内部连接(第二行)附近有一些语法错误。不过,并没有说明错误。
  • @Khuram 坚持几个小时,直到我回到家并在我的机器上检查一下
  • 可能是:DELETE FROM test WHERE id NOT IN (SELECT MAX(id) FROM test GROUP BY email)
  • 我收到错误Table 'test' is specified twice, both as a target for 'DELETE' and as a separate source for data
  • @HamSam 尝试使用嵌套子查询,以便 mySql 实现它并且不再使用“同一张表”,例如,使用delete from test where id not in ( SELECT * FROM (select max(id) from test group by email) AS S)(我已经添加了大写部分)
【解决方案2】:

试试这个方法

DELETE t1 FROM test t1, test t2 
WHERE t1.id > t2.id AND t1.email = t2.email

【讨论】:

  • 创建一个包含 2 列的表:- id(主键和包含重复项的电子邮件)然后运行此查询,您将得到它。这是来自同一个表的自连接,通过保留一份副本来删除重复记录
  • 不知道为什么它隐藏在页面的下方。简单有效?
  • 这真的保持最新吗?最新的有最高的id,看起来这个查询正在删除任何大于其他ids 的id。请参阅@TanvirChowdhury 的答案stackoverflow.com/a/63434018/277267
【解决方案3】:

正确的方法是

DELETE FROM `tablename` 
  WHERE id NOT IN (
    SELECT * FROM (
      SELECT MAX(id) FROM tablename 
        GROUP BY name
    ) 
  )

【讨论】:

  • x 字符的用途是什么?
  • 导致错误 1248 (42000):每个派生表都必须有自己的别名,添加名为 DTAB 的别名的工作方式如下所示:DELETE FROM tablename WHERE id NOT IN (SELECT * FROM (SELECT MAX(id) FROM tablename GROUP BY name) as DTAB)
【解决方案4】:
DELETE 
FROM
  `tbl_job_title` 
WHERE id NOT IN 
  (SELECT 
    * 
  FROM
    (SELECT 
      MAX(id) 
    FROM
      `tbl_job_title` 
    GROUP BY NAME) tbl)

修订版和工作版!!!谢谢@Gaurav

【讨论】:

    【解决方案5】:

    如果要保留 id 值最低的行:

    DELETE n1 FROM 'yourTableName' n1, 'yourTableName' n2 WHERE n1.id > n2.id AND n1.email = n2.email
    

    如果要保留id值最高的行:

    DELETE n1 FROM 'yourTableName' n1, 'yourTableName' n2 WHERE n1.id < n2.id AND n1.email = n2.email
    

    或者这个查询也可能有帮助

    DELETE FROM `yourTableName` 
      WHERE id NOT IN (
        SELECT * FROM (
          SELECT MAX(id) FROM yourTableName 
            GROUP BY name
        ) 
      )
    

    【讨论】:

      【解决方案6】:

      我个人对前两个投票的答案有疑问。这不是最干净的解决方案,但您可以利用临时表来避免 MySQL 通过加入同一个表来删除的所有问题。

      CREATE TEMPORARY TABLE deleteRows;
      SELECT MIN(id) as id FROM myTable GROUP BY myTable.email;
      
      DELETE FROM myTable
      WHERE id NOT IN (SELECT id FROM deleteRows);
      

      【讨论】:

        【解决方案7】:

        我必须说,优化后的版本是一段甜美、优雅的代码,即使在 DATETIME 列上进行比较时,它也能像魅力一样工作。这是我在脚本中使用的,我在其中搜索每个 EmployeeID 的最新合同结束日期:

        DELETE CurrentContractData
          FROM CurrentContractData
          INNER JOIN (
            SELECT
              EmployeeID,
              PeriodofPerformanceStartDate,
              max(PeriodofPerformanceEndDate) as lastDate,
              ContractID
            FROM CurrentContractData
            GROUP BY EmployeeID
            HAVING COUNT(*) > 1) Duplicate on Duplicate.EmployeeID = CurrentContractData.EmployeeID
            WHERE CurrentContractData.PeriodofPerformanceEndDate < Duplicate.lastDate;
        

        非常感谢!

        【讨论】:

          【解决方案8】:
          DELIMITER // 
          CREATE FUNCTION findColumnNames(tableName VARCHAR(255))
          RETURNS TEXT
          BEGIN
              SET @colNames = "";
               SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.columns
                  WHERE TABLE_NAME = tableName
                  GROUP BY TABLE_NAME INTO @colNames;
              RETURN @colNames;
          END // 
          DELIMITER ;
          
          DELIMITER // 
          CREATE PROCEDURE deleteDuplicateRecords (IN tableName VARCHAR(255))
          BEGIN
              SET @colNames = findColumnNames(tableName);
              SET @addIDStmt = CONCAT("ALTER TABLE ",tableName," ADD COLUMN id INT AUTO_INCREMENT KEY;");
              SET @deleteDupsStmt = CONCAT("DELETE FROM ",tableName," WHERE id NOT IN 
                  ( SELECT * FROM ",
                      " (SELECT min(id) FROM ",tableName," group by ",findColumnNames(tableName),") AS tmpTable);");
              set @dropIDStmt = CONCAT("ALTER TABLE ",tableName," DROP COLUMN id");
          
              PREPARE addIDStmt FROM @addIDStmt;
              EXECUTE addIDStmt;
          
              PREPARE deleteDupsStmt FROM @deleteDupsStmt;
              EXECUTE deleteDupsStmt;
          
              PREPARE dropIDStmt FROM @dropIDStmt;
              EXECUTE dropIDstmt;
          
          END // 
          DELIMITER ;
          

          我创建了一个很好的存储过程,用于删除表的所有重复记录,而无需该表上现有的唯一 ID。

          CALL deleteDuplicateRecords("yourTableName");
          

          【讨论】:

            【解决方案9】:

            我想删除表中基于多列的重复记录,所以这种方法对我有用,

            第 1 步 - 从重复记录中获取最大 ID 或唯一 ID

            select *  FROM ( SELECT MAX(id) FROM table_name 
            group by travel_intimation_id,approved_by,approval_type,approval_status having 
            count(*) > 1
            

            第 2 步 - 从表中获取单个记录的 ID

            select *  FROM ( SELECT id FROM table_name 
            group by travel_intimation_id,approved_by,approval_type,approval_status having 
            count(*) = 1
            

            第 3 步 - 从删除到排除上述 2 个查询

            DELETE FROM `table_name` 
            WHERE 
            id NOT IN (paste step 1 query) a //to exclude duplicate records
            and 
            id NOT IN (paste step 2 query) b // to exclude single records
            

            最终查询:-

            DELETE FROM `table_name` 
            
            WHERE id NOT IN (
            
            select *  FROM ( SELECT MAX(id) FROM table_name 
            group by travel_intimation_id,approved_by,approval_type,approval_status having 
            count(*) > 1) a 
            )
            and id not in (
            
            select *  FROM ( SELECT id FROM table_name 
            group by travel_intimation_id,approved_by,approval_type,approval_status having 
            count(*) = 1) b
            );
            

            通过此查询,只会删除重复的记录。

            【讨论】:

              猜你喜欢
              • 2020-12-19
              • 2019-12-20
              • 1970-01-01
              • 2017-02-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-12-29
              相关资源
              最近更新 更多