Entfernen doppelter Zeilen aus der Tabelle in Oracle


151

Ich teste etwas in Oracle und fülle eine Tabelle mit einigen Beispieldaten, aber dabei habe ich versehentlich doppelte Datensätze geladen, sodass ich jetzt mit einigen Spalten keinen Primärschlüssel erstellen kann.

Wie kann ich alle doppelten Zeilen löschen und nur eine davon belassen?

Antworten:


306

Verwenden Sie die rowidPseudospalte.

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

Wo column1, column2und column3die Identifizierungsschlüssel für jeden Datensatz bilden. Sie können alle Ihre Spalten auflisten.


6
+1 Ich musste zwei doppelte Telefonnummern finden, die in mehr als 12.000 Datensätzen vergraben waren. Das LÖSCHEN wurde in AUSWÄHLEN geändert und dies fand sie in Sekunden. Hat mir eine Menge Zeit gespart, danke.
Shimonyk

3
Dieser Ansatz hat bei mir nicht funktioniert. Ich weiß nicht warum. Als ich "DELETE" durch "SELECT *" ersetzte, wurden die Zeilen zurückgegeben, die ich löschen wollte, aber als ich mit "DELETE" ausgeführt habe, hing es nur auf unbestimmte Zeit.
aro_biz

Meins hängt auch entweder oder wird nur extrem lange ausgeführt. Läuft seit ungefähr 22 Stunden und geht immer noch. Tabelle enthält 21 Millionen Datensätze.
Cameron Castillo

Ich schlage vor, der WHERE-Anweisung eine weitere Filterung hinzuzufügen, wenn Sie über einen sehr großen Datensatz verfügen und wenn dies machbar ist, kann dies Leuten bei lang laufenden Abfragen helfen.
Ricardo Sanchez

2
Wenn die Auswahl funktioniert, das Löschen jedoch nicht, liegt dies möglicherweise an der Größe der resultierenden Unterabfrage. Es kann interessant sein, zuerst eine Erstelltabelle mit dem Ergebnis der Unterabfrage zu erstellen, einen Index für die Spalte min (Zeilen-ID) zu erstellen und dann die Anweisung delete auszuführen.
Wouter

15

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

(Die fehlende Klammer wurde behoben)


Klammer fehlt in Aussage. Ich nehme an, es sollte am Ende sein?
Cameron Castillo

12

Von DevX.com :

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

Wobei Spalte1, Spalte2 usw. der Schlüssel ist, den Sie verwenden möchten.


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
Zu meinem obigen Kommentar zur am besten bewerteten Antwort war es diese Anfrage, die mein Problem tatsächlich gelöst hat.
aro_biz

2
Dies wird auf großen Tischen viel langsamer sein als Bills Lösung.
Wouter

8

Lösung 1)

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

Lösung 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
               );

Lösung 3)

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

6

Erstellen Sie die Tabelle t2 als Auswahl, die * von t1 unterscheidet.


keine Antwort - distinct *nimmt jeden Datensatz, der sich in mindestens 1 Symbol in 1 Spalte unterscheidet. Sie müssen lediglich unterschiedliche Werte aus Spalten auswählen, für die Sie Primärschlüssel erstellen möchten. Bills Antwort ist ein hervorragendes Beispiel für diesen Ansatz.
Nogard

1
Das war es, was ich brauchte (völlig identische Zeilen entfernen). Vielen Dank !
Emmanuel

Ein weiterer Nachteil dieser Methode ist, dass Sie eine Kopie Ihrer Tabelle erstellen müssen. Bei großen Tabellen bedeutet dies, dass Sie einen zusätzlichen Tabellenbereich bereitstellen und den Tabellenbereich nach dem Kopieren löschen oder verkleinern. Bills Methode hat mehr Vorteile und keine zusätzlichen Nachteile.
Wouter

3

Sie sollten einen kleinen pl / sql-Block mit einem Cursor für die Schleife ausführen und die Zeilen löschen, die Sie nicht behalten möchten. Zum Beispiel:

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;

Ich glaube, die Ablehnung liegt darin, dass Sie PL / SQL verwenden, wenn Sie dies in SQL tun können, falls Sie sich fragen.
WW.

7
Nur weil Sie es in SQL tun können, heißt das nicht, dass es die einzige Lösung ist. Ich habe diese Lösung veröffentlicht, nachdem ich die Nur-SQL-Lösung gesehen hatte. Ich dachte, Stimmen wären für falsche Antworten.
Nick

