Tabellenschema
Um Ihre Regel durchzusetzen, deklarieren Sie einfach pvanlagen.buildid
EINZIGARTIG
:
ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);
building.gid
ist die PK, wie dein Update ergeben hat. Um auch die referenzielle Integrität zu erzwingen, fügen Sie einen Fremdschlüssel
Einschränkung
zu buildings.gid
.
Beides haben Sie inzwischen umgesetzt. Aber es wäre effizienter, das große UPDATE
auszuführen unten vorher Sie fügen diese Einschränkungen hinzu.
Es gibt noch viel mehr, was in Ihrer Tabellendefinition verbessert werden sollte. Zum einen buildings.gid
sowie pvanlagen.buildid
sollte vom Typ integer
sein (oder möglicherweise bigint
wenn Sie viel brennen von PK-Werten). numerisch
ist teurer Quatsch.
Konzentrieren wir uns auf das Kernproblem:
Einfache Abfrage, um das nächstgelegene Gebäude zu finden
Der Fall ist nicht so einfach, wie es scheinen mag. Es ist ein "nächster Nachbar" Problem, mit der zusätzlichen Komplikation der eindeutigen Zuordnung.
Diese Abfrage findet die nächste Eins Gebäude für jede PV (kurz für PV Anlage - Zeile in pvanlagen
), wo weder zugewiesen noch:
SELECT pv_gid, b_gid, dist
FROM (
SELECT gid AS pv_gid, ST_Transform(geom, 31467) AS geom31467
FROM pvanlagen
WHERE buildid IS NULL -- not assigned yet
) p
, LATERAL (
SELECT b.gid AS b_gid
, round(ST_Distance(p.geom31467
, ST_Transform(b.centroid, 31467))::numeric, 2) AS dist -- see below
FROM buildings b
LEFT JOIN pvanlagen p1 ON p1.buildid = b.gid -- also not assigned ...
WHERE p1.buildid IS NULL -- ... yet
-- AND p.gemname = b.gemname -- not needed for performance, see below
ORDER BY p.geom31467 <-> ST_Transform(b.centroid, 31467)
LIMIT 1
) b;
Um diese Abfrage schnell zu machen, brauchen Sie ein räumlicher, funktionaler GiST-Index zu Gebäuden
um es viel zu machen schneller:
CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));
Nicht sicher warum du nicht
Verwandte Antworten mit mehr Erklärung:
- Räumliche Abfrage für große Tabelle mit mehreren Self-Joins, die langsam ausgeführt werden
- Wie frage ich alle Zeilen innerhalb eines 5-Meilen-Radius um meine Koordinaten ab?
Weiterführende Literatur:
- http://workshops.boundlessgeo.com/postgis-intro/knn. html
- http://www.postgresonline.com/journal/archives/306-KNN-GIST-with-a-Lateral-twist-Coming-soon-to-a-database-near- du.html
Wenn der Index vorhanden ist, müssen wir Übereinstimmungen nicht auf denselben Edelsteinnamen
beschränken für Leistung. Tun Sie dies nur, wenn es sich tatsächlich um eine zu erzwingende Regel handelt. Wenn es immer eingehalten werden muss, nehmen Sie die Spalte in die FK-Einschränkung auf:
Verbleibendes Problem
Wir können die obige Abfrage in einem UPDATE
verwenden Aussage. Jedes PV wird nur einmal verwendet, aber mehr als ein PV kann immer noch dasselbe Gebäude finden am nächsten sein. Sie erlauben nur eine PV pro Gebäude. Wie würden Sie das lösen?
Mit anderen Worten, wie würden Sie hier Objekte zuweisen?
Einfache Lösung
Eine einfache Lösung wäre:
UPDATE pvanlagen p1
SET buildid = sub.b_gid
, dist = sub.dist -- actual distance
FROM (
SELECT DISTINCT ON (b_gid)
pv_gid, b_gid, dist
FROM (
SELECT gid AS pv_gid, ST_Transform(geom, 31467) AS geom31467
FROM pvanlagen
WHERE buildid IS NULL -- not assigned yet
) p
, LATERAL (
SELECT b.gid AS b_gid
, round(ST_Distance(p.geom31467
, ST_Transform(b.centroid, 31467))::numeric, 2) AS dist -- see below
FROM buildings b
LEFT JOIN pvanlagen p1 ON p1.buildid = b.gid -- also not assigned ...
WHERE p1.buildid IS NULL -- ... yet
-- AND p.gemname = b.gemname -- not needed for performance, see below
ORDER BY p.geom31467 <-> ST_Transform(b.centroid, 31467)
LIMIT 1
) b
ORDER BY b_gid, dist, pv_gid -- tie breaker
) sub
WHERE p1.gid = sub.pv_gid;
Ich verwende DISTINCT ON (b_gid)
auf genau eins zu reduzieren Reihe pro Gebäude, wobei das PV mit dem kürzesten Abstand ausgewählt wird. Einzelheiten:
Für jedes Gebäude, das für mehrere PV am nächsten ist, wird nur das nächste PV zugewiesen. Die PK-Spalte gid
(alias pv_gid
) dient als Tiebreaker, wenn zwei gleich nah dran sind. In einem solchen Fall werden einige PV aus dem Update gelöscht und bleiben nicht zugewiesen . Wiederholen die Abfrage, bis alle PV zugewiesen sind.
Dies ist immer noch ein einfacher Algorithmus , obwohl. Betrachtet man mein Diagramm oben, weist dies Gebäude 4 PV 4 und Gebäude 5 PV 5 zu, während 4-5 und 5-4 insgesamt wahrscheinlich eine bessere Lösung wären ...
Beiseite:Geben Sie für dist
ein Spalte
Derzeit verwenden Sie numeric
dafür. Ihrer ursprünglichen Abfrage wurde eine Konstante integer
zugewiesen , macht keinen Sinn in numerisch
.
In meiner neuen Abfrage ST_Distance()
gibt die tatsächliche Entfernung in Metern als double zurück Präzision
. Wenn wir das einfach zuweisen, erhalten wir ungefähr 15 Nachkommastellen im numeric
Datentyp, und die Nummer ist nicht das anfangs genau. Ich bezweifle ernsthaft, dass Sie den Speicherplatz verschwenden wollen.
Ich würde lieber die ursprüngliche double precision
speichern aus der Berechnung. oder noch besser , nach Bedarf runden. Wenn Meter genau genug sind, wandeln Sie einfach um und speichern Sie eine Ganzzahl
(Rundung der Zahl automatisch). Oder erst mit 100 multiplizieren, um cm zu sparen:
(ST_Distance(...) * 100)::int