sql >> Base de Datos >  >> RDS >> PostgreSQL

Filas duplicadas en una tabla de clave principal.

De vuelta otra vez, tengo menos tiempo para bloguear 🙂

"ERROR:no se pudo crear un índice único
DETALLE:la tabla contiene valores duplicados".

Postgres arroja este error cuando encuentra filas duplicadas en una tabla de clave principal al fallar cualquiera de estos comandos REINDEX o CREATE UNIQUE INDEX.

¿Por qué existen filas duplicadas en una tabla?

No estoy seguro exactamente 🙂 ni ninguna explicación comprobada...
Dos cosas en mi mente.

En primer lugar, es posible que se retrase la creación del índice o, si tiene secuencias compartidas en una base de datos, compartir en dos tablas de clave principal diferentes puede ser la causa al restaurar los datos en la tabla (pg_restore). En segundo lugar, si se está realizando una gran transacción en esa tabla y en el backend alguien detuvo abruptamente la instancia, lo que también podría fallar en el índice (clave principal) para apuntar a la fila correcta.

¿Cómo solucionarlo?

Bueno, como práctica común, cuando encontramos filas duplicadas en una tabla (a pesar de cualquier motivo), primero filtramos las filas duplicadas y las eliminamos, y luego al hacer REINDEX debería solucionar el problema.

Consulta para encontrar filas duplicadas:

select count(*),primary_column from table_name group by primary_column having count(*) > 1;

Incluso después de eliminar las filas duplicadas, REINDEX o CREATE UNIQUE INDEX fallan, significa que su índice no se limpia correctamente. Es posible que la consulta anterior no brinde un resultado 100% orientado a los resultados de lo que espera, porque la consulta seleccionará el índice que ya está dañado con filas duplicadas. Vea el plan de explicación a continuación.

postgres=# explain select count(*),id from duplicate_test group by id having count(*) > 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
GroupAggregate (cost=0.00..5042.90 rows=99904 width=4)
Filter: (count(*) > 1)
-> Index Scan using duplicate_test_pkey on duplicate_test (cost=0.00..3044.82 rows=99904 width=4)
(3 rows)

Necesitamos capturar CTID de filas duplicadas de la tabla principal y eliminarlas con una declaración condicional como CTID + PRIMARY KEY VALUE.

He jugado un poco con pg_catalogs para volatilizar la tabla de clave principal para reproducir el escenario con un error similar. (Por favor, no lo hagas)

postgres=# create unique index idup on duplicate_test(id);
ERROR: could not create unique index "idup"
DETAIL: Key (id)=(10) is duplicated.

Definición y datos de mi tabla:

postgres=# d duplicate_test
Table "public.duplicate_test"
Column | Type | Modifiers
--------+---------+-----------
id | integer | not null
name | text |
Indexes:
"duplicate_test_pkey" PRIMARY KEY, btree (id)

postgres=# select * from duplicate_test ;
id | name
----+---------
10 | Raghav ---Duplicate
20 | John H
30 | Micheal
10 | Raghav ---Duplicate
(4 rows)

Ahora, arreglemos esto….

Paso 1. Cree una nueva tabla a partir de la tabla afectada extrayendo solo dos valores de columna CTID y PRIMARY KEY.

postgres=# CREATE TABLE dupfinder AS SELECT ctid AS tid, id FROM duplicate_test;
SELECT 4

Paso 2. Ahora, ejecutemos la consulta del buscador de duplicados con CTID para obtener los duplicados exactos.

postgres=# select * from dupfinder x where exists (select 1 from dupfinder y where x.id = y.id and x.tid != y.tid);
tid | id
-------+----
(0,1) | 10
(0,5) | 10
(2 rows)

Paso 3. En el resultado anterior, ahora puede eliminar una fila de la tabla principal (tabla afectada) con CTID.

postgres=# delete from duplicate_test where ctid='(0,5)' and id=10;
DELETE 1

Paso 4. Ahora, su REINDEX o CREAR ÍNDICE ÚNICO será exitoso.

postgres=# create unique index idup on duplicate_test(id);
CREATE INDEX

postgres=# select * from duplicate_test ;
id | name
----+---------
10 | Raghav
20 | John H
30 | Micheal
(3 rows)

Paso 5. No olvide realizar un ANÁLISIS DE VACÍO inmediato en la mesa para actualizar los catálogos del sistema y el movimiento CTID.

Por favor, comparta sus comentarios.