Schéma tabulky
Chcete-li prosadit své pravidlo, jednoduše deklarujte pvanlagen.buildid
JEDINEČNÉ
:
ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);
building.gid
je PK, jak odhalila vaše aktualizace. Chcete-li také vynutit referenční integritu, přidejte CIZÍ KLÍČ
omezení
na buildings.gid
.
Oba jste již implementovali. Ale efektivnější by bylo spustit velkou UPDATE
níže před přidáte tato omezení.
Ve vaší definici tabulky je toho mnohem více, co by se mělo zlepšit. Za prvé buildings.gid
stejně jako pvanlagen.buildid
by měl být typ integer
(nebo možná bigint
pokud hodně spalujete hodnot PK). numerický
je drahý nesmysl.
Zaměřme se na hlavní problém:
Základní dotaz k nalezení nejbližší budovy
Případ není tak jednoduchý, jak se může zdát. Je to "nejbližší soused" problém s další komplikací jedinečného přiřazení.
Tento dotaz vyhledá nejbližší budova pro každé PV (zkratka pro PV Anlage - řádek v pvanlagen
), kde ani jeden není přiřazen, zatím:
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;
Aby byl tento dotaz rychlý, potřebujete prostorový, funkční index GiST na budovách
aby toho bylo hodně rychleji:
CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));
Nevím, proč ty ne
Související odpovědi s podrobnějším vysvětlením:
- Prostorový dotaz na velké tabulce s několika samostatnými spojeními je pomalý
- Jak mohu zadat dotaz na všechny řádky v okruhu 5 mil od mých souřadnic?
Další čtení:
- 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
Se zavedeným indexem nemusíme omezovat shody na stejný název drahokamu
za výkon. Udělejte to pouze v případě, že se jedná o skutečné pravidlo, které se má vynucovat. Pokud to musí být vždy dodržováno, zahrňte sloupec do omezení FK:
Zbývající problém
Můžeme použít výše uvedený dotaz v UPDATE
tvrzení. Každý PV je použit pouze jednou, ale více než jeden PV může stále najít stejnou budovu být nejblíže. Povolíte pouze jeden PV na budovu. Jak byste to tedy vyřešili?
Jinými slovy, jak byste sem přiřadili objekty?
Jednoduché řešení
Jedno jednoduché řešení by bylo:
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;
Používám DISTINCT ON (b_gid)
snížit přesně na jedna řádek na budovu, výběr PV s nejkratší vzdáleností. Podrobnosti:
Pro každou budovu, která je nejblíže pro více FV, je přiřazena pouze nejbližší FV. Sloupec PK gid
(jinak pv_gid
) slouží jako nerozhodný výsledek, pokud jsou dva stejně blízko. V takovém případě budou některé PV z aktualizace vyřazeny a zůstanou nepřiřazeny . Opakujte dotaz, dokud nebudou přiřazeny všechny PV.
Toto je stále zjednodušující algoritmus , ačkoli. Když se podívám na můj výše uvedený diagram, tak to přiřazuje budovu 4 k PV 4 a budovu 5 k PV 5, zatímco 4-5 a 5-4 by byly pravděpodobně celkově lepší řešení ...
Na stranu:zadejte pro dist
sloupec
V současné době používáte numerické
pro to. váš původní dotaz přiřadil konstantní celé číslo
, nemá smysl v numerické
.
V mém novém dotazu ST_Distance()
vrátí skutečnou vzdálenost v metrech jako dvojnásobek přesnost
. Pokud to jednoduše přiřadíme, dostaneme přibližně 15 desetinných míst v numeric
datový typ a číslo není to pro začátek přesně. Vážně pochybuji, že chcete plýtvat úložištěm.
Raději bych ušetřil původní dvojitou přesnost
z výpočtu. nebo ještě lépe , kulaté podle potřeby. Pokud jsou metry dostatečně přesné, stačí přetypovat a uložit celé číslo
(automatické zaokrouhlení čísla). Nebo nejprve vynásobte 100, abyste ušetřili cm:
(ST_Distance(...) * 100)::int