Schemat tabeli
Aby wymusić regułę, po prostu zadeklaruj pvanlagen.buildid
UNIKALNE
:
ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);
budynek.gid
to PK, jak ujawniła twoja aktualizacja. Aby wymusić również integralność referencyjną, dodaj KLUCZ OBCY
ograniczenie
do buildings.gid
.
Do tej pory wdrożyłeś oba. Ale bardziej wydajne byłoby uruchomienie dużej AKTUALIZACJI
poniżej przed dodajesz te ograniczenia.
Jest o wiele więcej rzeczy, które należy poprawić w definicji tabeli. Po pierwsze, buildings.gid
jak również pvanlagen.buildid
należy wpisać liczba całkowita
(lub ewentualnie bigint
jeśli palisz dużo wartości PK). numeryczny
to kosztowny nonsens.
Skupmy się na głównym problemie:
Podstawowe zapytanie, aby znaleźć najbliższy budynek
Sprawa nie jest tak prosta, jak mogłoby się wydawać. To "najbliższy sąsiad" problem, z dodatkową komplikacją unikalnego przypisania.
To zapytanie znajduje najbliższy jeden budynek dla każdego PV (skrót od PV Anlage - wiersz w pvanlagen
), gdzie żadne z nich nie jest jeszcze przypisane:
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 przyspieszyć to zapytanie, potrzebujesz przestrzenny, funkcjonalny indeks GiST na budynkach
aby było dużo szybciej:
CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));
Nie wiem dlaczego ty nie
Powiązane odpowiedzi z dodatkowym wyjaśnieniem:
- Zapytanie przestrzenne na dużym stole z wieloma samozłączeniami działającymi wolno
- Jak wysłać zapytanie do wszystkich wierszy w promieniu 5 mil od moich współrzędnych?
Dalsza lektura:
- 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- ty.html
Mając indeks, nie musimy ograniczać dopasowań do tego samego gemname
dla wydajności. Zrób to tylko wtedy, gdy jest to reguła do wyegzekwowania. Jeśli ma to być obserwowane przez cały czas, uwzględnij kolumnę w ograniczeniu FK:
Pozostały problem
Możemy użyć powyższego zapytania w UPDATE
oświadczenie. Każdy PV jest używany tylko raz, ale więcej niż jeden PV może nadal znaleźć ten sam budynek być najbliżej. Zezwalasz tylko na jeden PV na budynek. Jak więc byś to rozwiązał?
Innymi słowy, jak przypisałbyś tutaj obiekty?
Proste rozwiązanie
Jednym prostym rozwiązaniem byłoby:
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;
Używam DISTINCT ON (b_gid)
zmniejszyć do dokładnie jednego wiersz na budynek, wybierając PV o najkrótszej odległości. Szczegóły:
Do każdego budynku, który jest najbliżej więcej niż jednego PV, przypisywany jest tylko najbliższy PV. Kolumna PK gid
(alias pv_gid
) służy jako rozstrzygający remis, jeśli dwie osoby są jednakowo blisko siebie. W takim przypadku niektóre PV są usuwane z aktualizacji i pozostają nieprzypisane . Powtórz zapytanie, aż wszystkie PV zostaną przypisane.
To wciąż uproszczony algorytm , chociaż. Patrząc na mój diagram powyżej, to przypisuje budynek 4 do PV 4 i budynek 5 do PV 5, podczas gdy 4-5 i 5-4 byłyby prawdopodobnie lepszym rozwiązaniem ogólnie ...
Na bok:wpisz odleg
kolumna
Obecnie używasz numeric
dla tego. Twoje oryginalne zapytanie przypisało stałą liczbę całkowitą
, nie ma sensu robić w numeric
.
W moim nowym zapytaniu ST_Distance()
zwraca rzeczywistą odległość w metrach jako double precyzja
. Jeśli po prostu przypiszemy to, otrzymamy 15 lub więcej cyfr ułamkowych w numerycznym
typ danych, a liczba nie jest ta dokładnie na początek. Poważnie wątpię, czy chcesz marnować magazyn.
Wolałbym zapisać oryginalny podwójnej precyzji
z obliczeń. lub jeszcze lepiej , w razie potrzeby zaokrąglić. Jeśli liczniki są wystarczająco dokładne, po prostu rzutuj i zapisz liczbę całkowitą
(automatyczne zaokrąglanie liczby). Lub pomnóż najpierw przez 100, aby zaoszczędzić cm:
(ST_Distance(...) * 100)::int