Oracle'daki tablodan yinelenen satırları kaldırma


151

Oracle'da bir şey test ediyorum ve bazı örnek verilerle bir tablo doldurdum, ancak süreçte yanlışlıkla yinelenen kayıtlar yükledim, bu yüzden şimdi sütunlardan bazılarını kullanarak birincil bir anahtar oluşturamıyorum.

Tüm yinelenen satırları nasıl silebilir ve bunlardan yalnızca birini nasıl bırakabilirim?

Yanıtlar:


306

rowidSözde sütun kullanın .

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

Where column1,, column2ve column3her kayıt için tanımlayıcı anahtarı oluşturun. Tüm sütunlarınızı listeleyebilirsiniz.


6
+1 12.000'den fazla kayda gömülü iki yinelenen telefon numarası bulmak zorundaydım. DELETE öğesini SELECT olarak değiştirdi ve bu onları saniyeler içinde buldu. Bana bir ton zaman kazandım, teşekkürler.
shimonyk

3
Bu yaklaşım benim için işe yaramadı. Nedenini bilmiyorum. "SİL" i "SEÇİM *" ile değiştirdiğimde, silmek istediğim satırları döndürdü, ancak "SİL" ile yürüttüğümde süresiz olarak asılıydı.
aro_biz

Benimki de asılı ya da sadece çok uzun sürüyor. Yaklaşık 22 saattir koşuyor ve hala devam ediyor. Tablo 21M kayıtlarına sahiptir.
Cameron Castillo

Çok büyük bir veri kümeniz varsa ve mümkünse, WHERE deyimine daha fazla filtreleme eklemenizi öneririm, bu uzun süren sorgulara sahip insanlara yardımcı olabilir.
Ricardo Sanchez

2
Seçim çalışır, ancak silme işe yaramazsa, bunun sonucunda ortaya çıkan alt sorgunun boyutu olabilir. İlk olarak alt sorgu sonucuyla bir oluşturma tablosu yapmak, min (rowid) sütununda bir dizin oluşturmak ve daha sonra delete deyimini çalıştırmak ilginç olabilir.
Wouter

14

Gönderen Tom sor

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

(eksik parantez düzeltildi)


1
Parantez ifadesinde eksik. Sonunda olması gerektiğini mi sanıyorum?
Cameron Castillo

12

Gönderen DevX.com :

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

Burada sütun1, sütun2 vb. Kullanmak istediğiniz anahtardır.


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
En çok oy alan cevap yukarıdaki benim yorum yeniden, aslında benim sorunum çözüldü bu istek oldu.
aro_biz

2
Bu, devasa masalarda Bill'in çözümünden çok daha yavaş olacaktır.
Wouter

8

Çözüm 1)

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

Çözüm 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
               );

Çözüm 3)

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

6

t1'den farklı * olarak tablo t2'yi oluşturun;


cevap değil - distinct *1 sütunda en az 1 sembol farklı olan her kaydı alacaktır. Tek ihtiyacınız olan yalnızca birincil anahtarlar yapmak istediğiniz sütunlardan farklı değerler seçmek - Bill'in yanıtı bu yaklaşımın harika bir örneğidir.
Nogard

1
İhtiyacım olan buydu (tamamen aynı satırları kaldırın). Teşekkürler !
Emmanuel

Bu yöntemin bir başka dezavantajı, tablonuzun bir kopyasını oluşturmanız gerektiğidir. Büyük tablolar için bu, ek tablo alanı sağlama ve kopyadan sonra tablo alanını silme veya daraltma anlamına gelir. Bill'in yönteminin daha fazla faydası vardır ve ek dezavantajları yoktur.
Wouter

3

Döngü için bir imleç kullanarak küçük bir pl / sql bloğu yapmalı ve saklamak istemediğiniz satırları silmelisiniz. Örneğin:

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;

Düşündüğünüz yere inanıyorum PL / SQL SQL'de yapabildiğiniz zaman, merak ediyorsanız.
WW.

7
SQL'de yapabilmeniz, tek çözüm olduğu anlamına gelmez. Yalnızca SQL çözümünü gördükten sonra bu çözümü yayınladım. Oyların yanlış cevaplar olduğunu düşündüm.
Nick

