2012-01-22 16 views
9

Tengo un modelo en el que debido a una falla en el código, hay filas duplicadas. Ahora necesito eliminar cualquier duplicado de la base de datos.Eliminar filas duplicadas en Django DB

Cada fila debe tener un único photo_id. ¿Hay una manera simple de eliminarlos? O tengo que hacer algo como esto:

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

Sería mejor, en adelante, definir ese campo como único en el esquema de su base de datos. Entonces eliminas este problema de que ocurra alguna vez. De hecho, debe agregar tales detalles a todo su esquema de base de datos. – Keith

Respuesta

21

¡La manera más simple es la manera más simple! Especialmente para uno de los scripts donde el rendimiento ni siquiera importa (a menos que lo haga). Dado que no es un código central, solo escribiría lo primero que se me ocurra y funciona.

# 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() 

Como siempre, una copia de seguridad antes de hacer estas cosas.

+1

Gracias. ¿Conoces una consulta que simplemente me muestre qué filas son dúplex? Sé que distinto me mostrará el db sin dúplex, pero ¿qué me mostrará solo dúplex? – Brenden

+0

'SELECT * FROM table GROUP BY photo_id TENIENDO COUNT (photo_id)> 1;' –

+0

@brenden, en lugar de eliminar las filas, ¿podría agregarlas a una lista? Eliminé mi segunda consulta porque olvidé que coincidiría tanto con los duplicados como con los no duplicados ... ¡cierra uno! –

10

Esto puede ser más rápido porque evita el filtro interno para cada fila en MyModel.

Dado que los identificadores son únicos, si los modelos son ordenados por ellos en orden creciente, podemos hacer un seguimiento de la última identificación que vimos y mientras caminamos sobre las filas si vemos un modelo con la misma identificación, debe ser un duplicado, entonces podemos 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

Con respecto al rendimiento, ¡esa es definitivamente la mejor opción! ¡Gracias, necesitábamos esto para una gran base de datos! –

+0

Funciona bien para convertir una tabla para tener una restricción 'unique_together' también, ¡gracias! – mlissner

+0

Otra buena característica es que le permite ordenar por otro campo para mantener los que desea mantener en la parte superior de los grupos de estafadores. – hobs

3

Aquí es una solución rápida:

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()] 

ahora se puede hacer:

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

o si ids_list era demasiado grande para ser manejado por su DBMS

se puede segmentarlo en trozos que puedan ser manejados por él:

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

Esto eliminará solo 1 de los duplicados.Por lo tanto, tendrías que hacer esto recursivamente si hubiera más de 2 filas. Por lo tanto, puede no ser más rápido que las otras soluciones. – hobs

Cuestiones relacionadas