Oracle의 테이블에서 중복 행 제거


151

Oracle에서 무언가를 테스트하고 샘플 데이터로 테이블을 채웠지만 실수로 중복 레코드를로드했기 때문에 일부 열을 사용하여 기본 키를 만들 수 없습니다.

모든 중복 행을 삭제하고 그중 하나만 남겨 둘 수 있습니까?

답변:


306

rowid의사 열을 사용하십시오 .

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

어디에서 column1, column2그리고 column3각 레코드에 대한 식별 키를 구성합니다. 모든 열을 나열 할 수 있습니다.


6
+1 12,000 개 이상의 레코드에 묻힌 두 개의 중복 전화 번호를 찾아야했습니다. DELETE를 SELECT로 변경했는데 몇 초 만에 발견되었습니다. 많은 시간을 절약했습니다. 감사합니다.
shimonyk

3
이 방법은 저에게 효과적이지 않았습니다. 이유를 모르겠습니다. "DELETE"를 "SELECT *"로 바꾸면 삭제하려는 행이 반환되었지만 "DELETE"로 실행하면 무기한으로 중단되었습니다.
aro_biz 2016 년

광산은 또한 매달려 있거나 아주 오래 실행됩니다. 약 22 시간 동안 달리면서 여전히 가고 있습니다. 테이블에는 21M 레코드가 있습니다.
카메론 카스티요

매우 큰 데이터 세트가 있고 가능한 경우 장기 실행 쿼리를 사용하는 사람들에게 도움이 될 수 있으면 WHERE 문에 추가 필터링을 추가하는 것이 좋습니다.
Ricardo Sanchez

2
선택이 작동하지만 삭제가 수행되지 않으면 결과 하위 쿼리의 크기 때문일 수 있습니다. 먼저 하위 쿼리 결과로 테이블을 만들고 min (rowid) 열에 인덱스를 작성한 다음 delete 문을 실행하는 것이 흥미로울 수 있습니다.
Wouter

14

에서 톰 질문

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

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 2016 년

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

t1과 구별되는 선택으로 테이블 t2를 작성하십시오.


답이 아님 distinct *-1 열에 1 개 이상의 기호가 다른 모든 레코드를 사용합니다. 기본 키를 만들려는 열에서만 고유 한 값을 선택하기 만하면됩니다. Bill의 대답은이 방법의 좋은 예입니다.
Nogard

1
그것이 내가 필요한 것입니다 (전적으로 동일한 줄을 제거하십시오). 감사 !
Emmanuel

이 방법의 또 다른 단점은 테이블의 사본을 작성해야한다는 것입니다. 거대한 테이블의 경우 추가 테이블 스페이스를 제공하고 복사 후 테이블 스페이스를 삭제하거나 축소합니다. Bill의 방법에는 더 많은 이점이 있으며 추가 단점이 없습니다.
Wouter

3

커서 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;

다운 보트는 SQL에서 할 수있을 때 PL / SQL을 사용하고 있기 때문이라고 생각합니다.
WW.

7
SQL로 할 수 있다고해서 유일한 해결책은 아닙니다. SQL 전용 솔루션을 본 후에이 솔루션을 게시했습니다. 나는 찬성 투표가 오답이라고 생각했다.
Nick

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 Certified Associate (2008)


2

정말 큰 테이블을위한 가장 빠른 방법

  1. 아래 구조로 예외 테이블을 작성하십시오. exceptions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. 복제본이 위반하는 고유 제한 조건 또는 기본 키를 작성하십시오. 중복되어 있기 때문에 오류 메시지가 나타납니다. 예외 테이블에는 중복 행의 rowid가 포함됩니다.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. rowid로 exceptions_table을 사용하여 테이블을 조인하고 dups를 삭제하십시오.

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

안녕하세요 Tandale, 가독성을 높이기 위해 답변을 제출하는 동안 코드 형식 도구를 사용하십시오.
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

파티션이있는 밀도가 높은 순위는 동일한 수의 중복 행에 대한 순위를 제공합니다 (예 : 순위 1, 1, 1을 가진 3 개의 행과 모든 행에 대해 rowid create가 unic으로 생성되어 일치하지 않는 rowids를 삭제하려고합니다.
DoOrDie

우리는 rank와 dense_rank 함수를 모두 사용할 수 있지만이 시나리오에서는 rank가 완벽하게 작동한다고 생각합니다.
DoOrDie

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;

삼.

 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) N> = 1을 사용하여 final where 절을 "Where RN> N"으로 변경하여 보존되는 숫자 중복을 변경할 수 있습니다 (N = 0은 중복이있는 모든 행을 삭제하지만 모든 행을 삭제한다고 생각했습니다) .

4) Sum 파티션 필드에 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
);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.