Menghapus duplikat baris dari tabel di Oracle

151

Saya menguji sesuatu di Oracle dan mengisi tabel dengan beberapa sampel data, tetapi dalam prosesnya saya secara tidak sengaja memuat rekaman duplikat, jadi sekarang saya tidak dapat membuat kunci utama menggunakan beberapa kolom.

Bagaimana saya bisa menghapus semua baris duplikat dan hanya menyisakan satu dari mereka?

juan
sumber

Jawaban:

306

Gunakan rowidpseudocolumn.

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

Di mana column1,, column2dan column3buat kunci pengidentifikasi untuk setiap catatan. Anda mungkin mencantumkan semua kolom Anda.

Bill the Lizard
sumber
6
+1 Saya harus menemukan dua nomor telepon rangkap yang terkubur dalam 12.000+ catatan. Ubah DELETE menjadi SELECT dan ini menemukan mereka dalam hitungan detik. Menghemat saya banyak waktu, terima kasih.
shimonyk
3
Pendekatan ini tidak berhasil untuk saya. Saya tidak tahu kenapa. Ketika saya mengganti "DELETE" dengan "SELECT *", itu mengembalikan baris yang ingin saya hapus, tetapi ketika saya dieksekusi dengan "DELETE" itu hanya menggantung tanpa batas.
aro_biz
Milik saya juga tergantung atau hanya mengeksekusi sangat lama. Sudah berjalan sekitar 22 jam dan masih berjalan. Tabel memiliki catatan 21 juta.
Cameron Castillo
Saya menyarankan untuk menambahkan pemfilteran lebih lanjut ke pernyataan WHERE jika Anda memiliki kumpulan data yang sangat besar dan jika memungkinkan, ini dapat membantu orang-orang dengan pertanyaan yang sudah berjalan lama.
Ricardo Sanchez
2
Jika pilih berfungsi, tetapi penghapusan tidak, itu mungkin karena ukuran subquery yang dihasilkan. Mungkin menarik untuk pertama-tama melakukan membuat tabel dengan hasil subquery, membangun indeks pada kolom min (rowid), dan kemudian menjalankan pernyataan delete.
Wouter
15

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

(memperbaiki tanda kurung yang hilang)

Programmer Mati
sumber
Parenthesis hilang dalam pernyataan. Saya berasumsi seharusnya pada akhirnya?
Cameron Castillo
12

Dari DevX.com :

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

Di mana kolom1, kolom2, dll adalah kunci yang ingin Anda gunakan.

Menandai
sumber
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)

sumber
1
Kembali komentar saya di atas pada jawaban terpilih, permintaan inilah yang sebenarnya memecahkan masalah saya.
aro_biz
2
Ini akan - jauh lebih lambat di meja besar daripada solusi Bill.
Wouter
8

Solusi 1)

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

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

Solusi 3)

delete from emp e1
         where rowid not in
          (select max(rowid) from emp e2
           where e1.empno = e2.empno ); 
Lakukan atau mati
sumber
6

buat tabel t2 sebagai pilih berbeda * dari t1;

Mohammed khaled
sumber
bukan jawaban - distinct *akan mengambil setiap catatan yang berbeda setidaknya dalam 1 simbol dalam 1 kolom. Yang Anda butuhkan hanyalah memilih nilai yang berbeda hanya dari kolom yang ingin Anda buat kunci utama - Jawaban Bill adalah contoh yang bagus untuk pendekatan ini.
Nogard
1
Itulah yang saya butuhkan (menghapus garis yang sepenuhnya identik). Terima kasih!
Emmanuel
Kerugian lain dari metode ini adalah Anda harus membuat salinan tabel Anda. Untuk tabel besar, ini berarti menyediakan tablespace tambahan, dan menghapus atau mengecilkan tablespace setelah salinan. Metode Bill memiliki lebih banyak manfaat, dan tidak ada kerugian tambahan.
Wouter
3

Anda harus melakukan blok pl / sql kecil menggunakan kursor untuk loop dan menghapus baris yang tidak ingin Anda pertahankan. Misalnya:

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;
Nick
sumber
Saya percaya downvote adalah karena Anda menggunakan PL / SQL ketika Anda bisa melakukannya dalam SQL, jika Anda bertanya-tanya.
WW.
7
Hanya karena Anda bisa melakukannya dalam SQL, bukan berarti itu satu-satunya solusi. Saya memposting solusi ini, setelah saya melihat solusi SQL-only. Saya pikir suara untuk jawaban yang salah.
Nick
3

Untuk memilih duplikat hanya format kueri yang bisa:

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

Jadi permintaan yang benar sesuai saran lainnya adalah:

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

Kueri ini akan menyimpan catatan tertua di database untuk kriteria yang dipilih di WHERE CLAUSE .

Oracle Certified Associate (2008)

pengguna1799846
sumber
2

