Löschen Sie doppelte Geometrie in Postgis-Tabellen

11

Nach - ich weiß nicht, was passiert ist - werden alle meine Einträge in meinen PostGIS-Tabellen verdoppelt! Ich habe versucht, sie zu löschen, aber es werden keine / alle Duplikate gelöscht:

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

oder dieses:

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

BEARBEITEN:

Ich habe endlich einen einfachen Weg gefunden, der in meinem Fall funktioniert:

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

auf dieser Seite gefunden: http://technobytz.com/most-useful-postgresql-commands.html

KARTE
quelle
1
Könnten Sie bitte die aktuelle planet_osm_pointTabellenstruktur angeben? bedeutet Art der Spalten. Sie können einen grundlegenden Python-Code schreiben, um die ausgewählten Spalten zu erfassen, wenn Sie Probleme mit den SQL-Funktionen haben.
Zia
Ja, das funktioniert, wenn Sie eine andere ID (ctid) haben, die nicht dupliziert wird. Ich ging davon aus, dass alles identisch war und zweimal dupliziert wurde.
John Powell
Entschuldigung, aber ich habe diesen ctidAnsatz nicht verstanden. Diese Spalte wurde nach dem Duplizierungsereignis manuell hinzugefügt.
Zia
1
"Die Spalte 'ctid' ist eine spezielle Spalte, die für alle Tabellen verfügbar ist, jedoch nur angezeigt wird, wenn dies ausdrücklich erwähnt wird. Der Wert der ctid-Spalte wird für alle Zeilen in einer Tabelle als eindeutig angesehen. -" technobytz.com/most-useful-postgresql-commands.html
Karte

Antworten:

20

Eine Möglichkeit, dies zu tun, besteht darin, eine Fensterfunktion und eine Partition nach Geometrie zu verwenden, sodass jede wiederholte Geometrie eine ID erhält: 1, 2, 3 usw. (oder 1, 2) in Ihrem Fall, und dann wählen Sie einfach aus Tabelle mit der ID = 1, um einen eindeutigen Satz von Werten (Attribute und Geometrie) zurückzugewinnen, z.

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;

Natürlich müssten Sie auch die anderen osm-Spalten in die Auswahl einfügen. Dies dient nur zur Veranschaulichung, entspricht jedoch im Wesentlichen der Gruppierung nach Geometrie und der Auswahl der ersten Instanz jeder einzelnen Spalte. Beachten Sie, dass Sie ST_AsBinary in der Partition By verwenden müssen, da sonst der Vergleich für den Begrenzungsrahmen und nicht für die tatsächliche Geometrie erfolgt.

Da alle anderen Attribute vermutlich für jedes Geometriepaar gleich sind, würden Sie so etwas für alle anderen Felder, einschließlich osm_id, tun und tatsächlich eine neue, eindeutige Tabelle erstellen:

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;

Dies ist möglicherweise schneller als das Löschen aus einer vorhandenen Tabelle, insbesondere wenn viele Indizes vorhanden sind.

BEARBEITEN . Zur besseren Lesbarkeit umgeschrieben, aber dbaston die Ehre überlassen , meine Aufmerksamkeit auf ST_AsBinary (geom) zu lenken

John Powell
quelle
Vielen Dank. Ich habe eine Notiz gemacht. Betrachten wir zum Beispiel dieses Szenario, in dem es einen Punkt gibt, der sowohl eine Bushaltestelle als auch eine Kreuzung ist (berücksichtigen Sie nicht die OSM-Daten). Dann haben wir zwei identische Geome, die diese beiden Merkmale darstellen. Wenn Sie Ihren Ansatz verwenden, wird eine der Funktionen entfernt. Was ich sage ist, wie man dieses Problem löst, wenn es keine spezifische Spalte gibt Partition By?
Zia
1
Hallo Zia, dann partitionierst du nach (geom, attribute), so dass beide gleich sein müssen, um die gleiche ID zu erhalten. In Ihrem Beispiel wäre das Geom dasselbe, das Attribut nicht, also würde row_number () für beide 1 zurückgeben.
John Powell
1
Dadurch werden derzeit unterschiedliche Geometrien mit einem gemeinsam genutzten Begrenzungsrahmen als Duplikate identifiziert (da PARTITION BYder =Operator verwendet wird, der die Gleichheit von Begrenzungsrahmen bearbeitet). Ich würde vorschlagen, das oben genannte PARTITION BY ST_AsBinary(geom)als Korrektur zu ändern .
Dbaston
Ich denke, Sie sollten diese Antwort akzeptieren oder angeben, dass sie die Frage nicht beantwortet.
John Powell
1
@AndreSilva. Erledigt. Ich bin immer nervös, wenn es darum geht, Antworten zu ändern, ohne klar zu machen, dass eine Bearbeitung stattgefunden hat. Aber Sie haben Recht, das ist viel besser lesbar.
John Powell
2

