Tabelschema
Om uw regel af te dwingen, declareert u eenvoudig pvanlagen.buildid
UNIEK
:
ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);
building.gid
is de PK, zoals je update onthulde. Om ook referentiële integriteit af te dwingen, voegt u een BUITENLANDSE SLEUTEL
beperking
naar buildings.gid
.
Beide heb je inmiddels geïmplementeerd. Maar het zou efficiënter zijn om de grote UPDATE
. uit te voeren hieronder voor u voegt deze beperkingen toe.
Er is nog veel meer dat moet worden verbeterd in uw tabeldefinitie. Ten eerste, buildings.gid
evenals pvanlagen.buildid
moet type geheel getal
. zijn (of mogelijk bigint
als je veel verbrandt van PK-waarden). numeriek
is dure onzin.
Laten we ons concentreren op het kernprobleem:
Basisquery om het dichtstbijzijnde gebouw te vinden
De zaak is niet zo eenvoudig als het lijkt. Het is een "dichtstbijzijnde buur" probleem, met de extra complicatie van een unieke toewijzing.
Deze zoekopdracht vindt de dichtstbijzijnde een gebouw voor elke PV (afkorting van PV Anlage - rij in pvanlagen
), waar geen van beide is toegewezen, maar:
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;
Om deze zoekopdracht snel uit te voeren, heeft u nodig een ruimtelijke, functionele GiST-index op gebouwen
om er veel van te maken sneller:
CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));
Niet zeker waarom jij niet
Gerelateerde antwoorden met meer uitleg:
- Ruimtelijke query op grote tafel met meerdere self-joins die traag worden uitgevoerd
- Hoe doorzoek ik alle rijen binnen een straal van 5 mijl van mijn coördinaten?
Verder lezen:
- 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- jij.html
Met de index op zijn plaats, hoeven we overeenkomsten niet te beperken tot dezelfde edelsteennaam
voor prestaties. Doe dit alleen als het een echte regel is die moet worden gehandhaafd. Als het te allen tijde moet worden nageleefd, neem dan de kolom op in de FK-beperking:
Overig probleem
We kunnen de bovenstaande query gebruiken in een UPDATE
uitspraak. Elke PV wordt slechts één keer gebruikt, maar meer dan één PV kan nog steeds het hetzelfde gebouw vinden het dichtst bij zijn. Je staat slechts één toe PV per gebouw. Dus hoe zou je dat oplossen?
Met andere woorden, hoe zou u hier objecten toewijzen?
Eenvoudige oplossing
Een eenvoudige oplossing zou zijn:
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;
Ik gebruik DISTINCT ON (b_gid)
terugbrengen tot precies één rij per gebouw en kies de PV met de kortste afstand. Details:
Voor elk gebouw dat het dichtst bij meerdere PV's ligt, wordt alleen de dichtstbijzijnde PV toegewezen. De PK-kolom gid
(alias pv_gid
) dient als tiebreak als twee even dicht bij elkaar liggen. In een dergelijk geval worden sommige PV uit de update verwijderd en blijven niet-toegewezen . Herhalen de vraag totdat alle PV zijn toegewezen.
Dit is nog steeds een simplistisch algoritme , hoewel. Kijkend naar mijn diagram hierboven, wijst dit gebouw 4 toe aan PV 4 en gebouw 5 aan PV 5, terwijl 4-5 en 5-4 over het algemeen waarschijnlijk een betere oplossing zouden zijn ...
Terzijde:typ voor dist
kolom
Momenteel gebruikt u numeriek
ervoor. je oorspronkelijke zoekopdracht heeft een constant geheel getal
toegewezen gekregen , geen punt maken in numeriek
.
In mijn nieuwe zoekopdracht ST_Distance()
geeft de werkelijke afstand in meters terug als double precisie
. Als we dat gewoon toewijzen, krijgen we ongeveer 15 breukcijfers in de numerieke
gegevenstype, en het nummer is niet dat precies om mee te beginnen. Ik betwijfel ten zeerste of je de opslagruimte wilt verspillen.
Ik bewaar liever de originele dubbele precisie
uit de berekening. of, beter nog , rond naar behoefte. Als de meters exact genoeg zijn, cast dan gewoon een integer
. en sla deze op (het getal automatisch afronden). Of vermenigvuldig eerst met 100 om cm te besparen:
(ST_Distance(...) * 100)::int