在Oracle中从表中删除重复行

Posted

技术标签:

【中文标题】在Oracle中从表中删除重复行【英文标题】:Removing duplicate rows from table in Oracle 【发布时间】:2010-10-06 11:00:08 【问题描述】:

我正在 Oracle 中测试某些东西并用一些示例数据填充了一个表,但在此过程中我不小心加载了重复记录,所以现在我无法使用某些列创建主键。

如何删除所有重复的行并只保留其中一个?

【问题讨论】:

【参考方案1】:

使用rowid 伪列。

DELETE FROM your_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM your_table
GROUP BY column1, column2, column3);

其中column1column2column3 构成每条记录的标识键。您可以列出所有列。

【讨论】:

+1 我必须在 12,000 多条记录中找到两个重复的电话号码。将 DELETE 更改为 SELECT 并在几秒钟内找到它们。为我节省了大量时间,谢谢。 这种方法对我不起作用。我不知道为什么。当我用“SELECT *”替换“DELETE”时,它返回了我想删除的行,但是当我用“DELETE”执行时,它只是无限期地挂起。 如果选择有效,但删除无效,这可能是由于生成的子查询的大小。首先使用子查询结果创建表,在 min(rowid) 列上建立索引,然后运行 ​​delete 语句可能会很有趣。 这不是只删除第一个重复的吗,如果有几个? 对我来说非常有用的查询,删除重复项效果很好【参考方案2】:

来自Ask Tom

delete from t
 where rowid IN ( select rid
                    from (select rowid rid, 
                                 row_number() over (partition by 
                         companyid, agentid, class , status, terminationdate
                                   order by rowid) rn
                            from t)
                   where rn <> 1);

(修正了缺少的括号)

【讨论】:

语句中缺少括号。我认为它应该在最后?【参考方案3】:

来自DevX.com:

DELETE FROM our_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM our_table
GROUP BY column1, column2, column3...) ;

其中 column1、column2 等是您要使用的键。

【讨论】:

【参考方案4】:
DELETE FROM tablename a
      WHERE a.ROWID > ANY (SELECT b.ROWID
                             FROM tablename b
                            WHERE a.fieldname = b.fieldname
                              AND a.fieldname2 = b.fieldname2)

【讨论】:

回复我上面对最高投票答案的评论,正是这个请求真正解决了我的问题。 这在大桌子上会比比尔的解决方案慢很多。【参考方案5】:

解决方案 1)

delete from emp
where rowid not in
(select max(rowid) from emp group by empno);

解决方案 2)

delete from emp where rowid in
               (
                 select rid from
                  (
                    select rowid rid,
                      row_number() over(partition by empno order by empno) rn
                      from emp
                  )
                where rn > 1
               );

解决方案 3)

delete from emp e1
         where rowid not in
          (select max(rowid) from emp e2
           where e1.empno = e2.empno ); 

【讨论】:

您能告诉我们每种方法的优缺点吗?【参考方案6】:

create table t2 as select distinct * from t1;

【讨论】:

不是答案 - distinct * 将获取每列中至少有 1 个符号不同的记录。您只需要从您想要创建主键的列中选择不同的值 - 比尔的回答就是这种方法的一个很好的例子。 这就是我所需要的(删除完全相同的行)。谢谢! 此方法的另一个缺点是您必须创建表的副本。对于大表,这意味着提供额外的表空间,并在复制后删除或缩小表空间。比尔的方法有更多的好处,没有额外的缺点。【参考方案7】:

您应该使用游标 for 循环执行一个小的 pl/sql 块并删除您不想保留的行。例如:

declare
prev_var my_table.var1%TYPE;

begin

for t in (select var1 from my_table order by var 1) LOOP

-- if previous var equal current var, delete the row, else keep on going.
end loop;

end;

【讨论】:

我相信不赞成票是因为您使用的是 PL/SQL,而您可以在 SQL 中执行它,以防您想知道。 仅仅因为您可以在 SQL 中完成,并不意味着它是唯一的解决方案。在我看到仅 SQL 的解决方案之后,我发布了这个解决方案。我认为反对票是针对不正确的答案。【参考方案8】:

要选择重复项,查询格式可以是:

SELECT GroupFunction(column1), GroupFunction(column2),..., 
COUNT(column1), column1, column2...
FROM our_table
GROUP BY column1, column2, column3...
HAVING COUNT(column1) > 1

因此,根据其他建议,正确的查询是:

DELETE FROM tablename a
      WHERE a.ROWID > ANY (SELECT b.ROWID
                             FROM tablename b
                            WHERE a.fieldname = b.fieldname
                              AND a.fieldname2 = b.fieldname2
                              AND ....so on.. to identify the duplicate rows....)

