Schema tabella
Per applicare la tua regola, dichiara semplicemente pvanlagen.buildid
UNIQUE
:
ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);
building.gid
è il PK, come ha rivelato il tuo aggiornamento. Per rafforzare anche l'integrità referenziale, aggiungi un FOREIGN KEY
vincolo
a buildings.gid
.
Hai implementato entrambi ormai. Ma sarebbe più efficiente eseguire il grande UPDATE
sotto prima aggiungi questi vincoli.
C'è molto di più che dovrebbe essere migliorato nella definizione della tua tabella. Per uno, buildings.gid
così come pvanlagen.buildid
dovrebbe essere di tipo integer
(o possibilmente bigint
se bruci molto di valori farmacocinetici). numeric
è una sciocchezza costosa.
Concentriamoci sul problema centrale:
Richiesta di base per trovare l'edificio più vicino
Il caso non è così semplice come potrebbe sembrare. È un "vicino più vicino" problema, con l'ulteriore complicazione dell'assegnazione univoca.
Questa query trova il uno più vicino edificio per ogni PV (abbreviazione di PV Anlage - riga in pvanlagen
), dove nessuno dei due è ancora assegnato:
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;
Per eseguire rapidamente questa query, è necessario un indice GiST spaziale e funzionale su buildings
per renderlo molto più veloce:
CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));
Non sono sicuro del perché tu non
Risposte correlate con più spiegazioni:
- Query spaziale su una tabella di grandi dimensioni con più auto join che funzionano lentamente
- Come faccio a interrogare tutte le righe entro un raggio di 5 miglia dalle mie coordinate?
Ulteriori letture:
- 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- tu.html
Con l'indice attivo, non è necessario limitare le corrispondenze allo stesso gemname
per le prestazioni. Fallo solo se è una regola reale da applicare. Se deve essere sempre osservato, includi la colonna nel vincolo FK:
Problema residuo
Possiamo usare la query sopra in un UPDATE
dichiarazione. Ogni PV viene utilizzato solo una volta, ma più di un PV potrebbe comunque trovare lo stesso edificio essere più vicino. Ne consenti solo uno FV per edificio. Allora come lo risolveresti?
In altre parole, come assegneresti gli oggetti qui?
Soluzione semplice
Una soluzione semplice sarebbe:
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;
Uso DISTINCT ON (b_gid)
ridurre esattamente a uno riga per edificio, selezionando il PV con la distanza più breve. Dettagli:
Per ogni edificio più vicino per più di un PV, viene assegnato solo il PV più vicino. La colonna PK gid
(alias pv_gid
) funge da spareggio se due sono ugualmente vicini. In tal caso, alcuni PV vengono eliminati dall'aggiornamento e rimangono non assegnati . Ripeti la query finché non vengono assegnati tutti i PV.
Questo è ancora un algoritmo semplicistico , anche se. Guardando il mio diagramma sopra, questo assegna l'edificio 4 a PV 4 e l'edificio 5 a PV 5, mentre 4-5 e 5-4 sarebbero probabilmente una soluzione migliore nel complesso ...
A parte:digita dist
colonna
Attualmente utilizzi numeric
per questo. la tua query originale ha assegnato un integer
costante , non ha senso in numeric
.
Nella mia nuova query ST_Distance()
restituisce la distanza effettiva in metri come double precision
. Se assegniamo semplicemente che otteniamo circa 15 cifre frazionarie nel numeric
tipo di dati e il numero non è quello esatto per cominciare. Dubito seriamente che tu voglia sprecare lo spazio di archiviazione.
Preferirei salvare la double precision
originale dal calcolo. o, meglio ancora , rotondo secondo necessità. Se i metri sono sufficientemente precisi, esegui il cast e salva un integer
(arrotondando automaticamente il numero). Oppure moltiplica per 100 prima per risparmiare cm:
(ST_Distance(...) * 100)::int