3

Kopyaları seçmek için yalnızca sorgu biçimi olabilir:

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

Yani diğer öneriye göre doğru sorgu:

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

Bu sorgu veritabanında seçilen kriterler için en eski kaydı tutacak WHERE CLAUSE.

Oracle Sertifikalı Ortak (2008)


2

Gerçekten büyük masalar için en hızlı yol

  1. Aşağıdaki yapıya sahip istisna tablosu oluşturun: exceptions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. Yinelemeler tarafından ihlal edilecek benzersiz bir kısıtlama veya birincil anahtar oluşturmayı deneyin. Yinelemeleriniz olduğu için bir hata mesajı alırsınız. Özel durumlar tablosu, yinelenen satırlar için satır kimliğini içerecektir.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. Rowid tarafından exceptions_table ile tablonuza katılın ve dups silin

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. Silinecek satır sayısı büyükse, rowid ile istisna_tablo ile anti-join ile yeni bir tablo oluşturun (tüm hibeler ve dizinlerle) ve orijinal tabloyu original_dups tablosuna yeniden adlandırın ve new_table_with_no_dups'ı orijinal tabloya yeniden adlandırın

    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- kullanma

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

Kendini birleştirme

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

Merhaba Tandale, Okunabilirliği artırdığı için cevapları gönderirken lütfen kod biçimlendirme aracını kullanın.
NSNoob

2

Çözüm 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
);

Biraz açıklayabilir misin?
Dieter Meemken

bölümü ile yoğun sıralama, aynı sayıya sahip yinelenen satırlar için sıra verir, örneğin, her satır için tekli olarak 1, 1, 1 ve rowid oluşturmaya sahip üç satır, ve eşleşmeyen satır satırlarını silmeye çalışıyoruz.
DoOrDie

biz hem rütbe hem de dense_rank fonksiyonlarını kullanabilirsiniz ama rütbe bu senaryoda mükemmel çalışır düşünüyorum.
DoOrDie

2

1. çözüm

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

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

4. çözüm

 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. çözüm

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

ayrıca yinelenen kayıtları başka bir şekilde silebilirsiniz

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

Kertenkele Bill'in daha ayrıntılı cevabı ile aynı cevap.
Wouter

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

Yolunuz hakkında daha fazla bilgi ekleyebilir misiniz? Teşekkürler.
Muhabir

1

En iyi performans için şöyle yazdım:
(yürütme planına bakın)

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

Aşağıdaki komut dosyalarını kontrol edin -

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;    

Burada 6 kayıt göreceksiniz.
4. sorgunun altında koş -

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;

Yinelenen kayıtların silindiğini göreceksiniz.
Umarım bu sorgunuzu çözer. Teşekkürler :)


1

Ortak tablo ifadeleri ve pencere işlevleri kullanan herhangi bir yanıt görmedim. Çalışmak için en kolay bulduğum şey bu.

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

Unutulmaması gereken şeyler:

1) Yalnızca bölüm yan tümcesindeki alanlarda yineleme olup olmadığını kontrol ediyoruz.

2) Diğerlerinden bir kopya seçmek için bir nedeniniz varsa, bu satırın row_number () = 1 değerine sahip olmasını sağlamak için cümle ile sipariş kullanabilirsiniz.

3) N> = 1 ile final where yan tümcesini "Where RN> N" olarak değiştirerek korunan sayı kopyasını değiştirebilirsiniz (N = 0'ın kopyaları olan tüm satırları sileceğini düşünüyordum, ancak tüm satırları silecekti) .

4) Toplam bölümü alanına, her satırı gruptaki sayı satırlarıyla etiketleyecek CTE sorgusu eklendi. Bu nedenle, ilk öğe dahil, yinelenen satırları seçmek için "WHERE cnt> 1" kullanın.


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;

Bu yöntemin en büyük dezavantajı iç birleşmedir. Büyük tablolar için bu Bill'in yönteminden çok daha yavaş olacaktır. Ayrıca, bunu yapmak için PL / SQL kullanmak aşırıdır, bunu sadece sql kullanarak da kullanabilirsiniz.
Wouter

0

çözüm :

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
);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.