2011-07-05 23 views
40

Tengo una tabla en una base de datos PostgreSQL 8.3.8, que no tiene claves/restricciones, y tiene varias filas con exactamente los mismos valores.Eliminar registros duplicados en PostgreSQL

Me gustaría eliminar todos los duplicados y guardar solo 1 copia de cada fila.

Hay una columna en particular (llamada "clave") que se puede usar para identificar duplicados (es decir, solo debería existir una entrada para cada "tecla" distinta).

¿Cómo puedo hacer esto? (idealmente con un solo comando SQL) La velocidad no es un problema en este caso (solo hay unas pocas filas).

Respuesta

39
DELETE FROM dupes a 
WHERE a.ctid <> (SELECT min(b.ctid) 
       FROM dupes b 
       WHERE a.key = b.key); 
+0

Perfecto, gracias! No sabía acerca de ctid –

+4

¡No lo use, es demasiado lento! –

+2

Si bien esta solución definitivamente funciona, @rapimo [la solución a continuación] (https://stackoverflow.com/a/12963112/1156554) se ejecuta mucho más rápido. Creo que esto tiene que ver con la instrucción de selección interna aquí que se ejecuta N veces (para todas las N filas en la tabla dupes) en lugar de la agrupación que está sucediendo en la otra solución. – David

4

me gustaría utilizar una tabla temporal:

create table tab_temp as 
select distinct f1, f2, f3, fn 
    from tab; 

A continuación, eliminar y cambiar el nombre tabtab_temp en tab.

+4

Este enfoque no tiene en cuenta los factores desencadenantes, índices y estadísticas. Ciertamente podrías agregarlos, pero agrega mucho más trabajo también. – Jordan

+0

No todo el mundo lo necesita. Este enfoque es extremadamente rápido y funcionó mucho mejor que el resto en correos electrónicos de 200k (varchar 250) sin índices. –

58

Una solución más rápida es

DELETE FROM dups a USING (
     SELECT MIN(ctid) as ctid, key 
     FROM dups 
     GROUP BY key HAVING COUNT(*) > 1 
    ) b 
     WHERE a.key = b.key 
     AND a.ctid <> b.ctid 
+14

¿Por qué es más rápido que la solución de a_horse_with_no_name? – Roberto

+1

Esto se ejecutó en menos de un segundo en mi prueba, en comparación con 53 minutos para la solución aceptada. – Alex

+0

¡Esto es más rápido! – Alfabravo

5

tuve que crear mi propia versión. La versión escrita por @a_horse_with_no_name es demasiado lenta en mi tabla (21M filas). Y @rapimo simplemente no elimina dups.

Esto es lo que uso en PostgreSQL 9,5

DELETE FROM your_table 
WHERE ctid IN (
    SELECT unnest(array_remove(all_ctids, actid)) 
    FROM (
     SELECT 
      min(b.ctid)  AS actid, 
      array_agg(ctid) AS all_ctids 
     FROM your_table b 
     GROUP BY key1, key2, key3, key4 
     HAVING count(*) > 1) c); 
11

He intentado esto:

DELETE FROM tablename 
WHERE id IN (SELECT id 
       FROM (SELECT id, 
          ROW_NUMBER() OVER (partition BY column1, column2, column3 ORDER BY id) AS rnum 
        FROM tablename) t 
       WHERE t.rnum > 1); 

proporcionada por Postgres wiki:

https://wiki.postgresql.org/wiki/Deleting_duplicates

+0

¿Alguna idea del rendimiento en comparación con la respuesta de @ rapimo y la aceptada (@a_horse_with_no_name)? – tuxayo

+1

Éste no funcionará si, como las preguntas declaran, _todas las columnas son idénticas, el 'id' incluido. – ibizaman

0

Esto funcionó bien para mí. Tenía una tabla, términos, que contenía valores duplicados. Se ejecutó una consulta para completar una tabla temporal con todas las filas duplicadas. Luego ejecuté una declaración de eliminación con esos identificadores en la tabla temporal. valor es la columna que contenía los duplicados.

 CREATE TEMP TABLE dupids AS 
     select id from (
        select value, id, row_number() 
over (partition by value order by value) 
    as rownum from terms 
       ) tmp 
        where rownum >= 2; 

delete from [table] where id in (select id from dupids) 
-2

funciona tanto para SQL NORMAL y PostgreSQL (también trabaja en AWS CORRIMIENTO AL ROJO)

DROP TABLE IF EXISTS backupOfTheTableContainingDuplicates; 

CREATE TABLE aNewEmptyTemporaryOrBackupTable 
AS SELECT DISTINCT * FROM originalTableContainingDuplicates; 

TRUNCATE TABLE originalTableContainingDuplicates; 

INSERT INTO originalTableContainingDuplicates SELECT * FROM 
aNewEmptyTemporaryOrBackupTable ; 

DROP TABLE aNewEmptyTemporaryOrBackupTable ; 

EXPLICACIÓN DE LA ANTERIOR SQL Script

Así,

La primera la consulta asegura, si tiene una copia de seguridad/tabla temporal de la tabla original que contiene g duplicados luego primero suelta esa tabla.

La segunda consulta, crea una nueva tabla (temporal/de respaldo) tabla con entradas únicas en la tabla original que contiene duplicados, por lo que la nueva tabla temporal es igual a la tabla original. MINIMA las entradas duplicadas.

La 3ª consulta, trunca o vacía la tabla original.

la 4ª consulta, inserta o copia todas las entradas únicas en la tabla temporal a la tabla original que se ha truncado recientemente (por lo que no tiene datos). Después de que se ejecute esta consulta, la tabla original se rellenará con datos ÚNICOS que estaban en la tabla temporal.

La quinta consulta, elimina/gotas de la tabla temporal innecesaria.

resultado final es Así, la tabla original tiene sólo entradas únicas y no hay duplicados.

5

Ésta es rápida y concisa:

DELETE FROM dupes T1 
    USING dupes T2 
WHERE T1.ctid < T2.ctid -- delete the older versions 
    AND T1.key = T2.key; -- add more columns if needed 
+0

esto es excelente! – user151496

Cuestiones relacionadas