3

Um die Duplikate auszuwählen, kann nur das Abfrageformat sein:

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

Die richtige Abfrage gemäß anderem Vorschlag lautet also:

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

Diese Abfrage speichert den ältesten Datensatz in der Datenbank für die in der WHERE CLAUSE .

Oracle Certified Associate (2008)


2

Der schnellste Weg für wirklich große Tische

  1. Erstellen Sie eine Ausnahmetabelle mit der folgenden Struktur: exception_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. Versuchen Sie, eine eindeutige Einschränkung oder einen Primärschlüssel zu erstellen, gegen die die Duplikate verstoßen. Sie erhalten eine Fehlermeldung, weil Sie Duplikate haben. Die Ausnahmetabelle enthält die Zeilen-IDs für die doppelten Zeilen.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. Verbinden Sie Ihre Tabelle mit Ausnahmen_Tabelle nach Zeilen-ID und löschen Sie Dups

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. Wenn die Anzahl der zu löschenden Zeilen groß ist, erstellen Sie eine neue Tabelle (mit allen Berechtigungen und Indizes), die mit Ausnahmen_Tabelle nach Zeilen-ID nicht verknüpft ist, und benennen Sie die ursprüngliche Tabelle in die Tabelle original_dups um und benennen Sie die Tabelle new_table_with_no_dups in die ursprüngliche Tabelle um

    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

Verwenden von rowid-

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

Verwenden von Self Join-

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

Hallo Tandale, bitte verwenden Sie das Code-Formatierungs-Tool, während Sie Antworten senden, da dies die Lesbarkeit erhöht.
NSNoob

2

Lösung 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
);

Kannst du ein bisschen erklären?
Dieter Meemken

dichter Rang mit Partition durch gibt den Rang für doppelte Zeilen mit derselben Nummer an, z. B. drei Zeilen mit Rang 1, 1, 1 und Zeilen-ID, die für jede Zeile als einheitlich erstellt werden, und wir versuchen, die nicht übereinstimmenden Zeilen-IDs zu löschen.
DoOrDie

Wir können sowohl Rank- als auch Dens_Rank-Funktionen verwenden, aber ich denke, Rank funktioniert in diesem Szenario perfekt.
DoOrDie

2

1. Lösung

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.Lösung

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

4. Lösung

 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. Lösung

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

Sie können doppelte Datensätze auch auf andere Weise löschen

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

Gleiche Antwort wie die ausführlichere Antwort von 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
);

Können Sie weitere Informationen zu Ihrem Weg hinzufügen? Vielen Dank.
Reporter

1

Für die beste Leistung habe ich Folgendes geschrieben:
(siehe Ausführungsplan)

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

Überprüfen Sie die folgenden Skripte -

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;    

Sie sehen hier 6 Datensätze.
4. Führen Sie die folgende Abfrage aus -

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;

Sie werden sehen, dass doppelte Datensätze gelöscht wurden.
Hoffe das löst deine Frage. Vielen Dank :)


1

Ich habe keine Antworten gesehen, die allgemeine Tabellenausdrücke und Fensterfunktionen verwenden. Dies ist das, womit ich am einfachsten arbeiten kann.

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

Zu beachten:

1) Wir prüfen nur, ob die Felder in der Partitionsklausel doppelt vorhanden sind.

2) Wenn Sie einen Grund haben, ein Duplikat gegenüber anderen auszuwählen, können Sie eine order by-Klausel verwenden, damit diese Zeile row_number () = 1 hat

3) Sie können das beibehaltene Nummernduplikat ändern, indem Sie die letzte where-Klausel in "Where RN> N" mit N> = 1 ändern (ich dachte, N = 0 würde alle Zeilen mit Duplikaten löschen, aber nur alle Zeilen) .

4) Dem Feld Summenpartition wurde die CTE-Abfrage hinzugefügt, die jede Zeile mit den Zahlenzeilen in der Gruppe kennzeichnet. Um also Zeilen mit Duplikaten auszuwählen, einschließlich des ersten Elements, verwenden Sie "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;

Ein Hauptnachteil dieser Methode ist die innere Verbindung. Bei großen Tischen ist dies viel langsamer als bei Bill. Die Verwendung von PL / SQL ist zu viel des Guten. Sie können dies auch verwenden, indem Sie einfach SQL verwenden.
Wouter

0

Lösung:

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
);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.