2009-07-17 19 views
6

Ho una tabella DB che consiste di 2,5 miliardi di record. Ci sono duplicati per un totale di 11 milioni. Qual è il modo più veloce per eliminare questi 11 milioni di record?Come eliminare più velocemente?

+0

Solo per visualizzare le prestazioni del sistema, la query per ottenere il conteggio delle righe duplicate ha richiesto 1 ora e 40 minuti. –

+0

Penso che l'OP abbia cancellato il suo account. – Shimmy

+0

Grazie ragazzi! Ho dovuto copiare i record univoci in una tabella, troncare la tabella originale e copiare i dati univoci. – Chattz

risposta

1

Prima mettere un indice sulla colonna o le colonne che definiscono e contengono i valori duplicati,

Poi, assumimg la tabella ha una chiave primaria (PK),

Delete Table T Where PK <> 
     (Select Min(PK) From Table 
     Where ColA = T.ColA 
      ... for each column in set defined above 
      And ColB = T.ColB) 

NOTA: potrebbe anche usare Max (PK), tutto quello che stai facendo è identificare un singolo record da non eliminare da ogni serie di duplicati

MODIFICA: per eliminare l'uso estensivo del log delle transazioni e della partizione UNDO, è possibile memorizzare i valori che sono dupes in una tabella temporanea e quindi eliminare i duplicati f o ciascuna coppia all'interno di una singola transazione ...

Supponendo solo una colonna (chiamare ColA, un numero) definisce i dupes ...

Create Table Dupes (ColA Number) 
    Insert Dupes(ColA) 
    Select Distinct ColA 
    From Table 
    Group By ColA 
    Having Count(*) > 1 

    recordExists Number := 0 ; 
    ColAValue Number; 
    Select Case When Exists (Select Count(*) From Dupes) 
    Then 1 Else 0 End Into recordExists From Dual; 


    While recordExists = 1 
     Loop 
     Select (Select Max(ColA) From Dupes) 
     Into ColAValue From Dual; 
     Begin Transaction 
      Delete Table T 
      Where ColA = ColAValue 
       And pk <> (Select Min(Pk) From Table 
          Where ColA = ColAValue); 
      Delete Dupes Where ColA = ColAValue; 
     Commit Transaction; 
     Select Case When Exists (Select Count(*) From Dupes) 
     Then 1 Else 0 End Into recordExists From Dual; 
     End Loop; 

Non testato, così sintassi può neeed massaggio ...

0

Se si è certi di non alterare l'integrità dei dati (integrità referenziale), disabilitare i vincoli (indici, altri vincoli), eseguire l'eliminazione, quindi abilitare i vincoli. Devi prima provare, per vedere se l'aggiornamento degli indici durante l'attivazione richiede meno tempo rispetto all'eliminazione con loro abilitata.

Anche l'ottimizzazione delle query può essere d'aiuto, ma senza conoscere ulteriori dettagli, stiamo discutendo in teoria.

+1

Non lasciare cadere l'indice sulle colonne che si stanno utilizzando per trovare duplicati, facendo ripetute scansioni di tabelle complete di 2.500.000.000 di righe sarà molto molto lento. – Richard

+0

Non eseguirà scansioni di tabelle ripetute, eseguirà hash semi join se non ci sono indici. – Quassnoi

3
DELETE 
FROM mytable 
WHERE rowid IN 
     (
     SELECT rowid 
     FROM (
       SELECT rowid, ROW_NUMBER() OVER (ORDER BY dupfield) rn 
       FROM mytable r 
       ) 
     WHERE rn > 1 
     ) 

o forse anche questo:

DELETE 
FROM mytable mo 
WHERE EXISTS 
     (
     SELECT NULL 
     FROM mytable mi 
     WHERE mi.dup_field = mo.dup_field 
       AND mi.rowid <> mo.rowid 
     ) 

Entrambe queste query userà abbastanza efficiente HASH SEMI JOIN, quest'ultimo sarà più veloce se non v'è alcun indice su dup_field.

Si può essere tentati di copiare le righe, ma è da notare che molto più REDO e UNDO informazioni saranno generati durante la copia di file 2G rispetto a quando la cancellazione 11M.

+1

come sono le prestazioni di un aggiornamento come questo quando la dimensione della tabella è di 2,5 miliardi? –

+0

Ho la sensazione che questa query sia lenta, ma potrebbe raggiungere ciò di cui l'OP ha bisogno. Può essere riscritto come join? –

+0

Ci sarà un ordinamento su 'dupfield' (se non ci sono indici su di esso), che può richiedere molto tempo. Il join su 'rowid' sarà un' HASH SEMI JOIN', che è una questione di minuti su '2G' contro' 11M' righe. Cancellare stesso richiederà anche decine di minuti, principalmente per generare 'REDO' e' UNDO'. – Quassnoi

