web-dev-qa-db-ja.com

重複する行-1つを削除する方法?

OS MasterMapデータを含むかなり大きなテーブル(約1億1,400万行)があります。これは、新しいテーブルに新しく読み込まれたデータです。主キーを設定しようとすると、次のエラーが発生します。

ERROR:  could not create unique index "tbl_os_mmap_topoarea_pkey"
DETAIL:  Key (toid)=(1000000004081308) is duplicated.

どういうわけか、私は正確に複製された行で終わりました。これらの2つの行のすべてのフィールドは同じです。 1つの行を削除したいが、もう1つの行は保持したい。 2つを区別する方法がないので、これをどのように行うことができますか?

これをできるだけ迅速かつ簡単に行いたいと思います。このサイズのデータ​​セットでは時間がかかりすぎるため、一時テーブルなどの作成は実際にはオプションではありません。新しい一意のID列を作成する方が速いと思いますが、おそらく多少時間がかかります。

少し調べたところ、postgresのすべてのレコードに非表示の一意のIDであるctidがあることがわかりました。これを使用して重複行の1つを削除できますか?

5
Matt

これはうまくいくと思います:

with d as 
  ( select ctid, row_number() over (partition by t.*) as rn 
    from tablename as t 
  ) 
delete from tablename as t 
using d 
where d.rn > 1 
  and d.ctid = t.ctid ;

そして別のバリエーション。どちらがより効率的かわかりません:

delete from tablename as t 
where exists 
      ( select * 
        from tablename as d 
        where d.ctid > t.ctid 
          and d.* is not distinct from t.*
      ) ;

しかし、ドキュメントが「ctid」と言っていることに注意してください:

ctid

テーブル内の行バージョンの物理的な場所。 ctidを使用すると、行のバージョンをすばやく見つけることができますが、VACUUM FULLによって行が更新または移動されると、行のctidが変更されます。したがって、ctidは長期的な行識別子としては役に立ちません。論理行を識別するには、OID、またはそれ以上のユーザー定義のシリアル番号を使用する必要があります。

したがって、テーブルがWITH OIDSで作成された場合は、代わりにそれを使用してください。

9
ypercubeᵀᴹ