2009-07-24 16 views
6

Qualcuno ha un'elegante istruzione sql per eliminare i record duplicati da una tabella, ma solo se ci sono più di x numeri di duplicati? Quindi consente fino a 2 o 3 duplicati, ma è così?Query SQL - Elimina i duplicati se più di 3 dups?

Attualmente ho una dichiarazione prescelta che fa la seguente:

delete table 
from table t 
left outer join (
select max(id) as rowid, dupcol1, dupcol2 
from table 
group by dupcol1, dupcol2 
) as keeprows on t.id=keeprows.rowid 
where keeprows.rowid is null 

Questa grande opera. Ma ora quello che mi piacerebbe fare è cancellare quelle righe solo se hanno più di 2 duplicati.

Grazie

+0

quando ci sono 5 i duplicati, volete solo una sinistra dopo la cancellazione, o tre? – Stobor

risposta

7
with cte as (
    select row_number() over (partition by dupcol1, dupcol2 order by ID) as rn 
    from table) 
delete from cte 
    where rn > 2; -- or >3 etc 

la query è la produzione di un 'numero di riga' per ogni record, raggruppati per il (dupcol1, dupcol2) e ordinato da ID . In effetti questo numero di riga conta 'duplicati' che hanno lo stesso dupcol1 e dupcol2 e assegna quindi il numero 1, 2, 3 .. N, ordine per ID. Se si desidera mantenere a soli 2 'duplicati', allora avete bisogno di eliminare quelli che sono stati assegnati i numeri 3,4,.. N e che è la parte curata dal DELLETE.. WHERE rn > 2;

Usando questo metodo è possibile modificare la ORDER BY per soddisfare il vostro preferito ordine (ad esempio ORDER BY ID DESC), in modo che lo LATEST abbia rn=1, quindi il più recente sia rn = 2 e così via. Il resto rimane lo stesso, lo DELETE rimuoverà solo quelli più vecchi in quanto hanno i numeri di riga più alti.

A differenza di this closely related question, poiché la condizione diventa più complessa, l'utilizzo di CTE e row_number() diventa più semplice. Le prestazioni potrebbero essere problematiche se non esiste un indice di accesso adeguato.

+0

Grazie Remus, ma siccome non sono un esperto sql e non familiare come le parole chiave specifiche del 2005, potresti spiegarmi cosa sta facendo la query? Suppongo che la partizione sia una bella scorciatoia per l'unione a sinistra in una tabella raggruppata, simile al mio primo esempio ?? Quindi la tua seconda riga restituisce un nuovo id di tutti i record duplicati in base alle colonne fornite? È il numero di volte in cui la riga è stata duplicata in base alle colonne nella seconda riga? Grazie. – Scott

+1

La query sta producendo un 'numero di riga' per ogni record, raggruppato per (dupcol1, dupcol2) e ordinato per ID. In effetti questo numero di riga conta 'duplicati' che hanno lo stesso dupcol1 e dupcol2 e assegna quindi il numero 1, 2, 3 .. N, ordine per ID. Se vuoi mantenere solo 2 "duplicati", allora devi cancellare quelli a cui sono stati assegnati i numeri 3,4, .. N e quella è la parte occupata dallo DELLETE .. WHERE rn> 2; HTH, fammi sapere se non è ancora chiaro. –

+0

No, ho capito, grazie mille. Un'ultima cosa però, voglio assicurarmi di mantenere sempre il record ULTIMO. Quindi, se tengo dei record con dire <2 duplicati e poi butto fuori tutti gli altri, come posso modificare la query per assicurare che i due o tre record più recenti (max (id)) della tabella vengano conservati. Ad esempio: diciamo che un record è elencato nel nostro sistema 10 volte. Questo viola la regola "2" duplicata. Vorremmo rimuovere 7 dei duplicati, lasciando solo un record principale e due duplicati. Per master record, intendiamo il ULTIMO (più aggiornato) record inserito nel sistema. – Scott

3

HAVING è tuo amico

select id, count(*) cnt from table group by id having cnt>2

0

piuttosto tardi, ma soluzione più semplice potrebbe essere la seguente supponiamo di avere tavolo emp_dept (EmpID, deptid), che ha le righe duplicate, Qui ho usato come @Count varibale .. per esempio 2 duplicato permesso poi @count = 2 Sul database Oracle

delete from emp_dept where @Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.rowid < emp_dept.rowid) 

On SQL Server o anydatabase che non supporta fila id dispongono po ', abbiamo bisogno di aggiungere colonna di identità solo per identificare ogni riga. dicono abbiamo aggiunto nid come identità al tavolo

alter table emp_dept add nid int identity(1,1) -- to add identity column 

ora query per eliminare duplicati potrebbe essere scritto come

delete from emp_dept where @@Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.nid< emp_dept.nid) 

Qui il concetto è eliminare tutte le righe per le quali esiste altre righe che hanno simili valori fondamentali ma numero n o maggiore di rowid o identità più piccoli. Quindi se esistono righe duplicate, una di esse con ID o ID di riga superiore verrà eliminata. e per la riga non c'è duplicato non riesce a trovare id di riga inferiore quindi non verrà eliminato.

0

Per Oracle:

delete from test where rowid = ANY (select min(test.rowid) from test left 
    outer join 
    (select min(rowid) row_id from test group by id,name)t on 
    test.rowid=t.row_id where t.row_id is null group by test.id,test.name); 
Problemi correlati