Oracleのテーブルから重複行を削除する


151

私はOracleで何かをテストし、いくつかのサンプルデータをテーブルに入力しましたが、その過程で誤って重複レコードをロードしたため、一部の列を使用して主キーを作成できません。

重複する行をすべて削除して、そのうちの1つだけを残すにはどうすればよいですか?

回答:


306

rowid疑似列を使用します。

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

ここでcolumn1column2、およびcolumn3各レコードの識別キーを構成します。すべての列をリストできます。


6
+1 12,000以上のレコードに埋め込まれた2つの重複した電話番号を見つける必要がありました。DELETEをSELECTに変更したところ、数秒で見つかりました。時間を大幅に節約してくれて、ありがとう。
shimonyk

3
このアプローチは私にはうまくいきませんでした。理由はわかりません。「DELETE」を「SELECT *」で置き換えると、削除したい行が返されましたが、「DELETE」で実行すると、無期限にハングしました。
aro_biz

鉱山もハングしているか、非常に長く実行されています。約22時間走り続けました。テーブルには21Mのレコードがあります。
Cameron Castillo

非常に大きなデータセットがあり、実行可能な場合は、クエリが長時間実行されている場合に役立ちます。
Ricardo Sanchez

2
選択は機能するが削除が機能しない場合は、結果のサブクエリのサイズが原因である可能性があります。最初にサブクエリの結果でテーブルを作成し、min(rowid)列にインデックスを作成してから、deleteステートメントを実行するのは興味深いかもしれません。
Wouter、2014年

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

(欠落した括弧を修正)


ステートメントに括弧がありません。私はそれが最後であるべきだと思いますか?
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

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列に少なくとも1つの記号が異なるすべてのレコードを取得します。必要なのは、主キーにしたい列からのみ個別の値を選択することだけです。Billの答えは、このアプローチの良い例です。
Nogard 2013年

1
それが私が必要としていたことです(まったく同じ行を削除してください)。よろしくお願いします!
Emmanuel

この方法のもう1つの欠点は、テーブルのコピーを作成する必要があることです。巨大なテーブルの場合、これは追加のテーブルスペースを提供し、コピー後にテーブルスペースを削除または縮小することを意味します。ビルの方法には、より多くの利点があり、追加の欠点はありません。
Wouter、2014年

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のみのソリューションを見た後、私はこのソリューションを投稿しました。反対票は不正解だと思いました。
ニック

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ます。

オラクル認定アソシエイト(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を使用してテーブルを結合し、重複を削除する

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

こんにちはタンデール、可読性が向上するので、回答を送信するときにコードフォーマットツールを使用してください。
NSNoob、2015

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

パーティション化による密なランクは、同じ番号の重複する行のランクを示します。たとえば、ランク1、1、1を持つ3つの行と、行IDがunicとして作成され、一致しない行IDを削除しようとしています。
DoOrDie

ランク関数と稠密ランク関数の両方を使用できますが、このシナリオではランクが完全に機能すると思います。
DoOrDie

2

1.ソリューション

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

2. sloution

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

ビル・ザ・トカゲのより複雑な答えと同じ答え。
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)パーティション句のフィールドの重複のみをチェックしています。

2)重複を選択する理由がある場合は、order by句を使用して、その行にrow_number()= 1を設定できます。

3)最後の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;

この方法の主な欠点は、内部結合です。大きなテーブルの場合、これはビルの方法よりもかなり遅くなります。また、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.