「エラー:一意のインデックスを作成できませんでした
詳細:テーブルに重複する値が含まれています。」
このエラーは、PostgresがこれらのコマンドREINDEXまたはCREATE UNIQUE INDEXのいずれかに失敗して、主キーテーブルで重複する行を検出した場合にスローされます。
テーブルに重複する行が存在するのはなぜですか?
正確にはわからない🙂または証明された説明はありません…
私の心に2つのこと。
まず、インデックスの作成が遅れたり、データベースでシーケンスを共有している場合は、データをテーブル(pg_restore)に復元するときに、2つの異なる主キーテーブルで共有することが原因である可能性があります。次に、そのテーブルで巨大なトランザクションが発生していて、バックエンドで誰かがインスタンスを突然停止した場合、インデックス(主キー)が正しい行を指すことができない可能性があります。
修正方法は?
よくあることですが、テーブルに重複する行が見つかった場合(何らかの理由で)、最初に重複する行をフィルタリングして削除し、後でREINDEXを実行することで問題を修正する必要があります。
重複する行を見つけるためのクエリ:
select count(*),primary_column from table_name group by primary_column having count(*) > 1;
重複する行の削除REINDEXまたはCREATEUNIQUEINDEXが失敗した後でも、インデックスが適切にクリーンアップされていないことを意味します。上記のクエリは、重複した行ですでに破損しているインデックスを選択するため、期待どおりの100%の結果指向の出力が得られない可能性があります。以下の説明プランを参照してください。
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)
メインテーブルから重複行のCTIDをキャッチし、CTID + PRIMARYKEYVALUEとして条件ステートメントを使用して削除する必要があります。
同様のエラーでシナリオを再現するために、主キーテーブルを無効にするためにpg_catalogsで少し遊んだことがあります。 (しないでください)
postgres=# create unique index idup on duplicate_test(id);
ERROR: could not create unique index "idup"
DETAIL: Key (id)=(10) is duplicated.
マイテーブルの定義とデータ:
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)
では、これを修正しましょう…。
ステップ1。 CTIDとPRIMARYKEYの2つの列値のみをプルして、影響を受けるテーブルから新しいテーブルを作成します。
postgres=# CREATE TABLE dupfinder AS SELECT ctid AS tid, id FROM duplicate_test;
SELECT 4
ステップ2。 次に、CTIDを使用して重複ファインダークエリを実行し、正確な重複を取得しましょう。
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)
ステップ3。 上記の結果により、CTIDを使用してメインテーブル(影響を受けるテーブル)から1行を削除できるようになりました。
postgres=# delete from duplicate_test where ctid='(0,5)' and id=10;
DELETE 1
ステップ4。 これで、REINDEXまたはCREATEUNIQUEINDEXが成功します。
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)
ステップ5。 システムカタログとCTIDの移動を更新するために、テーブルですぐにVACUUMANALYZEを実行することを忘れないでください。
コメントを共有してください。