2012-01-22 19 views
9

Ho un modello in cui a causa di un bug di codice, ci sono righe duplicate. Ora ho bisogno di eliminare eventuali duplicati dal database.Elimina righe duplicate in Django DB

Ogni riga deve avere un photo_id univoco. C'è un modo semplice per rimuoverli? O devo fare qualcosa di simile:

rows = MyModel.objects.all() 
for row in rows: 
    try: 
     MyModel.objects.get(photo_id=row.photo_id) 
    except: 
     row.delete() 
+0

Sarebbe meglio, in futuro, definire tale campo come univoco nello schema del database. Quindi elimini questo problema da sempre in atto. In effetti, è necessario aggiungere tali dettagli a tutti gli schemi del database. – Keith

risposta

21

Il modo più semplice è il modo più semplice! Soprattutto per script off-off in cui le prestazioni non contano (a meno che non lo facciano). Dal momento che non è un codice base, vorrei semplicemente scrivere la prima cosa che mi viene in mente e funziona.

# assuming which duplicate is removed doesn't matter... 
for row in MyModel.objects.all(): 
    if MyModel.objects.filter(photo_id=row.photo_id).count() > 1: 
     row.delete() 

Come sempre, eseguire il backup prima di fare questa roba.

+1

Grazie. Conosci una query che mi mostrerebbe solo quali file sono duplicati? Lo so distinto mi mostrerà il db senza duplicati, ma cosa mi farà vedere solo i dups? – Brenden

+0

'SELECT * FROM tabella GROUP BY photo_id HAVING COUNT (photo_id)> 1;' –

+0

@brenden, invece di eliminare le righe, è possibile aggiungerle a un elenco? Ho rimosso la mia seconda query perché ho dimenticato che avrebbe dovuto corrispondere sia ai duplicati che ai non duplicati ... chiudi uno! –

10

Questo può essere più veloce perché evita il filtro interno per ogni riga in MyModel.

Poiché gli ID sono unici, se i modelli vengono ordinati in ordine crescente, possiamo tenere traccia dell'ultimo ID che abbiamo visto e mentre camminiamo sulle righe se vediamo un modello con lo stesso id, deve essere un duplicato, quindi possiamo eliminarlo.

lastSeenId = float('-Inf') 
rows = MyModel.objects.all().order_by('photo_id') 

for row in rows: 
    if row.photo_id == lastSeenId: 
    row.delete() # We've seen this id in a previous row 
    else: # New id found, save it and check future rows for duplicates. 
    lastSeenId = row.photo_id 
+2

Per quanto riguarda le prestazioni, questa è sicuramente la scelta migliore! Grazie, ne avevamo bisogno per un grande database! –

+0

Funziona bene anche per convertire una tabella con un vincolo 'unique_together', grazie! – mlissner

+0

Un'altra bella caratteristica è che ti permette di ordinare da un altro campo per mantenere quelli che vuoi mantenere in cima ai gruppi di dupes! – hobs

3

Ecco una soluzione rapida:

from django.db import connection 

query = "SELECT id FROM table_name GROUP BY unique_column HAVING COUNT(unique_column)>1" 
cursor = connection.cursor() 
cursor.execute(query) 
ids_list = [item[0] for item in cursor.fetchall()] 

ora si può fare:

Some_Model.objects.filter(id__in=ids_list).delete() 

o se ids_list era troppo grande per essere gestito dal DBMS

voi può segmentarlo in blocchi che possono essere gestiti da esso:

seg_length = 100 
ids_lists = [ids_list[x:x+seg_length] for x in range(0,len(ids_list),seg_length)] 
for ids_list in ids_lists: 
    SomeModel.objects.filter(id__in=ids_list).delete() 
+0

Questo eliminerà solo 1 dei duplicati.Quindi dovresti farlo in modo ricorsivo se ci fossero più di 2 righe. Quindi potrebbe non essere più veloce delle altre soluzioni. – hobs