【问题标题】:How to delete duplicates from one table, but keeping only one record?如何从一张表中删除重复项,但只保留一条记录?
【发布时间】:2020-06-02 01:50:54
【问题描述】:

您好 SQL 查询专家! 我有一张名为 'mytable' 的表,它有 2 列,例如 id 和 title 。 我试图删除重复项,但只有一条记录(行)比较标题。 以下是我的选择:

DELETE FROM `myTable` AS `m1`
WHERE `m1`.`id` 
NOT IN (SELECT MIN(`b`.`id`) as `recordid` FROM `myTable` AS `b` GROUP BY `b`.`title`)

错误:Error in query (1064): Syntax error near '* FROM `myTable` AS `m1` WHERE `m1`.`id` NOT IN (SELECT MIN(`b`.`id`) as `reco' at line 1 但我遇到了麻烦,并试图解决这个问题超过 2 小时。 这似乎是一个非常简单的问题。 但我无法弄清楚。所以我要求stackoverflow!

主要是,我看到了一些奇怪的东西。 我试过这样,但没有任何错误。

SELECT * FROM `myTable` AS `m1`
WHERE `m1`.`id` 
NOT IN (SELECT MIN(`b`.`id`) as `recordid` FROM `myTable` AS `b` GROUP BY `b`.`title`)

当我运行这个查询时,我可以获得我想从“myTable”表中删除的记录(行)列表。

为什么我可以获取要删除的列表却遇到删除问题?

我真的需要你的帮助。 谢谢大家!

【问题讨论】:

标签: mysql sql duplicates sql-delete


【解决方案1】:

你可以这样表述:

delete m
    from mytable m left join
         (select m2.title, min(m2.id) as min_id
          from mytable m2
          group by m2.title
         ) m2
         on m.title = m2.title and m.id > m.min_id;

为了性能,您需要在(title, id) 上的索引。

【讨论】:

    【解决方案2】:

    我认为 Gordon 的回答说明了要点。最近不得不做类似的事情,最终得到了这个(适用于你的情况):

    DELETE FROM mytable WHERE id IN (
        SELECT *
        FROM (
            SELECT m.id
            FROM my_table m
            WHERE m.id NOT IN (
                SELECT MAX(m.id)
                FROM my_table sub
                GROUP BY sub.title
                HAVING COUNT(sub.title) > 1
            )
            AND m.id NOT IN (
                SELECT MAX(sub2.id)
                FROM my_table sub2
                GROUP BY sub2.title
                HAVING COUNT(sub2.title) = 1
            )
      ) AS m
    )
    

    额外的包装器是必要的(如果我没记错的话),因为在 DELETE 语句中不允许子查询(但可以像显示的那样使用)。

    这将按 ID 删除所有计数(title)大于 0 的记录,但不会删除最新的 (max) 记录。


    注意:这是一个非常密集的查询。建议使用 ID 和标题索引,即使这样:sloooowwww。仅通过 100k 条带索引的记录运行此程序,仍然需要大约 10 秒。

    【讨论】:

      【解决方案3】:

      语法:

      DELETE FROM `myTable` AS `m1`
      

      错了。
      应该是:

      DELETE m1 FROM `myTable` AS `m1`
      

      但你不需要给表起别名,你可以这样做

      DELETE FROM `myTable`
      

      MySql 也不允许在子查询中直接使用目标表,就像您在 NOT IN 中使用的那样,但是您可以通过将子查询包含在另一个子查询中来克服这个限制:

      DELETE FROM `myTable` 
      WHERE `id` NOT IN (
        SELECT `recordid`
        FROM (
          SELECT MIN(`id`) as `recordid` 
          FROM `myTable` 
          GROUP BY `title`
        ) t  
      )
      

      我删除了嵌套子查询的别名,因为它们不需要。

      【讨论】:

        【解决方案4】:

        我终于找到了我遇到的问题的确切原因。 我参考了@Malakiyasanjay 的评论。 你可以从这里找到How to keep only one row of a table, removing duplicate rows?

        我试过这样:(它也对我有用,但运行 30,000 行查询需要很长时间)

        delete from myTable
        where id not in 
        (select min(id) as min from (select * from myTable) as x group by title)
        

        问题是我无法将“myTable”表指定为目标表。所以我使用了(select * from myTable) as x 并弄清楚了。

        很抱歉,我无法解释更多细节,因为我不熟悉 mysql 查询。但你应该注意:

        MySql 不允许在子查询中直接使用目标表,就像您在 NOT IN 中使用的那样,但是您可以通过将子查询包含在另一个子查询中来克服这个限制。 (请参考@forpas 的回答。)

        但是你必须注意到这需要很长时间......它可能会导致超时错误。我对大约 600,000 行的表运行了此查询,但它几天没有响应。所以我认为这个想法非常适合小型数据库表。

        希望对大家有帮助! :)

        【讨论】:

          猜你喜欢
          • 2013-12-30
          • 2016-02-14
          • 2020-10-07
          • 1970-01-01
          • 1970-01-01
          • 2015-09-27
          • 2013-05-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多