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?

Juan
quelle

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.

Bill die Eidechse
quelle
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)

Toter Programmierer
quelle
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.

Kennzeichen
quelle
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)

quelle
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 ); 
DoOrDie
quelle
6

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

Mohammed khaled
quelle
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;
Nick
quelle
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)

user1799846
quelle
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 )
    )
user2158672
quelle
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 );
Dnyaneshwar Tandale
quelle
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
);
DoOrDie
quelle
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
);

quelle
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
    );
DoOrDie
quelle
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);
Md Wasi
quelle
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
quelle
1
DELETE FROM tableName  WHERE ROWID NOT IN (SELECT   MIN (ROWID) FROM table GROUP BY columnname);
JgSudhakar
quelle
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
);
user3655760
quelle
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
);
Enguerrand JORE
quelle
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 :)

Rakesh Roshan
quelle
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".

Darrel Lee
quelle
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;
Ashish Sinha
quelle
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
);
Sandeep Gupta
quelle