Hapus duplikat geometri di tabel postgis


11

Setelah - Saya tidak tahu apa yang terjadi - semua entri saya di tabel PostGIS saya digandakan! Saya mencoba ini untuk menghapusnya tetapi tidak menghapus semua / semua duplikat:

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

atau ini:

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

EDIT:

Saya akhirnya menemukan cara mudah, yang berfungsi dalam kasus saya:

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

ditemukan di halaman ini: http://technobytz.com/most-useful-postgresql-commands.html


1
Bisakah Anda memberikan planet_osm_pointstruktur tabel saat ini? berarti jenis kolom. Anda dapat menulis kode Python dasar untuk mengumpulkan kolom yang dipilih, jika mengalami kesulitan dengan fungsi SQL.
Zia

Ya, itu akan berhasil, jika Anda memiliki id lain (ctid) yang tidak digandakan. Saya berasumsi bahwa semuanya identik dan digandakan dua kali.
John Powell

Maaf, tapi saya tidak mendapatkan ctidpendekatan ini . Kolom ini telah ditambahkan secara manual setelah acara duplikasi?
Zia

1
"Kolom 'ctid' adalah kolom khusus yang tersedia untuk setiap tabel tetapi tidak terlihat kecuali disebutkan secara khusus. Nilai kolom ctid dianggap unik untuk setiap baris dalam tabel. -" technobytz.com/most-useful-postgresql-commands.html
PETA

Jawaban:


20

Salah satu cara untuk melakukan ini, adalah dengan menggunakan fungsi jendela dan partisi berdasarkan geometri, sehingga setiap geometri yang diulang mendapatkan id: 1, 2, 3, dll (atau 1, 2) dalam kasus Anda, dan kemudian Anda tinggal memilih dari tabel tempat id = 1, untuk mendapatkan set nilai yang unik (atribut dan geometri) kembali, misalnya,

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;

Jelas, Anda perlu menambahkan kolom osm lainnya di pilih juga, ini hanya untuk ilustrasi, tetapi ini pada dasarnya seperti pengelompokan berdasarkan geometri dan hanya memilih contoh pertama masing-masing. Catatan, Anda perlu menggunakan ST_AsBinary di Partisi Oleh karena jika tidak, perbandingan dilakukan pada kotak pembatas, bukan geometri yang sebenarnya.

Karena semua atribut lainnya mungkin sama untuk setiap pasangan geometri, Anda akan melakukannya seperti ini untuk semua bidang lainnya, termasuk osm_id, dan untuk benar-benar membuat tabel baru dan unik:

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;

Ini mungkin lebih cepat daripada menghapus dari tabel yang ada, terutama jika ada banyak indeks di tempat.

EDIT . Ditulis ulang untuk keterbacaan, tetapi, meninggalkan kredit untuk dbaston karena menarik perhatian saya ke ST_AsBinary (geom)


Terima kasih. Saya telah membuat catatan. Tapi sebagai contoh mari kita pertimbangkan skenario ini di mana ada geom titik yang merupakan halte bus dan persimpangan (jangan mempertimbangkan data OSM). Kemudian kita akan memiliki dua geom identik yang mewakili dua fitur ini. Menggunakan pendekatan Anda akan menghapus salah satu fitur. Apa yang saya katakan adalah bagaimana menyelesaikan masalah ini ketika tidak ada kolom khusus untuk itu Partition By?
Zia

1
Hai Zia, lalu Anda partisi dengan (geom, atribut), sehingga keduanya harus sama untuk mendapatkan id yang sama. Dalam contoh Anda, geom akan sama, atributnya tidak, jadi row_number () akan mengembalikan 1 untuk keduanya.
John Powell

1
Ini saat ini mengidentifikasi geometri yang berbeda dengan kotak pembatas bersama sebagai duplikat (karena PARTITION BYmenggunakan =operator, yang bekerja pada kesetaraan kotak pembatas). Saya sarankan mengubah di atas PARTITION BY ST_AsBinary(geom)sebagai perbaikan.
dbaston