此查询将根据WHERE CLAUSE 中选择的条件在数据库中保留最旧的记录。

Oracle 认证助理(2008 年)

【讨论】:

【参考方案9】:
create table abcd(id number(10),name varchar2(20))

insert into abcd values(1,'abc')

insert into abcd values(2,'pqr')


insert into abcd values(3,'xyz')

insert into abcd values(1,'abc')

insert into abcd values(2,'pqr')

insert into abcd values(3,'xyz')


select * from abcd
id  Name
1   abc
2   pqr
3   xyz
1   abc
2   pqr
3   xyz

Delete Duplicate record but keep Distinct Record in table 

DELETE 
FROM abcd a
WHERE ROWID > (SELECT MIN(ROWID) FROM abcd b
WHERE b.id=a.id
);

run the above query 3 rows delete 

select * from abcd

id  Name 
1   abc
2   pqr
3   xyz

【讨论】:

【参考方案10】:

真正大桌子的最快方法

    创建具有以下结构的异常表: 异常表

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
    

    尝试创建一个唯一约束或主键,这将被重复项违反。您将收到一条错误消息,因为您有重复项。例外表将包含 重复行的 rowid。

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
    

    通过 rowid 使用 exceptions_table 加入您的表并删除 dups

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
    

    如果要删除的行数很大,则通过rowid创建一个新表(包含所有授权和索引)anti-joining exceptions_table并将原表重​​命名为original_dups表并将new_table_with_no_dups重命名为原表

    create table new_table_with_no_dups AS (
        select field1, field2 ........ 
        from original_dups t1
        where not exists ( select null from exceptions_table T2 where t1.rowid = t2.row_id )
    )
    

【讨论】:

【参考方案11】:

使用rowid-

delete from emp
 where rowid not in
 (select max(rowid) from emp group by empno);

使用自连接-

delete from emp e1
 where rowid not in
 (select max(rowid) from emp e2
 where e1.empno = e2.empno );

【讨论】:

您好 Tandale,请在提交答案时使用代码格式化工具,以提高可读性。【参考方案12】:

解决方案 4)

 delete from emp where rowid in
            (
             select rid from
                (
                  select rowid rid,
                  dense_rank() over(partition by empno order by rowid
                ) rn
             from emp
            )
 where rn > 1
);

【讨论】:

你能解释一下吗? dense rank with partition by 给出了具有相同编号的重复行的排名,例如具有 1 、 1 、 1 和 rowid 的三行为每一行创建为 unic 并且我们正在尝试删除那些不匹配。 我们可以同时使用 rank 和 dense_rank 函数,但我认为 rank 在这种情况下非常有效。【参考方案13】:

1.解决方案

delete from emp
    where rowid not in
    (select max(rowid) from emp group by empno);

2。懒惰

delete from emp where rowid in
               (
                 select rid from
                  (
                    select rowid rid,
                      row_number() over(partition by empno order by empno) rn
                      from emp
                  )
                where rn > 1
               );

3.解决方案

delete from emp e1
         where rowid not in
          (select max(rowid) from emp e2
           where e1.empno = e2.empno ); 

4.解决方案

 delete from emp where rowid in
            (
             select rid from
                (
                  select rowid rid,
                  dense_rank() over(partition by empno order by rowid
                ) rn
             from emp
            )
 where rn > 1
);

【讨论】:

【参考方案14】:

5.解决方案

delete from emp where rowid in 
    (
      select  rid from
       (
         select rowid rid,rank() over (partition by emp_id order by rowid)rn from emp     
       )
     where rn > 1
    );

【讨论】:

【参考方案15】:
DELETE from table_name where rowid not in (select min(rowid) FROM table_name group by column_name);

你也可以通过其他方式删除重复记录

DELETE from table_name a where rowid > (select min(rowid) FROM table_name b where a.column=b.column);

【讨论】:

【参考方案16】:

This blog post 对于一般情况真的很有帮助:

如果行完全重复(所有列中的所有值都可以有副本),则没有可使用的列!但是要保留一个,您仍然需要为每个组中的每一行设置一个唯一标识符。 幸运的是,Oracle 已经有了一些您可以使用的东西。行号。 Oracle 中的所有行都有一个 rowid。这是一个物理定位器。也就是说,它说明了 Oracle 在磁盘上存储该行的位置。这对每一行都是独一无二的。因此,您可以使用此值来识别和删除副本。为此,请将不相关删除中的 min() 替换为 min(rowid):