Hier ist eine andere Methode, mit der ich Duplikate aus einem SSURGO-Bodendaten-Download entfernt habe. Die heruntergeladenen Shapefiles hatten keinen eindeutigen Schlüssel, daher wurde beim Importieren in PostGIS eine serielle pk-Spalte generiert. Es gab einige Überlappungen in den Datensätzen, und ich habe versehentlich einige Datensätze mehr als einmal importiert, während ich das Importskript entwickelt habe.

Die Anweisung group by enthält alle Spalten in der Tabelle mit Ausnahme des Primärschlüssels.

Bei jeder Ausführung wird nur ein Satz doppelter Zeilen gelöscht. Wenn eine Zeile also viermal wiederholt wird, müssen Sie diese mindestens dreimal ausführen. Dies ist wahrscheinlich nicht so schnell wie Johns Lösung, funktioniert jedoch innerhalb einer vorhandenen Tabelle. Dies funktioniert auch, wenn Sie nicht für jede eindeutige Geometrie eine eindeutige ID haben (z. B. die osm_id in der ursprünglichen Frage).

Ich benutzte ein Python-Skript, um zu wiederholen, bis Duplikate verschwunden waren, und ließ dann ein volles Vakuum laufen. Ich denke, das Skript und das Vakuum dauerten jeweils etwa 30 Minuten für einige hunderttausend Duplikate von etwa 1,5 Millionen Datensätzen in 6 Tabellen. Viel Gutes für ein Einzelstück. Es ging sehr schnell durch die kleinen Tische.

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

BEARBEITEN: SQL wurde geändert, um zu vermeiden, dass es basierend auf dem @ dbaston-Vorschlag (siehe unten) mehrmals ausgeführt wird. Ich habe diese Abfragemethode in einer großen Tabelle (~ 1,5 Millionen Datensätze, ~ 25.000 doppelte Punktzeilen) ausprobiert und nach 45 Minuten die Ausführung abgebrochen. Das Ausführen mit dem obigen SQL (unter Verwendung einer kleineren Unterabfrage aus dem HAVING COUNT) reduzierte jeden Lauf auf weniger als 30 Sekunden. Nach dreimaligem Ausführen wurde dies mit allen Duplikaten durchgeführt. Die folgende SQL sollte für kleine Tabellen in Ordnung sein.

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);
Nate Wanner
quelle
1
Wenn Sie keinen Primärschlüssel haben, können Sie die immer verfügbare ctidSpalte verwenden (siehe Dokumente ).
Dbaston
1
Sie können dies vermeiden, indem Sie nachprimary_key NOT IN (SELECT max(primary_key) ....
dbaston
@dbaston Ich habe mir in der obigen Antwort Notizen gemacht. Durch das Entfernen von HAVING COUNT wird die Größe der Unterabfrageergebnisse und damit die Anzahl der Vergleiche, die die delete-Anweisung durchführen muss, erheblich erhöht. Ich war überrascht, wie lange die Ausführung auf einem großen Tisch dauerte.
Nate Wanner
@NateWanner NOT EXISTS bietet Ihnen in diesem Fall möglicherweise zusätzliche Geschwindigkeit.
Michal Zimmermann
@MichalZimmermann Ich bin mir nicht sicher, ob ich dir folge - beide Versionen erwarten, dass die Unterabfrage ein Ergebnis zurückgibt.
Nate Wanner
1

Eine allgemeinere Antwort zum einfachen Löschen von Geometrieduplikaten in der PostGIS-Tabelle. Der folgende Befehl löscht alle Features mit doppelter Geometrie in "tabellenname" basierend auf dem Primärschlüssel (Spalte "gid") und der Gleichheit der Geometrie (Spalte "geom"). Beachten Sie, dass wirklich alle Geometrieduplikate gelöscht werden. Sie werden für immer verschwunden sein! Vielleicht zuerst sichern?

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