sql >> データベース >  >> RDS >> PostgreSQL

主キーテーブルの行が重複しています。

    もう一度、ブログの時間を大幅に減らします🙂

    「エラー:一意のインデックスを作成できませんでした
    詳細:テーブルに重複する値が含まれています。」

    このエラーは、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を実行することを忘れないでください。

    コメントを共有してください。


    1. MariaDB数値関数(全リスト)

    2. Djangoプロジェクトをsqlite3バックエンドからpostgresqlに切り替えると、データダンプの読み込みに失敗します

    3. 配列パラメータをネイティブクエリにバインドします

    4. カンマ区切りのデータ列を結合する