postgisテーブルの重複するジオメトリを削除する


11

後-何が起こったのかわかりません-PostGISテーブルのすべてのエントリが2倍になりました!私はこれを削除しようとしましたが、重複は削除されません:

DELETE FROM planet_osm_point
       WHERE osm_id NOT IN (SELECT min(osm_id)
                        FROM planet_osm_point
                        GROUP BY osm_id)

またはこれ:

DELETE FROM planet_osm_point
WHERE osm_id NOT IN (
    select max(dup.osm_id)
    from planet_osm_point as dup
    group by way);

編集:

私はついに簡単な方法を見つけました、それは私の場合に機能しています:

DELETE FROM planet_osm_point WHERE ctid NOT IN
(SELECT max(ctid) FROM planet_osm_point GROUP BY osm_id);

このページにあります:http : //technobytz.com/most-useful-postgresql-commands.html


1
現在のplanet_osm_pointテーブル構造を教えていただけませんか?列のタイプを意味します。SQL関数に問題がある場合は、選択した列を収集する基本的なPythonコードを記述できます。
Zia

はい、重複していない別のID(ctid)がある場合、それは機能します。私はすべてが同じであり、2回複製されていると想定していました。
John Powell

申し訳ありませんが、私はこのctidアプローチを取得できませんでした。この列は複製イベントの後に手動で追加されましたか?
Zia

1
「列 'ctid'は、すべてのテーブルで使用できる特別な列ですが、特に記載がない限り表示されません。ctid列の値は、テーブルのすべての行で一意と見なされます。-
MAP 14

回答:


20

これを行う1つの方法は、ウィンドウ関数を使用し、ジオメトリごとにパーティション分割することです。これにより、繰り返される各ジオメトリにID(1、2、3など)(または1、2)が割り当てられ、その後、 id = 1のテーブル。一意の値のセット(属性とジオメトリ)を取得します。たとえば、

WITH unique_geoms (id, geom) as 
 (SELECT row_number() OVER (PARTITION BY ST_AsBinary(geom)) AS id, geom FROM some_table)
SELECT geom 
FROM unique_geoms 
WHERE id=1;

明らかに、selectに他のosm列も追加する必要があります。これは単に説明のためですが、これは基本的に、ジオメトリによるグループ化とそれぞれの最初のインスタンスの選択と同じです。注意してください。そうでない場合、比較は実際のジオメトリではなくバウンディングボックスで行われるため、Partition ByでST_AsBinaryを使用する必要があります。

他のすべての属性はおそらくジオメトリペアごとに同じであるため、osm_idを含む他のすべてのフィールドに対して次のようにし、実際に新しい一意のテーブルを作成します。

CREATE TABLE osm_unique AS
 WITH unique_geoms (id, osm_id, attr1, attr2,... attrn, geom) AS 
  (SELECT row_number() OVER (PARTITION BY ST_AsBinary(geom)) AS id, osm_id, attr1, attr2,... attrn, geom 
    FROM osm_planet_point)
 SELECT osm_id, attr1, attr2,... attrn, geom 
 FROM unique_geoms 
 WHERE id=1;

これは既存のテーブルから削除するよりも速い場合があります(特に、適切な場所に多数のインデックスがある場合)。

編集。読みやすくするために書き直しましたが、ST_AsBinary(geom)に注意を向けたのはdbastonのおかげです。


ありがとう。書き留めました。しかし、たとえば、バス停と交差点の両方であるポイントジオメトリがあるこのシナリオを考えてみましょう(OSMデータは考慮しないでください)。次に、これら2つの機能を表す2つの同一のgeomがあります。アプローチを使用すると、機能の1つが削除されます。私が言っているのは、特定の列がない場合にこの問題を解決する方法Partition Byですか?
Zia

1
こんにちはZia、それから(geom、attribute)でパーティション分割するので、同じIDを取得するには両方が同じでなければなりません。あなたの例では、geomは同じで、属性は異なります。そのため、row_number()は両方に対して1を返します。
John Powell

