从Oracle表中删除重复行

198

我在Oracle中正在测试某些内容,并使用一些示例数据填充了一个表格,但是在此过程中意外加载了重复记录,因此现在我无法使用某些列创建主键。

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

24个回答

411
使用rowid伪列。
DELETE FROM your_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM your_table
GROUP BY column1, column2, column3);

column1column2column3是每个记录的标识键。您可以列出所有列。


9
我必须在12,000多条记录中找到两个重复的电话号码。将“DELETE”更改为“SELECT”,几秒钟内就能找到它们。这节省了我大量的时间,谢谢你。+1 - shimonyk
3
这种方法对我没起作用,我不知道为什么。当我把"DELETE"替换成"SELECT *"时,它返回了我想要删除的行,但是当我使用"DELETE"执行时,程序一直卡住没有反应。 - aro_biz
1
我的程序也一直卡着或者执行时间极长。已经运行了大约22个小时,仍在继续。表格有2100万条记录。 - Cameron Castillo
1
如果你有一个非常大的数据集,并且可行的话,我建议在WHERE语句中添加进一步的过滤条件,这可能有助于那些运行时间较长的查询。 - Ricardo Sanchez
2
如果选择操作正常,但删除操作失败,可能是由于子查询结果的大小。首先可以使用子查询结果创建一个表,对min(rowid)列建立索引,然后再运行删除语句,这样可能会更有趣。 - Wouter
显示剩余2条评论

18

From 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);

(修复了缺失的括号)


1
语句缺少括号。我认为应该在结尾处加上括号? - Cameron Castillo

16

来自DevX.com

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

在这里,column1、column2等是您想要使用的键。


15
DELETE FROM tablename a
      WHERE a.ROWID > ANY (SELECT b.ROWID
                             FROM tablename b
                            WHERE a.fieldname = b.fieldname
                              AND a.fieldname2 = b.fieldname2)

1
关于最受欢迎的答案上面我的评论,正是这个请求实际上解决了我的问题。 - aro_biz
4
在处理大型表格时,这种方法比Bill的解决方案要慢得多。 - Wouter

11

解决方案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 ); 

1
你能告诉我们每种方法的优缺点吗? - Arun Gowda

7

创建表t2,将t1中的所有不重复的数据选出来。


"distinct *" 将获取每个至少在 1 列中的 1 个符号不同的记录。您只需要从要作为主键的列中选择唯一值即可 - Bill 的答案是这种方法的绝佳示例。 - Nogard
1
那正是我所需要的。谢谢! - Emmanuel
这种方法的另一个劣势是您需要创建表格的副本。对于大型表格,这意味着提供额外的表空间,并在复制后删除或缩小表空间。比尔的方法拥有更多的优点,没有额外的缺点。 - Wouter

4

这篇博客文章对于常见情况非常有帮助:

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

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

4

您应该使用游标循环编写一个小的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来完成它,以防你想知道。 - WW.
9
仅因为你可以在SQL中完成它并不意味着这是唯一的解决方案。我在看到仅限于SQL的解决方案后发布了此解决方案。 我认为负投票是用于错误答案。 - Nick

3
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

3

为了仅选择重复项,查询格式应为:

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)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接