miércoles, octubre 14, 2009

Eliminando registros duplicados, desde SQL

Zas! Por un error de programación, de repente aparecieron un montón de registros duplicados, y cuando digo un montón, son un montón!!!

y ahora? pues, a pensar con cabeza fría... cuáles son las opciones?
  • borrar todos los duplicados a través del mismo sistema
  • pensar en un script en cualquier lenguaje que haga todo de un sólo halón
Por razones de seguridad de los datos, me inclino por la segunda opción... lo primero que pensé es en cómo se podía hacer con SQL

La tabla:
\d centro_poblado
Tabla «public.centro_poblado»
Columna | Tipo | Modificadores
---------+-----------------------+-------------------------------------------------------------
reg_001 | character varying(2) | not null
est_001 | character varying(2) | not null
mun_001 | character varying(2) | not null
par_001 | character varying(2) | not null
cpo_001 | character varying(3) | not null
cpo_002 | character varying(50) | not null
id | integer | not null default nextval('centro_poblado_id_seq'::regclass)

Como la tabla tiene un id de tipo serial, eso ayuda.

Verficiar cuáles son los registros repetidos:
SELECT COUNT(*), reg_001, est_001, mun_001, par_001 FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001 ORDER BY reg_001, est_001, mun_001, par_001;

Verificar los registros repetidos:
SELECT * FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05';

Contar los registros repetidos, en detalle:
SELECT count(*) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';

Verificar el id min y máx del detalle de los registros repetidos:
SELECT min(id), max(id) FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018';

La verdad es que luego descubrí que se podía hacer así:
SELECT * FROM centro_poblado AS a
WHERE EXISTS(
SELECT cpo_001, COUNT(cpo_001)
FROM centro_poblado
WHERE a.cpo_001= cpo_001
GROUP BY cpo_001
HAVING COUNT(cpo_001)>1)
ORDER BY cpo_001

Eliminar los registros repetidos, menos 1:
DELETE FROM centro_poblado WHERE reg_001 = '06' AND est_001 = '14' AND mun_001 = '01' AND par_001 = '05' AND cpo_001 = '018' AND id > 1;

Esto también se puede resolver con una tabla temporal, y metiendo la consulta en una función y todo eso, pero aun no lo he probado...

Finalmente, y gracias a la lista de usuarios de postgres y su histórico, me encontré [1]:
DELETE FROM centro_poblado WHERE ctid NOT IN (SELECT min(ctid) FROM centro_poblado GROUP BY cpo_002, reg_001, est_001, mun_001, par_001);

[1]http://archives.postgresql.org/pgsql-es-ayuda/2007-01/msg00353.php

1 comentario:

Cesar Carbonara dijo...

Desde [0] y gracias a huesos52 (Daniel) copio y pego:

create table temporal as select distinct * from repetidos;

Esta sentencia crea una tabla temporal y la llena con el resultado de una consulta. Si te
fijas, la consulta lleva antepuesta la palabra distinct, que sirve para establecer registros únicos sin repetición en la consulta. Puedes ver que al ejecutar

select distinct *from repetidos;

únicamente se muestran 5 registros.

Después de tener los registros sanos y salvos en la tabla temporal, se procede a borrar
los registros existentes en la tabla original.

Se llena la tabla original con el resultado de la tabla temporal con la sentencia

insert into repetidos select *from temporal;

En este momento, la tabla original
(repetidos en este caso) ya tiene los registros sin repetición.

Aca ya hemos cumplido el objetivo y para nada nos sirve la tabla temporal, es por eso
que se borra.

[0]http://www.forosdelweb.com/f99/eliminar-duplicados-722374/