PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Duplicar linhas em uma tabela de chave primária.

De volta, ficando muito menos tempo para blogar 🙂

“ERRO:não foi possível criar índice exclusivo
DETALHE:A tabela contém valores duplicados.”

Este erro é lançado pelo Postgres quando encontra linhas duplicadas em uma tabela de chave primária falhando em qualquer um desses comandos REINDEX ou CREATE UNIQUE INDEX.

Por que existem linhas duplicadas em uma tabela?

Não tenho certeza exatamente 🙂 nem nenhuma explicação comprovada…
Duas coisas em minha mente.

Em primeiro lugar, pode haver atraso na criação do índice ou, se você compartilhou sequências em um banco de dados, o compartilhamento em duas tabelas de chave primária diferentes pode ser a causa ao restaurar os dados na tabela (pg_restore). Em segundo lugar, se alguma transação enorme estiver ocorrendo nessa tabela e no back-end alguém interrompeu abruptamente a instância, o que também pode fazer com que o índice (chave primária) aponte para a linha correta.

Como corrigi-lo?

Bem, como prática comum, quando encontramos linhas duplicadas em uma tabela (apesar de qualquer motivo), primeiro filtramos as linhas duplicadas e as excluímos, e depois fazendo REINDEX deve corrigir o problema.

Consulta para encontrar linhas duplicadas:
select count(*),primary_column from table_name group by primary_column having count(*) > 1;

Mesmo após a exclusão das linhas duplicadas REINDEX ou CREATE UNIQUE INDEX falhar, isso significa que seu índice não foi limpo corretamente. A consulta acima pode não estar fornecendo 100% de saída orientada a resultados do que você espera, porque a consulta escolherá o índice que já está corrompido com linhas duplicadas. Veja o plano de explicação abaixo.
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)

Precisamos pegar CTID de linhas duplicadas da tabela principal e excluir com instrução condicional como CTID + PRIMARY KEY VALUE.

Eu joguei um pouco com pg_catalogs para voilate Primary Key Table para reproduzir o cenário com erro semelhante. (Por favor, não)
postgres=# create unique index idup on duplicate_test(id);
ERROR: could not create unique index "idup"
DETAIL: Key (id)=(10) is duplicated.

Definição e dados da minha tabela:
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)

Agora, vamos corrigir isso….

Etapa 1. Crie uma nova tabela a partir da tabela efetuada puxando apenas dois valores de coluna CTID e PRIMARY KEY.
postgres=# CREATE TABLE dupfinder AS SELECT ctid AS tid, id FROM duplicate_test;
SELECT 4

Etapa 2. Agora, vamos executar a consulta do localizador de duplicatas com CTID para obter as duplicatas exatas.
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)

Etapa 3. No resultado acima, agora você pode excluir uma linha da tabela principal (tabela efetivada) com CTID.
postgres=# delete from duplicate_test where ctid='(0,5)' and id=10;
DELETE 1

Etapa 4. Agora, seu REINDEX ou CREATE UNIQUE INDEX será bem sucedido.
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)

Etapa 5. Não se esqueça de fazer VACUUM ANALYZE imediato na mesa para atualizar os catálogos do sistema, bem como o movimento CTID.

Por favor, compartilhe seus comentários.