delete films
where  rowid not in (
  select min(rowid)
  from   films
  group  by title, uk_release_date
)

【讨论】:

【参考方案17】:
DELETE FROM tableName  WHERE ROWID NOT IN (SELECT   MIN (ROWID) FROM table GROUP BY columnname);

【讨论】:

与蜥蜴比尔更详尽的答案相同。【参考方案18】:
delete from dept
where rowid in (
     select rowid
     from dept
     minus
     select max(rowid)
     from dept
     group by DEPTNO, DNAME, LOC
);

【讨论】:

您能添加更多关于您的方式的信息吗?谢谢。【参考方案19】:

为了获得最佳性能,这是我写的: (见执行计划)

DELETE FROM your_table
WHERE rowid IN 
  (select t1.rowid from your_table  t1
      LEFT OUTER JOIN (
      SELECT MIN(rowid) as rowid, column1,column2, column3
      FROM your_table 
      GROUP BY column1, column2, column3
  )  co1 ON (t1.rowid = co1.rowid)
  WHERE co1.rowid IS NULL
);

【讨论】:

【参考方案20】:

检查以下脚本 -

1.

Create table test(id int,sal int); 

2。

    insert into test values(1,100);    
    insert into test values(1,100);    
    insert into test values(2,200);    
    insert into test values(2,200);    
    insert into test values(3,300);    
    insert into test values(3,300);    
    commit;

3.

 select * from test;    

您将在此处看到 6 条记录。 4.运行下面的查询 -

delete from 
   test
where rowid in
 (select rowid from 
   (select 
     rowid,
     row_number()
    over 
     (partition by id order by sal) dup
    from test)
  where dup > 1)
    select * from test;

您会看到重复记录已被删除。 希望这能解决您的疑问。 谢谢:)

【讨论】:

【参考方案21】:

我没有看到任何使用公用表表达式和窗口函数的答案。 这是我发现最容易使用的。

DELETE FROM
 YourTable
WHERE
 ROWID IN
    (WITH Duplicates
          AS (SELECT
               ROWID RID, 
               ROW_NUMBER() 
               OVER(
               PARTITION BY First_Name, Last_Name, Birth_Date)
                  AS RN
               SUM(1)
               OVER(
               PARTITION BY First_Name, Last_Name, Birth_Date
               ORDER BY ROWID ROWS BETWEEN UNBOUNDED PRECEDING 
                                       AND UNBOUNDED FOLLOWING)
                   AS CNT
              FROM
               YourTable
              WHERE
               Load_Date IS NULL)
     SELECT
      RID
     FROM
      duplicates
     WHERE
      RN > 1);

注意事项:

1) 我们只检查分区子句中的字段是否重复。

2) 如果您有理由选择一个重复项而不是其他重复项,您可以使用 order by 子句使该行具有 row_number() = 1

3)您可以通过将最后的 where 子句更改为“Where RN > N”(其中 N >= 1)来更改保留的数字重复(我在想 N = 0 会删除所有有重复的行,但它只会删除所有行)。

4) 在 CTE 查询中添加 Sum 分区字段,该字段将使用组中的行数标记每一行。因此,要选择包含重复项的行,包括第一项,请使用“WHERE cnt > 1”。

【讨论】:

【参考方案22】:

解决方案:

delete from emp where rowid in
(
    select rid from
    (
        select rowid rid,
        row_number() over(partition by empno order by empno) rn
        from emp
    )
    where rn > 1
);

【讨论】:

【参考方案23】:
create or replace procedure delete_duplicate_enq as
    cursor c1 is
    select *
    from enquiry;
begin
    for z in c1 loop
        delete enquiry
        where enquiry.enquiryno = z.enquiryno
        and rowid > any
        (select rowid
        from enquiry
        where enquiry.enquiryno = z.enquiryno);
    end loop;
 end delete_duplicate_enq;

【讨论】:

这种方法的一个主要缺点是内部连接。对于大表,这将比比尔的方法慢很多。此外,使用 PL/SQL 来做这件事是大材小用,你也可以通过简单地使用 sql 来使用它。【参考方案24】:

这类似于最佳答案,但给了我一个更好的解释计划:

delete from your_table
 where rowid in (
        select max(rowid)
          from your_table
         group by column1, column2, column3
        having count(*) > 1
       );

【讨论】:

以上是关于在Oracle中从表中删除重复行的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQl,oracle 9i,使用sql删除重复行

Oracle 删除表中的重复行并使用另一个表中的值更新行

从表中的多个重复项中删除特定记录

oracle删除重复的行怎么删啊

如何从表中删除某些行?

oracle删除重复的行怎么删啊