Saya pikir Anda harus menerima jawaban ini, atau menyatakan bagaimana itu tidak menjawab pertanyaan.
John Powell

1
@AndreSilva. Selesai Saya selalu gugup mengubah jawaban tanpa membuatnya jelas telah ada suntingan. Tapi, Anda benar, ini jauh lebih mudah dibaca.
John Powell

2

Berikut adalah metode lain yang saya gunakan untuk menghapus duplikat dari unduhan data tanah SSURGO. Shapefile yang diunduh tidak memiliki kunci unik, sehingga kolom pk serial dihasilkan ketika saya mengimpor ke PostGIS. Ada beberapa tumpang tindih dalam set data, dan saya secara tidak sengaja mengimpor beberapa catatan lebih dari sekali saat mengembangkan skrip impor.

Grup dengan pernyataan termasuk semua kolom dalam tabel, tidak termasuk kunci utama.

Ini hanya akan menghapus satu set duplikat baris setiap kali dijalankan, jadi jika satu baris diulang 4 kali Anda harus menjalankan ini minimal 3 kali. Ini kemungkinan tidak secepat solusi John, tetapi bekerja dalam tabel yang ada. Ini juga berfungsi ketika Anda tidak memiliki id unik untuk setiap geometri unik (seperti osm_id dalam pertanyaan asli).

Saya menggunakan skrip python untuk mengulang sampai duplikat hilang, dan kemudian menjalankan kekosongan penuh. Saya pikir skrip dan vakum masing-masing memakan waktu sekitar 30 menit untuk beberapa ratus ribu duplikat dari sekitar 1,5 juta catatan dalam 6 tabel. Sangat bagus untuk sekali saja. Ia melewati meja-meja kecil dengan sangat cepat.

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);

EDIT: SQL dimodifikasi untuk menghindari menjalankan beberapa kali berdasarkan saran @daston (di bawah). Saya mencoba metode kueri ini pada tabel besar (~ 1,5 juta catatan, ~ 25.000 baris titik duplikat), dan setelah berjalan selama 45 menit, saya membatalkan eksekusi. Berjalan dengan SQL di atas (menggunakan subquery yang lebih kecil dari HAVING COUNT) mengurangi setiap proses hingga kurang dari 30 detik. Setelah berjalan 3 kali, itu dilakukan dengan semua duplikat. SQL di bawah ini harus OK untuk tabel kecil.

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
Jika Anda tidak memiliki kunci utama, Anda dapat menggunakan kolom yang selalu tersedia ctid(lihat dokumen ).
dbaston

1
Anda dapat menghindari menjalankan ini berulang kali dengan memeriksaprimary_key NOT IN (SELECT max(primary_key) ....
dbaston

@daston Saya membuat catatan dalam jawaban di atas. Menghapus COUNT HAVING sangat meningkatkan ukuran hasil subquery, dan karena itu jumlah perbandingan yang perlu dibuat pernyataan penghapusan. Saya terkejut melihat eksekusi yang lebih lama di atas meja besar.
Nate Wanner

@NateWanner NOT EXISTS mungkin memberi Anda beberapa kecepatan tambahan dalam hal ini.
Michal Zimmermann

@MichalZimmermann Saya tidak yakin saya mengikuti Anda - kedua versi mengharapkan subquery untuk mengembalikan hasil.
Nate Wanner

1

Jawaban yang lebih umum untuk dengan mudah menghapus duplikat geometri di tabel PostGIS. Perintah berikut menghapus semua fitur dengan duplikat geometri di "table_name" berdasarkan kunci primer (kolom "gid") dan persamaan geometri (kolom "geom"). Sadarilah itu benar-benar menghapus semua duplikat geometri, mereka akan hilang, selamanya! Mungkin kembali dulu?

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);
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.