Tabellschema
För att upprätthålla din regel, deklarera helt enkelt pvanlagen.buildid
UNIQUE
:
ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);
building.gid
är PK, som din uppdatering visade. För att även upprätthålla referensintegritet lägg till en UTLANDSNYCKEL
begränsning
till buildings.gid
.
Du har implementerat båda vid det här laget. Men det skulle vara mer effektivt att köra den stora UPDATE
nedan före du lägger till dessa begränsningar.
Det finns mycket mer som borde förbättras i din tabelldefinition. För det första, buildings.gid
samt pvanlagen.buildid
ska vara typ heltal
(eller möjligen bigint
om du bränner mycket av PK-värden). numerisk
är dyrt nonsens.
Låt oss fokusera på kärnproblemet:
Grundläggande fråga för att hitta närmaste byggnad
Fallet är inte så enkelt som det kan verka. Det är en "närmaste granne" problem, med den ytterligare komplikationen av unik tilldelning.
Den här frågan hittar den närmaste en byggnad för varje PV (förkortning för PV Anlage - rad i pvanlagen
), där ingendera är tilldelad, ännu:
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;
För att göra den här frågan snabb behöver du ett rumsligt, funktionellt GiST-index på byggnader
för att göra det mycket snabbare:
CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));
Inte säker på varför det gör du inte
Relaterade svar med mer förklaring:
- Rumslig fråga på ett stort bord med flera självkopplingar som fungerar långsamt
- Hur frågar jag alla rader inom en radie på 8 mil från mina koordinater?
Mer läsning:
- 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- you.html
Med indexet på plats behöver vi inte begränsa matchningar till samma gemnamn
för prestation. Gör bara detta om det är en faktisk regel som ska tillämpas. Om det måste observeras hela tiden, inkludera kolumnen i FK-begränsningen:
Återstående problem
Vi kan använda ovanstående fråga i en UPPDATERING
påstående. Varje PV används bara en gång, men mer än en PV kan fortfarande hitta samma byggnad att vara närmast. Du tillåter bara en PV per byggnad. Så hur skulle du lösa det?
Med andra ord, hur skulle du tilldela objekt här?
Enkel lösning
En enkel lösning skulle vara:
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;
Jag använder DISTINCT ON (b_gid)
för att reducera till exakt ett rad per byggnad, välj den PV med det kortaste avståndet. Detaljer:
För varje byggnad som är närmast för mer en PV, tilldelas endast den närmaste PV. PK-kolumnen gid
(alias pv_gid
) fungerar som tiebreaker om två är lika nära. I ett sådant fall tas vissa PV bort från uppdateringen och förblir ej tilldelade . Upprepa frågan tills alla PV är tilldelade.
Detta är fortfarande en förenklad algoritm , fastän. Om man tittar på mitt diagram ovan, tilldelar detta byggnad 4 till PV 4 och byggnad 5 till PV 5, medan 4-5 och 5-4 förmodligen skulle vara en bättre lösning totalt sett...
Aside:skriv för dist
kolumn
För närvarande använder du numerisk
för det. din ursprungliga fråga tilldelade ett konstant heltal
, ingen mening med numerisk
.
I min nya fråga ST_Distance()
returnerar det faktiska avståndet i meter som dubbel precision
. Om vi helt enkelt tilldelar att vi får 15 eller så bråksiffror i den numeriska
datatyp, och numret är inte det exakt till att börja med. Jag tvivlar allvarligt på att du vill slösa bort lagringen.
Jag skulle hellre spara den ursprungliga dubbla precisionen
från beräkningen. eller, bättre än , runda efter behov. Om mätare är tillräckligt exakta, casta bara till och spara ett heltal
(avrundar numret automatiskt). Eller multiplicera med 100 först för att spara cm:
(ST_Distance(...) * 100)::int