【问题标题】:Removing duplicate SQL records to permit a unique key删除重复的 SQL 记录以允许唯一键
【发布时间】:2010-05-18 22:28:14
【问题描述】:

我在 MYSQL 数据库中有一个表('sales'),它理应强制执行唯一约束以防止重复。首先删除欺骗并设置约束被证明有点棘手。

表结构(简化):

  • 'id (unique, autoinc)'
  • product_id

目标是强制 product_id 的唯一性。我要应用的重复数据删除策略是删除除最近创建的所有重复记录之外的所有重复记录,例如:最高 id。

或者换句话说,我想只删除重复记录,不包括以下查询匹配的 id,同时还保留现有的非重复记录:

select id 
  from sales s  
inner join (select product_id, 
                   max(id) as maxId 
              from sales 
          group by product_id 
            having count(product_id)  > 1) groupedByProdId on s.product_id 
                                                          and s.id = groupedByProdId.maxId

我在两个方面都在努力解决这个问题 - 编写查询以选择要删除的正确记录,然后是 MYSQL 中的约束,其中 DELETE 的子选择 FROM 子句不能引用要从中删除数据的同一个表。

我查看了this 的答案,它似乎处理了这个主题,但似乎特定于 sql-server,尽管我不排除复制另一个问题的可能性。

【问题讨论】:

    标签: sql mysql join


    【解决方案1】:

    在回复您的评论时,这里有一个适用于 MySQL 的查询:

    delete YourTable
    from YourTable
    inner join YourTable yt2
    on YourTable.product_id = yt2.product_id
    and YourTable.id < yt2.id
    

    这只会删除重复的行。 inner join 将过滤掉每个产品的最新行,即使不存在同一产品的其他行。

    附:如果您尝试在FROM 之后为表命名,MySQL 会要求您指定数据库的名称,例如:

    delete <DatabaseName>.yt
    from YourTable yt
    inner join YourTable yt2
    on yt.product_id = yt2.product_id
    and yt.id < yt2.id;
    

    【讨论】:

    • 如果我Cannot delete or update a parent row: a foreign key constraint fails ('db'.'AnotherTable', CONSTRAINT 'AnotherTable_fk' FOREIGN KEY ('YourTable.product_id') REFERENCES 'YourTable' ('product_id') ON DELETE NO ACTION)怎么办?
    【解决方案2】:

    也许使用ALTER IGNORE TABLE ... ADD UNIQUE KEY。 例如:

    describe sales;
    +------------+---------+------+-----+---------+----------------+
    | Field      | Type    | Null | Key | Default | Extra          |
    +------------+---------+------+-----+---------+----------------+
    | id         | int(11) | NO   | PRI | NULL    | auto_increment | 
    | product_id | int(11) | NO   |     | NULL    |                | 
    +------------+---------+------+-----+---------+----------------+
    
    select * from sales;
    +----+------------+
    | id | product_id |
    +----+------------+
    |  1 |          1 | 
    |  2 |          1 | 
    |  3 |          2 | 
    |  4 |          3 | 
    |  5 |          3 | 
    |  6 |          2 | 
    +----+------------+
    
    ALTER IGNORE TABLE sales ADD UNIQUE KEY idx1(product_id), ORDER BY id DESC; 
    Query OK, 6 rows affected (0.03 sec)
    Records: 6  Duplicates: 3  Warnings: 0
    
    
    select * from sales;
    +----+------------+
    | id | product_id |
    +----+------------+
    |  6 |          2 | 
    |  5 |          3 | 
    |  2 |          1 | 
    +----+------------+
    

    请参阅此pythian post 了解更多信息。

    请注意,ids 以相反的顺序结束。我不认为这很重要,因为ids 的顺序在数据库中应该不重要(据我所知!)。但是,如果这让您不快,上面链接的帖子也显示了解决此问题的方法。但是,它涉及创建一个临时表,它需要比我上面发布的就地方法更多的硬盘空间。

    【讨论】:

      【解决方案3】:

      我可能会在 sql-server 中执行以下操作来消除重复项:

      DELETE FROM Sales
      FROM Sales
          INNER JOIN Sales b ON Sales.product_id = b.product_id AND Sales.id < b.id
      

      看起来类似 mysql 的 delete 语句可能是:

      DELETE FROM Sales 
      USING Sales
          INNER JOIN Sales b ON Sales.product_id = b.product_id AND Sales.id < b.id
      

      【讨论】:

      • 是的,当您在我的评论之后发布修改后的答案时,我正在尝试,Andomar。谢谢你们。
      【解决方案4】:

      使用 CTE 和排名函数更容易解决此类问题,但是,您应该能够执行以下操作来解决您的问题:

      Delete Sales
      Where Exists(
                  Select 1
                  From Sales As S2
                  Where S2.product_id = Sales.product_id
                      And S2.id > Sales.Id
                  Having Count(*) > 0
                  )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-07
        • 2010-10-24
        • 1970-01-01
        • 2018-02-17
        • 2013-11-09
        • 1970-01-01
        • 1970-01-01
        • 2020-07-16
        相关资源
        最近更新 更多