1
これは現在、共有バウンディングボックスを持つ個別のジオメトリを複製として識別します(バウンディングボックスの等価性に作用PARTITION BYする=演算子を使用するため)。上記をPARTITION BY ST_AsBinary(geom)修正として変更することをお勧めします。
dbaston

私はあなたがこの答えを受け入れるか、それが質問に答えないかを述べるべきだと思います。
John Powell

1
@AndreSilva。できました。私は編集があったことを明確にしないで答えを変えることにいつも神経質になっています。しかし、あなたは正しい、これははるかに読みやすいです。
ジョンパウエル

2

これは、SSURGO土壌データダウンロードから重複を削除するために使用した別の方法です。ダウンロードしたシェープファイルには一意のキーがなかったため、PostGISにインポートしたときにシリアルpk列が生成されました。データセットにいくつかのオーバーラップがあり、インポートスクリプトの開発中に誤っていくつかのレコードを2回以上インポートしました。

group byステートメントには、主キーを除く、テーブル内のすべての列が含まれます。

実行されるたびに1セットの重複行のみが削除されるため、行が4回繰り返される場合、これを最低3回実行する必要があります。これはおそらくJohnのソリューションほど高速ではありませんが、既存のテーブル内で機能します。また、一意のジオメトリごとに一意のIDがない場合にも機能します(元の質問のosm_idなど)。

私はpythonスクリプトを使用して、重複がなくなるまで繰り返し、次に完全なバキュームを実行しました。スクリプトとバキュームはそれぞれ、6つのテーブルにある約150万のレコードから数十万の複製に約30分かかったと思います。単発には十分です。小さなテーブルを素早く通過しました。

DELETE FROM schema.table 
  WHERE primary_key IN
    (SELECT MAX(primary_key)
     FROM schema.table 
     GROUP BY ST_AsBinary(geom), col_1, col_2, col_etc
     HAVING COUNT(primary_key) > 1);

編集:@dbastonの提案(下記)に基づいて複数回実行されないようにSQLを変更しました。このクエリメソッドを大きなテーブル(最大150万レコード、最大25,000の重複するポイント行)で試し、45分間実行した後、その実行をキャンセルしました。上記のSQLで実行すると(HAVING COUNTの小さなサブクエリを使用)、各実行が30秒未満に短縮されました。3回実行した後、すべて重複して行われました。以下のSQLは、小さなテーブルでは問題ないはずです。

DELETE FROM schema.table 
  WHERE primary_key NOT IN
    (SELECT MAX(primary_key)
     FROM schema.table 
     GROUP BY ST_AsBinary(geom), col_1, col_2, col_etc);

1
主キーがない場合は、常に使用可能なctid列を使用できます(docsを参照)。
dbaston 2016年

1
以下を確認することで、これを複数回実行することを回避できますprimary_key NOT IN (SELECT max(primary_key) ....
dbaston

@dbaston私は上の答えでメモしました。HAVING COUNTを削除すると、サブクエリの結果のサイズが大幅に増加するため、削除ステートメントで実行する必要がある比較の数が増加します。大きなテーブルでの実行時間がどれほど長いかに驚いた。
Nate Wanner 2016年

この場合、@ NateWanner NOT EXISTSを使用すると速度が向上する可能性があります。
Michal Zimmermann

@MichalZimmermann私があなたをフォローしているのかわかりません-どちらのバージョンもサブクエリが結果を返すことを期待しています。
Nate Wanner 2016年

1

PostGISテーブルでジオメトリの重複を簡単に削除するためのより一般的な答え。次のコマンドは、主キー(列 "gid")とジオメトリの等価性(列 "geom")に基づいて、 "table_name"に重複するジオメトリがあるすべてのフィーチャを削除します。それは本当にすべてのジオメトリの複製を削除することに注意してください、それらは永遠に消えてしまいます!多分最初にバックアップしますか?

DELETE FROM schema_name.table_name a
    USING schema_name.table_name b 
WHERE a.gid > b.gid AND st_equals(a.geom, b.geom);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.