【问题标题】:SQL: How to find duplicates based on two fields?SQL:如何根据两个字段查找重复项?
【发布时间】:2011-03-31 01:27:49
【问题描述】:

我在 Oracle 数据库表中有行,这对于两个字段的组合应该是唯一的,但表上没有设置唯一约束,因此我需要使用 SQL 自己查找所有违反约束的行。不幸的是,我微薄的 SQL 技能无法胜任这项任务。

我的表有三列相关:entity_id、station_id 和 obs_year。对于每一行,station_id 和 obs_year 的组合应该是唯一的,我想通过使用 SQL 查询将它们刷新出来来确定是否存在违反此规则的行。

我尝试了以下 SQL(由 this previous question 建议),但它对我不起作用(我得到 ORA-00918 列的定义不明确):

SELECT
entity_id, station_id, obs_year
FROM
mytable t1
INNER JOIN (
SELECT entity_id, station_id, obs_year FROM mytable 
GROUP BY entity_id, station_id, obs_year HAVING COUNT(*) > 1) dupes 
ON 
t1.station_id = dupes.station_id AND
t1.obs_year = dupes.obs_year

有人可以建议我做错了什么,和/或如何解决这个问题?

【问题讨论】:

    标签: sql oracle unique unique-constraint ora-00918


    【解决方案1】:

    我认为这里的许多解决方案都很麻烦且难以理解,因为我有一个 3 列主键约束并且需要查找重复项。所以这里有一个选项

    SELECT id, name, value, COUNT(*) FROM db_name.table_name
    GROUP BY id, name, value
    HAVING COUNT(*) > 1
    

    【讨论】:

      【解决方案2】:
        SELECT entity_id, station_id, obs_year
          FROM mytable
      GROUP BY entity_id, station_id, obs_year
      HAVING COUNT(*) > 1
      

      指定字段以查找 SELECT 和 GROUP BY 上的重复项。

      它的工作原理是使用GROUP BY 根据指定的列查找与任何其他行匹配的任何行。 HAVING COUNT(*) > 1 表示我们只对出现超过 1 次的行感兴趣(因此是重复的)

      【讨论】:

      • 嗨,这可能很好地解决了这个问题......但如果你能提供一些关于它如何工作以及为什么工作的解释会很好:) 不要忘记 - 有很多新手在 Stack Overflow 上,他们可以从你的专业知识中学到一两件事——对你来说显而易见的事情对他们来说可能并不那么明显。
      • 谢谢塔林。它通过使用 GROUP BY 根据指定的列查找与任何其他行匹配的任何行来工作。 HAVING COUNT(*) > 1 表示我们只对出现超过 1 次的行感兴趣(因此是重复的)
      • 嗨,别告诉我(在 cmets 中)。我知道 SQL,我不是在问我......这种解释是“你完整答案的一部分”......所以请编辑你的答案并将其添加到那里。 :)
      • 这不只是答案的一部分吗?我的意思是,你会知道哪些 entity_id, station_id, obs_year 元组有重复,但你没有得到实际的重复行。
      【解决方案3】:
      SELECT  *
      FROM    (
              SELECT  t.*, ROW_NUMBER() OVER (PARTITION BY station_id, obs_year ORDER BY entity_id) AS rn
              FROM    mytable t
              )
      WHERE   rn > 1
      

      Quassnoi 对大型表最有效。 我有这样的成本分析:

      SELECT a.dist_code, a.book_date, a.book_no
      FROM trn_refil_book a
      WHERE EXISTS (SELECT 1 from trn_refil_book b Where
             a.dist_code = b.dist_code and a.book_date = b.book_date and a.book_no = b.book_no
             AND a.RowId <> b.RowId)
             ;
      

      付出了1322341的代价

      SELECT a.dist_code, a.book_date, a.book_no
      FROM trn_refil_book a
      INNER JOIN (
      SELECT b.dist_code, b.book_date, b.book_no FROM trn_refil_book b 
      GROUP BY b.dist_code, b.book_date, b.book_no HAVING COUNT(*) > 1) c 
      ON 
       a.dist_code = c.dist_code and a.book_date = c.book_date and a.book_no = c.book_no
      ;
      

      给了1271699的成本

      同时

      SELECT  dist_code, book_date, book_no
      FROM    (
              SELECT  t.dist_code, t.book_date, t.book_no, ROW_NUMBER() OVER (PARTITION BY t.book_date, t.book_no
                ORDER BY t.dist_code) AS rn
              FROM    trn_refil_book t
              ) p
      WHERE   p.rn > 1
      ;
      

      付出了1021984

      该表未编入索引....

      【讨论】:

        【解决方案4】:
        SELECT  *
        FROM    (
                SELECT  t.*, ROW_NUMBER() OVER (PARTITION BY station_id, obs_year ORDER BY entity_id) AS rn
                FROM    mytable t
                )
        WHERE   rn > 1
        

        【讨论】:

        • 非常感谢您的回复。不幸的是,当我运行它时,我收到“ORA-00923:FROM 关键字未在预期位置找到”消息。
        • 在 mssql 中,必须在 FROM ( ) 括号后面加上 as x (名称并不重要)才能使其工作。很好的答案!
        【解决方案5】:

        您需要为主选择中的列指定表。此外,假设 entity_id 是 mytable 的唯一键并且与查找重复项无关,您不应该在 dupes 子查询中对其进行分组。

        试试:

        SELECT t1.entity_id, t1.station_id, t1.obs_year
        FROM mytable t1
        INNER JOIN (
        SELECT station_id, obs_year FROM mytable 
        GROUP BY station_id, obs_year HAVING COUNT(*) > 1) dupes 
        ON 
        t1.station_id = dupes.station_id AND
        t1.obs_year = dupes.obs_year
        

        【讨论】:

        • 感谢 Mark,关于在分组子查询中不使用 entity_id 的提示以及说明性示例。
        【解决方案6】:

        重写您的查询

        SELECT
        t1.entity_id, t1.station_id, t1.obs_year
        FROM
        mytable t1
        INNER JOIN (
        SELECT entity_id, station_id, obs_year FROM mytable 
        GROUP BY entity_id, station_id, obs_year HAVING COUNT(*) > 1) dupes 
        ON 
        t1.station_id = dupes.station_id AND
        t1.obs_year = dupes.obs_year
        

        我认为模棱两可的列错误 (ORA-00918) 是因为您是 selecting 列,其名称同时出现在表和子查询中,但是您没有指定是从 dupes 还是从 @ 987654324@(别名为t1)。

        【讨论】:

          【解决方案7】:
          SELECT entity_id, station_id, obs_year
          FROM mytable t1
          WHERE EXISTS (SELECT 1 from mytable t2 Where
                 t1.station_id = t2.station_id
                 AND t1.obs_year = t2.obs_year
                 AND t1.RowId <> t2.RowId)
          

          【讨论】:

          • 看起来我们无法在视图上执行此操作:ORA-01445: 无法从没有键保留表的连接视图中选择 ROWID 或对其进行采样
          【解决方案8】:

          将初始选择中的3个字段更改为

          SELECT
          t1.entity_id, t1.station_id, t1.obs_year
          

          【讨论】:

            【解决方案9】:

            您不能创建一个包含唯一约束的新表,然后逐行复制数据,忽略失败吗?

            【讨论】:

            • 是的,这是个好主意,谢谢!顺便说一句,我正在尝试弄清楚如何使用实体类中的注释在我的表上创建约束(我是使用 JPA/Hibernate 的 Java 开发人员),请参阅stackoverflow.com/questions/3504477/…
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-02-27
            • 2023-03-10
            • 2015-02-21
            • 2019-12-23
            • 1970-01-01
            • 2015-03-08
            相关资源
            最近更新 更多