Rimozione di righe duplicate dalla tabella in Oracle


151

Sto testando qualcosa in Oracle e ho popolato una tabella con alcuni dati di esempio, ma nel processo ho caricato accidentalmente record duplicati, quindi ora non riesco a creare una chiave primaria usando alcune delle colonne.

Come posso eliminare tutte le righe duplicate e lasciarne solo una?

Risposte:


306

Usa la rowidpseudocolonna.

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

Dove column1, column2e column3crea la chiave identificativa per ciascun record. Potresti elencare tutte le tue colonne.


6
+1 Ho dovuto trovare due numeri di telefono duplicati sepolti in oltre 12.000 record. Modificato il CANC per selezionare e questo li trovò in pochi secondi. Mi hai salvato un sacco di tempo, grazie.
Shimonyk,

3
Questo approccio non ha funzionato per me. Non so perché. Quando ho sostituito "ELIMINA" con "SELEZIONA *", ha restituito le righe che volevo eliminare, ma quando l'ho eseguito con "ELIMINA" è rimasto sospeso indefinitamente.
aro_biz,

Il mio è anche sospeso o eseguito in modo estremamente lungo. Sono stato in esecuzione per circa 22 ore e ancora in corso. La tabella ha record 21M.
Cameron Castillo,

Suggerisco di aggiungere ulteriori filtri all'istruzione WHERE se si dispone di un set di dati molto grande e, se possibile, ciò potrebbe aiutare le persone con query a esecuzione prolungata.
Ricardo Sanchez,

2
Se la selezione funziona, ma l'eliminazione no, ciò potrebbe essere dovuto alla dimensione della sottoquery risultante. Potrebbe essere interessante innanzitutto creare una tabella di creazione con il risultato della sottoquery, creare un indice sulla colonna min (rowid), quindi eseguire l'istruzione delete.
Wouter,

14

Da chiedere a 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);

(risolta la parentesi mancante)


1
Parentesi mancante nell'istruzione. Presumo che dovrebbe essere alla fine?
Cameron Castillo,

12

Da DevX.com :

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

Dove colonna1, colonna2, ecc. È la chiave che si desidera utilizzare.


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
Per quanto riguarda il mio commento sopra sulla risposta più votata, è stata proprio questa richiesta a risolvere il mio problema.
aro_biz,

2
Questo sarà molto più lento su enormi tavoli rispetto alla soluzione di Bill.
Wouter,

8

Soluzione 1)

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

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

Soluzione 3)

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

6

creare la tabella t2 come selezionare distinto * da t1;


non una risposta - distinct *prenderà ogni record che differisce per almeno 1 simbolo in 1 colonna. Tutto ciò che serve è selezionare valori distinti solo dalle colonne che si desidera creare chiavi primarie: la risposta di Bill è un ottimo esempio di questo approccio.
Nogard,

1
Era quello di cui avevo bisogno (rimuovi le linee del tutto identiche). Grazie !
Emmanuel,

Un altro svantaggio di questo metodo è che devi creare una copia della tua tabella. Per le tabelle di grandi dimensioni, ciò implica fornire ulteriore tablespace e cancellare o ridurre il tablespace dopo la copia. Il metodo di Bill ha più vantaggi e nessun ulteriore svantaggio.
Wouter,

3

Dovresti fare un piccolo blocco pl / sql usando un cursore per loop ed eliminare le righe che non vuoi conservare. Per esempio:

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;

Credo che il downvote sia perché stai usando PL / SQL quando puoi farlo in SQL, nel caso ti stia chiedendo.
WW.

7
Solo perché puoi farlo in SQL, non significa che sia l'unica soluzione. Ho pubblicato questa soluzione, dopo aver visto la soluzione solo SQL. Ho pensato che i voti negativi fossero risposte errate.
Nick,

3

Per selezionare i duplicati, solo il formato della query può essere:

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

Quindi la query corretta come da altri suggerimenti è:

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

Questa query manterrà il record più vecchio nel database per i criteri scelti nel file WHERE CLAUSE .

Oracle Certified Associate (2008)


2

Il modo più veloce per tavoli davvero grandi

  1. Crea una tabella delle eccezioni con la struttura seguente: exceptions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. Prova a creare un vincolo univoco o una chiave primaria che verrà violata dai duplicati. Riceverai un messaggio di errore perché hai duplicati. La tabella delle eccezioni conterrà i rowid per le righe duplicate.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. Unisciti al tuo tavolo con exceptions_table per rowid ed elimina i duplicati

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. Se la quantità di righe da eliminare è grande, crea una nuova tabella (con tutte le concessioni e gli indici) anti-join con exceptions_table per rowid e rinomina la tabella originale nella tabella original_dups e rinomina new_table_with_no_dups nella tabella originale

    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

Utilizzando rowid-

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

Utilizzando self join-

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

Ciao Tandale, usa lo strumento di formattazione del codice durante l'invio delle risposte in quanto aumenta la leggibilità.
NSNoob,

2

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

Puoi spiegarci un po '?
Dieter Meemken,

il rango denso con partizione dà il rango di righe duplicate con lo stesso numero, ad esempio tre righe con rango 1, 1, 1 e rowid create per ogni riga come unic e stiamo provando a eliminare quei rowid che non corrispondono.
DoOrDie,

possiamo usare sia le funzioni rank che dense_rank ma penso che il rank funzioni perfettamente in questo scenario.
DoOrDie,

2

1. soluzione

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

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

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

e puoi anche eliminare i record duplicati in un altro modo

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

Stessa risposta della risposta più elaborata di 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
);

Puoi aggiungere ulteriori informazioni sulla tua strada? Grazie.
Reporter,

1

Per le migliori prestazioni, ecco cosa ho scritto:
(vedi piano di esecuzione)

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

Controlla sotto gli script -

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;    

Vedrai qui 6 record.
4. run sotto query -

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;

Vedrai che i record duplicati sono stati eliminati.
Spero che questo risolva la tua domanda. Grazie :)


1

Non ho visto alcuna risposta che utilizza espressioni di tabella comuni e funzioni di finestra. Questo è ciò che trovo più semplice con cui lavorare.

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

Qualcosa da notare:

1) Stiamo solo verificando la duplicazione sui campi nella clausola di partizione.

2) Se hai qualche motivo per scegliere un duplicato rispetto ad altri, puoi usare una clausola order by per fare in modo che quella riga abbia row_number () = 1

3) È possibile modificare il numero duplicato conservato modificando la clausola where finale in "Where RN> N" con N> = 1 (pensavo che N = 0 avrebbe eliminato tutte le righe che hanno duplicati, ma avrebbe semplicemente eliminato tutte le righe) .

4) Aggiunta nel campo della partizione Somma la query CTE che taggerà ogni riga con il numero di righe nel gruppo. Quindi, per selezionare le righe con i duplicati, incluso il primo elemento, usa "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;

Uno svantaggio principale di questo metodo è il join interno. Per i tavoli più grandi questo sarà molto più lento del metodo di Bill. Inoltre, l'utilizzo di PL / SQL per fare ciò è eccessivo, è possibile utilizzarlo anche semplicemente usando sql.
Wouter,

0

soluzione:

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
);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.