【问题标题】:Remove Duplicate Rows in Oracle Table删除 Oracle 表中的重复行
【发布时间】:2019-01-18 08:33:00
【问题描述】:

我想在 Toad 中执行 SQL 以提取具有某些条件的表的所有值。我捕获了所有值,但现在我想向 SQL 添加一个新条件,但我不知道如何。我只想捕获不重复的值。例如:

我现在有这个:

PRINCIPAL APELLIDO NOMBRE
a           b        c
b           c        d
c           d        e
a           l        m

在这种情况下,我只想捕获PRINCIPAL 不重复的值,在示例中我只想捕获第二行和第三行,因为第一行和第四行是重复的。

【问题讨论】:

    标签: sql oracle toad


    【解决方案1】:

    一种方法是你可以使用这个查询:

    SELECT principal,
        MAX(apellido) AS apellido,
        MAX(nombre) AS nombre 
    FROM table_name
    GROUP BY principal
    HAVING COUNT(*) = 1
    ORDER BY principal;
    

    【讨论】:

      【解决方案2】:

      有一个返回 PRINCIPAL 值的子查询不止一次:

      select *
      from tablename
      where PRINCIPAL not in (select PRINCIPAL from tablename
                              group by PRINCIPAL
                              having count(*) > 1)
      

      【讨论】:

        【解决方案3】:

        您可以使用COUNT 聚合函数来删除所有重复的行。

        select PRINCIPAL, APELLIDO, NOMBRE,
        count(*) over (partition by PRINCIPAL) dup_cnt
        from tab
        
        P A N    DUP_CNT
        - - - ----------
        a b c          2
        a l m          2
        b c d          1
        c d e          1
        

        COUNT 计算PARTITION BY 子句中定义的每个唯一键的行数。

        最后的查询只选择唯一的行,即带有DUP_CNT = 1的行

        with dedup as (
        select PRINCIPAL, APELLIDO, NOMBRE,
        count(*) over (partition by PRINCIPAL) dup_cnt
        from tab)
        select PRINCIPAL, APELLIDO, NOMBRE
        from dedup
        where dup_cnt = 1
        

        注意:使用ROW_NUMBER 而不是COUNT,您可以进行重复数据删除,即您可以在结果中保留重复的行之一并删除重复的行。

        请注意,此方法需要对表 (WINDOW SORT) 进行排序,这对于大型表可能很繁重。在这种情况下,使用NOT EXISTS 的方法可能会产生更好的性能,因为它被转换并作为反散列连接执行 - HASH JOIN RIGHT ANTI

        select principal, apellido, nombre 
        from tab t
        where not exists 
        (select null
         from tab 
         where  principal = t.principal and rowid <> t.rowid
        )
        

        如果重复数据删除列 (principal) 可以为空,则必须小心。与COUNT 的第一个解决方案相反,not exusts 将所有nulls 留在结果中。如果不需要,您必须添加过滤器:

         and t.principal is not NULL
        

        如果您在pricipal 列上有索引,则最佳执行计划如下所示

        --------------------------------------
        | Id  | Operation             | Name | 
        --------------------------------------
        |   0 | SELECT STATEMENT      |      |    
        |*  1 |  HASH JOIN RIGHT ANTI |      |     
        |   2 |   INDEX FAST FULL SCAN| IDX  |  
        |*  3 |   TABLE ACCESS FULL   | TAB  |  
        --------------------------------------
        Predicate Information (identified by operation id):
        ---------------------------------------------------
        
           1 - access("PRINCIPAL"="T"."PRINCIPAL")
               filter(ROWID<>"T".ROWID)
           3 - filter("T"."PRINCIPAL" IS NOT NULL)
        

        【讨论】:

        • 感谢@Boneist 我重新阅读了问题并更新了答案。
        【解决方案4】:

        您可以使用窗口函数。逻辑是:

        select principal, apellido, nombre
        from (select t.*, count(*) over (partition by principal) as cnt
              from t
             ) t
        where cnt = 1;
        

        这将返回principalNULL 的行;此类行被NOT EXISTS 消除。

        但是,如果您在(principal, pk) 上有一个主键和一个索引,那么最快的方法可能是:

        select principal, apellido, nombre
        from t
        where not exists (select 1
                          from t t2
                          where t2.principal = t.principal and t2.pk <> t.pk
                         );
        

        【讨论】:

        • 对(可能)最高效的解决方案有很好的直觉,但请注意,在使用not exists 时,如果该列是可为空的,则应注意一些。
        • @MarmiteBomber 。 . .不,根本不关心。它通常按预期运行。另一方面,NOT IN 有非常意外的行为。
        • 我的意思是,如果您在表中添加两行带有principal is null 的行,它们会被count 方法消除,但被not exists 查询接受。所以这两个查询的行为并不相同 - 因此需要小心。
        猜你喜欢
        • 2010-10-06
        • 2019-10-10
        • 1970-01-01
        • 2019-11-25
        • 1970-01-01
        • 2020-09-12
        • 2014-06-13
        • 2020-12-21
        相关资源
        最近更新 更多