„BŁĄD:nie można utworzyć unikalnego indeksu
SZCZEGÓŁY:Tabela zawiera zduplikowane wartości”.
Ten błąd jest wyrzucany przez Postgres, gdy napotka zduplikowane wiersze w tabeli kluczy podstawowych w wyniku niepowodzenia któregokolwiek z tych poleceń REINDEX lub CREATE UNIQUE INDEX.
Dlaczego w tabeli istnieją zduplikowane wiersze?
Nie jestem pewien dokładnie 🙂 ani żadnych udowodnionych wyjaśnień…
Moim zdaniem dwie rzeczy.
Po pierwsze, może to być opóźnione tworzenie indeksu lub jeśli masz wspólne sekwencje w bazie danych, współdzielenie w dwóch różnych tabelach kluczy podstawowych może być przyczyną podczas przywracania danych do tabeli (pg_restore). Po drugie, jeśli na tym stole ma miejsce jakakolwiek duża transakcja, a na zapleczu ktoś nagle zatrzymał instancję, co może również spowodować, że indeks (klucz podstawowy) nie będzie wskazywał właściwego wiersza.
Jak to naprawić?
Cóż, zgodnie z powszechną praktyką, gdy napotykamy zduplikowane wiersze w tabeli (mimo jakiegokolwiek powodu), najpierw filtrujemy zduplikowane wiersze i usuwamy je, a później, wykonując REINDEX, powinniśmy rozwiązać problem.
Zapytanie o znalezienie zduplikowanych wierszy:
select count(*),primary_column from table_name group by primary_column having count(*) > 1;
Nawet po usunięciu zduplikowanych wierszy REINDEX lub CREATE UNIQUE INDEX nie powiedzie się, oznacza to, że indeks nie jest prawidłowo czyszczony. Powyższe zapytanie może nie dawać w 100% zorientowanych na wynik wyników, których oczekujesz, ponieważ zapytanie wybierze indeks, który jest już uszkodzony przez zduplikowane wiersze. Zobacz plan wyjaśnień poniżej.
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)
Musimy przechwycić CTID zduplikowanych wierszy z głównej tabeli i usunąć za pomocą instrukcji warunkowej jako CTID + WARTOŚĆ KLUCZU PODSTAWOWEGO.
Bawiłem się trochę z pg_catalogs, aby zmienić tabelę kluczy podstawowych w celu odtworzenia scenariusza z podobnym błędem. (Proszę, nie rób tego)
postgres=# create unique index idup on duplicate_test(id);
ERROR: could not create unique index "idup"
DETAIL: Key (id)=(10) is duplicated.
Definicja i dane mojej tabeli:
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)
Teraz naprawmy to….
Krok 1. Utwórz nową tabelę z tabeli, której dotyczy, pobierając tylko dwie wartości kolumn CTID i PRIMARY KEY.
postgres=# CREATE TABLE dupfinder AS SELECT ctid AS tid, id FROM duplicate_test;
SELECT 4
Krok 2. Teraz uruchommy zapytanie wyszukiwarki duplikatów z CTID, aby uzyskać dokładne duplikaty.
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)
Krok 3. Na powyższym wyniku możesz teraz usunąć jeden wiersz z głównej tabeli (tabeli z efektami) z CTID.
postgres=# delete from duplicate_test where ctid='(0,5)' and id=10;
DELETE 1
Krok 4. Teraz Twój REINDEX lub CREATE UNIQUE INDEX odniesie sukces.
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)
Krok 5. Nie zapomnij wykonać natychmiastowej ANALIZY PRÓŻNIowej na stole, aby zaktualizować katalogi systemowe oraz ruch CTID.
Podziel się swoimi komentarzami.