Eliminar filas duplicadas de la base de datos sqlite

91

Tengo una tabla enorme, 36 millones de filas, en SQLite3. En esta tabla muy grande, hay dos columnas:

  • hash - texto
  • d - real

Algunas de las filas están duplicadas. Es decir, ambos hashy dtienen los mismos valores. Si dos hashes son idénticos, también lo son los valores de d. Sin embargo, dos idénticos dno implica dos idénticos hash.

Quiero eliminar las filas duplicadas. No tengo una columna de clave principal.

¿Cuál es la forma más rápida de hacer esto?

Parches
fuente
Coloque las respuestas en los bloques de respuestas. Más tarde, puede aceptar su propia respuesta. Consulte también ¿Cómo funciona aceptar una respuesta?
jww

Respuestas:

121

Necesita una forma de distinguir las filas. Según su comentario, podría usar la columna de identificador de fila especial para eso.

Para eliminar duplicados manteniendo el más bajo rowidpor (hash,d):

delete   from YourTable
where    rowid not in
         (
         select  min(rowid)
         from    YourTable
         group by
                 hash
         ,       d
         )
Andomar
fuente
SQLite no le permite agregar una columna de clave principal, ¿verdad?
Parches del
sqlite> alter table dist add id integer primary key autoincrement; Error: Cannot add a PRIMARY KEY column
Parches del
¡Interesante! La parte que necesita es el autoincrementaunque, ¿funciona si omite la primary keyparte?
Andomar
sqlite> alter table dist add id integer autoincrement; Error: near "autoincrement": syntax error Editar: SQLite tiene un tipo de pseudo columna "rowid" que está automáticamente allí, ¿podría usar esto?
Parches del
1
delete from dist where rowid not in (select max(rowid) from dist group by hash); ¡Parece hacer el truco! Gracias.
Parches del
5

Supongo que lo más rápido sería usar la base de datos misma: agregue una nueva tabla con las mismas columnas, pero con las restricciones adecuadas (¿un índice único en hash / par real?), Iterar a través de la tabla original e intentar insertar registros en la nueva tabla, ignorando los errores de violación de restricciones (es decir, continuar iterando cuando se generen excepciones).

Luego elimine la tabla anterior y cambie el nombre de la nueva por la anterior.

MaDa
fuente
No es tan elegante como simplemente alterar la tabla, supongo, PERO una cosa realmente buena de su enfoque es que puede volver a ejecutarlo tantas veces como desee sin tocar / destruir los datos de origen hasta que esté absolutamente satisfecho con los resultados .
Adrian K
1

Si agregar una clave principal no es una opción, entonces un enfoque sería almacenar los duplicados DISTINCT en una tabla temporal, eliminar todos los registros duplicados de la tabla existente y luego agregar los registros nuevamente a la tabla original desde la tabla temporal .

Por ejemplo (escrito para SQL Server 2008, pero la técnica es la misma para cualquier base de datos):

DECLARE @original AS TABLE([hash] varchar(20), [d] float)
INSERT INTO @original VALUES('A', 1)
INSERT INTO @original VALUES('A', 2)
INSERT INTO @original VALUES('A', 1)
INSERT INTO @original VALUES('B', 1)
INSERT INTO @original VALUES('C', 1)
INSERT INTO @original VALUES('C', 1)

DECLARE @temp AS TABLE([hash] varchar(20), [d] float)
INSERT INTO @temp
SELECT [hash], [d] FROM @original 
GROUP BY [hash], [d]
HAVING COUNT(*) > 1

DELETE O
FROM @original O
JOIN @temp T ON T.[hash] = O.[hash] AND T.[d] = O.[d]

INSERT INTO @original
SELECT [hash], [d] FROM @temp

SELECT * FROM @original

No estoy seguro de si sqlite tiene una ROW_NUMBER()función de tipo, pero si la tiene, también puede probar algunos de los enfoques enumerados aquí: Elimine registros duplicados de una tabla SQL sin una clave principal

rsbarro
fuente
+1, aunque no estoy seguro de si sqlite admite la delete <alias> from <table> <alias>sintaxis
Andomar