【问题标题】:SQL Removing duplicates one row at a timeSQL删除重复一行
【发布时间】:2010-11-22 06:19:31
【问题描述】:

我有一个表格,我可以在其中保存所有曾经发生的行更改。问题是在应用程序的开头有一个错误,它会为每一行制作一堆副本。

表格如下所示:

copies
|ID |CID |DATA
| 1 | 1  |  DA
| 2 | 2  |  DO
| 2 | 3  |  DO (copy of CID 2)
| 1 | 4  |  DA (copy of CID 1)
| 2 | 5  |  DA
| 1 | 6  |  DA (copy of CID 1)
| 2 | 7  |  DO

CID 在表副本中是唯一的。

我想要的是删除 DATA GROUP BY ID 的所有重复项,这些重复项依次按 CID 排序。

正如您在表格中看到的,CID 2 和 3 是相同的,它们是一个接一个。我想删除 CID 3。与 CID 4 和 CID 6 相同;它们之间没有 ID 1,是 CID 1 的副本。

删除重复项后,我希望表格如下所示:

copies
|ID |CID |DATA
| 1 | 1  |  DA
| 2 | 2  |  DO
| 2 | 5  |  DA
| 2 | 7  |  DO

有什么建议吗? :)

我认为我的问题被问得很糟糕,因为每个人似乎都认为最好的答案给出了这样的结果:

ID   | DATA | DATA | DATA | DATA | DATA |     DATA |        CID          |
                                                   |Expected |  Quassnoi |
1809 |    1 |    0 |    1 |    0 |    0 |     NULL |  252227 |    252227 |
1809 |    1 |    0 |    1 |    1 |    0 |     NULL |  381530 |    381530 |
1809 |    1 |    0 |    1 |    0 |    0 |     NULL |  438158 | (missing) |
1809 |    1 |    0 |    1 |    0 | 1535 | 20090113 |  581418 |    581418 |
1809 |    1 |    1 |    1 |    0 | 1535 | 20090113 |  581421 |    581421 |

CID 252227 和 CID 438158 是重复的,但因为 CID 381530 在它们之间;我想保留这个。在按 CID 和 ID 排序时,只有重复项是一个接一个。