Cara tercepat untuk tabel yang sangat besar

  1. Buat tabel pengecualian dengan struktur di bawah ini: exceptionions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. Coba buat batasan unik atau kunci utama yang akan dilanggar oleh duplikat. Anda akan mendapatkan pesan kesalahan karena Anda memiliki duplikat. Tabel pengecualian akan berisi baris baris untuk baris duplikat.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. Gabung meja Anda dengan exceptionionsable dengan rowid dan hapus dups

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. Jika jumlah baris yang akan dihapus besar, lalu buat tabel baru (dengan semua hibah dan indeks) anti-bergabung dengan exceptionions_table dengan rowid dan ganti nama tabel asli menjadi tabel original_dups dan ganti nama new_table_with_no_dups menjadi tabel asli

    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 )
    )
pengguna2158672
sumber
2

Menggunakan rowid-

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

Menggunakan self join-

delete from emp e1
 where rowid not in
 (select max(rowid) from emp e2
 where e1.empno = e2.empno );
Dnyaneshwar Tandale
sumber
Hai Tandale, Silakan gunakan alat pemformatan kode sambil mengirimkan jawaban karena meningkatkan keterbacaan.
NSNoob
2

Solusi 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
);
Lakukan atau mati
sumber
Bisakah Anda jelaskan sedikit?
Dieter Meemken
peringkat padat dengan partisi dengan memberikan peringkat untuk baris duplikat dengan nomor yang sama misalnya tiga baris yang memiliki peringkat 1, 1, 1 dan pembuatan rowid untuk setiap baris sebagai unic dan kami mencoba untuk menghapus rowid yang tidak cocok.
DoOrDie
kita bisa menggunakan fungsi peringkat dan dense_rank tetapi saya pikir peringkat berfungsi dengan baik dalam skenario ini.
DoOrDie
2

1. solusi

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. solusi

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

4. solusi

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

sumber
2

5. solusi

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
    );
Lakukan atau mati
sumber
2
DELETE from table_name where rowid not in (select min(rowid) FROM table_name group by column_name);

dan Anda juga dapat menghapus rekaman duplikat dengan cara lain

DELETE from table_name a where rowid > (select min(rowid) FROM table_name b where a.column=b.column);
Md Wasi
sumber
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
Krunal Patel
sumber
1
DELETE FROM tableName  WHERE ROWID NOT IN (SELECT   MIN (ROWID) FROM table GROUP BY columnname);
JgSudhakar
sumber
Jawaban yang sama dengan jawaban Bill the Lizard yang lebih rumit.
Wouter
1
delete from dept
where rowid in (
     select rowid
     from dept
     minus
     select max(rowid)
     from dept
     group by DEPTNO, DNAME, LOC
);
pengguna3655760
sumber
Bisakah Anda menambahkan informasi lebih lanjut tentang cara Anda? Terima kasih.
Reporter
1

Untuk kinerja terbaik, inilah yang saya tulis:
(lihat rencana eksekusi)

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
);
Enguerrand JORE
sumber
1

Periksa skrip di bawah ini -

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;    

Anda akan melihat di sini 6-catatan.
4. lari di bawah permintaan -

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;

Anda akan melihat bahwa rekaman duplikat telah dihapus.
Semoga ini bisa memecahkan pertanyaan Anda. Terima kasih :)

Rakesh Roshan
sumber
1

Saya tidak melihat jawaban apa pun yang menggunakan ekspresi tabel umum dan fungsi jendela. Inilah yang menurut saya paling mudah untuk dikerjakan.

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

Sesuatu yang perlu diperhatikan:

1) Kami hanya memeriksa duplikasi pada bidang dalam klausa partisi.

2) Jika Anda memiliki beberapa alasan untuk memilih satu duplikat daripada yang lain, Anda dapat menggunakan pesanan dengan klausa untuk membuat baris tersebut memiliki row_number () = 1

3) Anda dapat mengubah jumlah duplikat yang disimpan dengan mengubah klausa tempat final ke "Di mana RN> N" dengan N> = 1 (saya berpikir N = 0 akan menghapus semua baris yang memiliki duplikat, tetapi hanya akan menghapus semua baris) .

4) Menambahkan bidang Sum partisi kueri CTE yang akan menandai setiap baris dengan baris nomor dalam grup. Jadi untuk memilih baris dengan duplikat, termasuk item pertama gunakan "WHERE cnt> 1".

Darrel Lee
sumber
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;
Sinha ashish
sumber
Kerugian utama dari metode ini adalah sambungan internal. Untuk tabel besar ini akan jauh lebih lambat daripada metode Bill. Juga, menggunakan PL / SQL untuk melakukan ini berlebihan, Anda juga bisa menggunakan ini hanya dengan menggunakan sql.
Wouter
0

solusi:

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
);
sandeep gupta
sumber