20

Eliminare un duplicato da molti è un lavoro complicato e con tutti questi record si ha un problema.

Un'opzione è di trasformare il problema in testa e copiare i record che si desidera conservare in una nuova tabella. È possibile utilizzare la sintassi CREATE TABLE AS SELECT DISTINCT ... NOLOGGING, che copierà i record deduplicati senza utilizzare il log delle transazioni, che è molto più veloce. Una volta che la tua nuova tabella è popolata, cancella/rinomina la vecchia e rinominala nella nuova posizione.

Vedi http://www.databasejournal.com/features/oracle/article.php/3631361/Managing-Tables-Logging-versus-Nologging.htm

Oh, e ricordarsi di dargli un indice univoco nella nuova tabella per cui questo non accada di nuovo.

La morale della storia è ... mai uso DELETE per sbarazzarsi di un gran numero di record, è spaventosamente lento perché deve memorizzare tutti i record eliminati nel redo log. Copia-e-switch o TRUNCATE.

+8

... e potresti applicare lo stesso algoritmo al team che ha prodotto il prodotto che ha consentito solo 11.000.000 di righe duplicate ;-) Keith. – corlettk

+2

+1 per questa risposta. Sarei sicuramente tentato di creare una nuova copia del tavolo e inserirla in quello. La cosa fondamentale che aggiungerei è non inserire alcun indice su quella tabella secondaria fino a quando non hai copiato i dati - non vuoi che l'inutile hit di questo debba mantenere gli indici in alto durante l'inserimento dei dati.Mi piace anche questo approccio perché ha una rete di sicurezza extra: non devi liberarti del vecchio tavolo finché non sei sicuro al 100% di avere tutti i dati giusti. – AdaTheDev

+2

sarebbe interessante confrontare il tempo necessario per copiare 2.489 miliardi di record vs eliminare 11 milioni, usando gli stessi predicati –

2

Se eliminare le righe esistenti o creare una nuova tabella corretta e rilasciare quella precedente è più veloce dipende da molti fattori. 11 milioni di righe sono molte, ma è solo lo 0,5% del numero totale di righe nella tabella. È abbastanza probabile che la ricomposizione dello & calo possa essere molto più lenta dell'eliminazione, a seconda del numero di indici presenti nella tabella di origine, nonché del punto in cui le righe che devono essere eliminate esistono nelle pagine di dati.

Quindi c'è il problema se la tabella di origine è attiva o meno. Se ci sono inserimenti & in corso mentre è in corso la pulizia, la copia & non funzionerà senza una buona quantità di codice aggiuntivo per sincronizzare la tabella dopo il fatto.

Infine, perché è necessario che questa operazione sia "veloce"? È perché il sistema deve essere offline mentre si sta verificando il processo? È possibile scrivere una procedura che rimuove i duplicati mentre il sistema è attivo, ma non ha alcun impatto sul resto del sistema in termini di consumo di annullamento. Abbiamo risolto questo problema in passato dalla prima crei una query che raccoglie le chiavi primarie delle righe da rimosso in una seconda tabella, in questo modo:

INSERT 
    INTO RowsToDeleteTable 
    SELECT PKColumn 
    FROM SourceTable 
    WHERE <conditions used to find rows to remove> 

CREATE UNIQUE INDEX PK_RowsToDelete ON RowsToDeleteTable (PKColumn); 

allora abbiamo un blocco PL/SQL che o loop sopra le righe in un cursore in questo modo:

BEGIN 
    FOR theRow IN (SELECT PKColumn FROM RowsToDeleteTable ORDER BY 1) LOOP 
    <delete source table for theRow.PKColumn) 
    <optionally wait a bit> 
    commit; 
    END LOOP; 
END; 

o fa qualcosa di simile:

BEGIN 
    FOR theRow IN (SELECT MIN(PKColumn) FROM RowsToDeleteTable) LOOP 
    <delete source table for theRow.PKColumn) 
    <optionally wait a bit> 
    DELETE RowsToDeleteTable 
    WHERE PKColumn = theRow.PKColumn; 
    commit; 
    END LOOP; 
END; 

il looping e "SELECT MAX" è ovviamente meno efficiente, ma ha il vantaggio di permettere che si t o seguire l'avanzamento dell'operazione di cancellazione. Inseriamo un po 'di codice di attesa nel ciclo per permetterci di controllare la vigorosa operazione di mietitura.

La creazione iniziale di RowsToDeleteTable avviene molto rapidamente e si ha il vantaggio di consentire al processo di durare quanto si desidera. In questo caso, i "buchi" lasciati nelle estensioni dalle eliminazioni non saranno troppo negativi, dal momento che stai cancellando una percentuale così piccola dei dati totali.

Problemi correlati