【问题讨论】:

  • 在您的最终结果集中,(2, 2)(2, 7) 之间也没有 id 1。这是否意味着它们也应该被删除?如果不是,它们的区别是什么?
  • 他们之间没有 ID 1,但他们有 (2, 5, DA)。 ID 应该被忽略为“重复”数据,它应该用于将行分组在一起。谈论两个 ID 可能会让人分心。如果您查看最后一张表,您只会看到一个 ID 以及仅来自该 ID 的预期结果。
  • (1, 1)(1, 4) 在它们之间有一个 DO。那你为什么要删除(1, 4)呢?
  • 如果您采用 ID = 1 的条目序列(CID 的升序),您有:(1, 1, DA), (1, 4, DA), (1, 6,达)。由于这些行中的 ID 和 DATA 值相同,因此只保留“重复”中的第一个。同样,对于 ID = 2,序列为:(2, 2, DO), (2, 3, DO), (2, 5, DA), (2, 7, DA)。 CID = 3 和 CID = 2 的行中的 ID 和 DATA 值相同,应删除 CID = 3; CID = 5 的行在 DATA 中具有不同的值(与 CID = 2 相比),应为 ke[t,CID = 7 的行与 CID = 5 相比在 DATA 中具有不同的值,应保留。
  • Quassnoi:因为“DO”不是来自同一个 ID。查询应始终按 ID 分组。

标签: php sql mysql duplicates duplicate-removal


【解决方案1】:

您可以在子查询中为此使用count

delete from copies
where
    (select count(*) from copies s where s.id = copies.id 
                                   and s.data = copies.data 
                                   and s.cid > copies.cid) > 0

【讨论】:

  • 我认为这根本无法正常工作。它不考虑是否按ID和CID排序的连续行之间的DATA值。
  • 确实如此。我只是将data 错误标记为name。我的错。固定!
【解决方案2】:
DELETE   c.*
FROM     copies c
JOIN     (
         SELECT  id, data, MIN(copies) AS minc
         FROM    copies
         GROUP BY
                 id, data
         ) q
ON       c.id = q.id
         AND c.data = q.data
         AND c.cid <> q.minc

更新:

DELETE  c.*
FROM    (
        SELECT  cid
        FROM    (
                SELECT  cid,
                        COALESCE(data1 = @data1 AND data2 = @data2, FALSE) AS dup,
                        @data1 := data1,
                        @data2 := data2
                FROM    (
                        SELECT  @data1 := NULL,
                                @data2 := NULL
                        ) vars, copies ci
                ORDER BY
                        id, cid
                ) qi
        WHERE   dup
        ) q
JOIN    copies c
ON      c.cid = q.cid

此解决方案使用MySQL 会话变量。

有一个纯ANSI 解决方案可以使用NOT EXISTS,但是,由于MySQL 优化器的工作方式,它会很慢(它不会在相关子查询中使用range 访问方法)。

请参阅我博客中的这篇文章,了解非常接近任务的性能详细信息:

【讨论】:

  • 感谢您的建议,但这会删除:| 2 | 7 | DO 并非所有相同的行都应该删除,只是那些按 ID 分组的紧跟在另一个之后的行。
  • 要正确执行此操作,您还必须有一个标准,即不存在具有不同数据值和相同 ID 值以及在“当前行”和“较早行”之间具有 CID 的行具有相同的数据值'。充其量,这很繁琐。
  • 嗨 Quassnoi,我试过这个,它几乎删除了我在表中的所有内容。如果我有多个 DATA 列,查询应该是什么样子?
  • 很好,非常感谢 Quassnoi。乍一看,它似乎是正确的。我将运行一些测试来验证!
【解决方案3】:
// EDITED for @Jonathan Leffler comment
//$sql = "SELECT ID,CID,DATA FROM copies ORDER BY CID, ID";
$sql = "SELECT ID,CID,DATA FROM copies ORDER BY ID, CID";
$result = mysql_query($sql, $link); 
$data = "";
$id = "";
while ($row = mysql_fetch_row($result)){ 
       if (($row[0]!=$id) && ($row[2]!=$data) && ($id!="")){
            $sql2 = "DELETE FROM copies WHERE CID=".$row[1];
            $res = mysql_query($sql2, $link); 
       }
       $id=$row[0];
       $data=$row[2];
} 

【讨论】:

  • 真的吗?您在这里有三种纯 SQL 方法,并且您建议使用解释语言启动单个 delete 事务?我真诚地希望这不是您解决所有数据库问题的方式。数据库是关于集合思考的!
  • 1º 是因为咨询是一个过程。 2º 有一个 PHP 标记,因此可以包含该语言的解决方案。还有什么?
  • @Eric 该任务必须完成一次以进行清理,所以我认为速度不是这里的主要问题。这使它成为一个有效的答案,即使不是最好的答案。
  • 1º? 2º?学位分数有什么用? (我的意思不是要讽刺……某些语言是这样写序数的吗?)
  • @andres:我认为您的基于光标的解决方案可能是执行此操作的最佳方法(到目前为止显示的方法) - 但我希望使用“ORDER BY ID, CID'。另请参阅我对主要问题的评论。
【解决方案4】:

从副本 c 中删除 c.cid in (select max(cid) as max_cid, count(*) as num from copies where num > 1 group by id, data)

【讨论】:

  • 这只会删除最后一个重复项,而不是所有重复项。
  • 是的,您是对的,但是您可以再次运行查询,直到删除所有重复项。你的解决方案更优雅(我已经投票给你的答案)!
猜你喜欢
  • 2020-04-22
  • 1970-01-01
  • 2019-12-04
  • 2017-11-16
  • 1970-01-01
  • 2023-04-10
  • 2017-12-04
  • 2017-06-29
  • 2020-02-17
相关资源
最近更新 更多