从Oracle中的表中删除重复的行


151

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

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

Answers:


306

使用rowid伪列。

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

其中column1column2column3组成每个记录的标识键。您可能会列出所有列。


6
+1我不得不找到两个重复的电话号码,这些电话号码埋在12,000+个记录中。将DELETE更改为SELECT,这在几秒钟内找到了它们。节省了我很多时间,谢谢。
shimonyk 2010年

3
这种方法对我不起作用。我不知道为什么 当我用“ SELECT *”替换“ DELETE”时,它返回了我要删除的行,但是当我用“ DELETE”执行时,它无限期地挂起。
aro_biz 2012年

我的也要么吊死要么执行时间非常长。已经运行了大约22个小时,并且仍在运行。表有21M条记录。
卡梅隆·卡斯蒂略

如果您有非常大的数据集,并且如果可行,我建议对WHERE语句添加进一步的过滤,这可能对长期运行查询的人们有所帮助。
里卡多·桑切斯

2
如果选择有效,但删除无效,则可能是由于所生成子查询的大小所致。首先用子查询结果创建一个表,然后在min(rowid)列上建立索引,然后运行delete语句,这可能很有趣。
Wouter

15

问汤姆

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

(修复了丢失的括号)


语句中缺少括号。我认为应该在最后吗?
卡梅隆·卡斯蒂略

12

DevX.com

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

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


12
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 2012年

2
在大型表上,这将比Bill的解决方案慢很多。
Wouter

8

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

创建表t2作为选择与t1不同的*;


不是答案- distinct *会记录每列至少1个符号不同的记录。您只需要从要用作主键的列中选择不同的值-Bill的答案就是这种方法的一个很好的例子。
Nogard

1
那就是我所需要的(删除完全相同的行)。谢谢 !
伊曼纽尔

这种方法的另一个缺点是您必须创建表的副本。对于大型表,这意味着要提供额外的表空间,并在复制后删除或缩小表空间。比尔的方法有更多好处,没有其他缺点。
Wouter

3

您应该使用游标进行循环来做一个小的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;

我相信不满意的原因是,如果您想知道的话,可以在SQL中使用PL / SQL。
WW。

7
仅仅因为您可以在SQL中完成操作,并不意味着它是唯一的解决方案。在看到仅SQL解决方案之后,我发布了此解决方案。我以为不正确的答案是投反对票。
尼克

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)


2

真正大桌子的最快方法

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

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. 尝试创建唯一的约束或主键,这些约束或主键将被重复项所违反。您将收到一条错误消息,因为您有重复项。例外表将包含重复行的行ID。

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. 通过rowid将表与exceptions_table联接在一起,并删除重复项

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. 如果要删除的行数很大,则创建一个新表(包含所有授予和索引),并按rowid与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 )
    )

2

使用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 );

嗨,Tandal,您好,请在提交答案时使用代码格式工具,因为它会增加可读性。
NSNoob

2

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

你能解释一下吗?
Dieter Meemken 2015年

用partition by划分的密列给出具有相同编号的重复行的排名,例如,三列的行分别为1,1,1和rowid为unic创建,我们正试图删除那些不匹配的rowid。
DoOrDie 2015年

我们可以同时使用rank和density_rank函数,但是我认为rank在这种情况下可以完美地工作。
DoOrDie 2015年

2

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

2

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

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

2
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

1
DELETE FROM tableName  WHERE ROWID NOT IN (SELECT   MIN (ROWID) FROM table GROUP BY columnname);

答案与《蜥蜴人比尔(Bill the Lizard)》更详尽的答案相同。
Wouter

1
delete from dept
where rowid in (
     select rowid
     from dept
     minus
     select max(rowid)
     from dept
     group by DEPTNO, DNAME, LOC
);

您可以添加更多有关自己的方式的信息吗?谢谢。
记者

1

为了获得最佳性能,这是我写的内容:(
请参阅执行计划)

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

1

检查以下脚本-

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)
  1. select * from test;

您将看到重复的记录已被删除。
希望这可以解决您的查询。谢谢 :)


1

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

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)我们只检查partition子句中的字段是否重复。

2)如果您出于某种原因要选择一个重复项,则可以使用order by子句使该行的row_number()= 1

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

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


0
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;

此方法的主要缺点是内部联接。对于大表,这将比Bill的方法慢很多。另外,使用PL / SQL执行此操作是过大的,您也可以通过简单地使用sql来使用它。
Wouter

0

解决方案:

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
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.