Esquema de tabela
Para impor sua regra, simplesmente declare
pvanlagen.buildid
UNIQUE
:ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);
building.gid
é o PK, como sua atualização revelou. Para também impor a integridade referencial, adicione um CHAVE ESTRANGEIRA
restrição
para buildings.gid
. Você implementou ambos até agora. Mas seria mais eficiente executar o grande
UPDATE
abaixo antes você adiciona essas restrições. Há muito mais que deve ser melhorado na definição da sua tabela. Por um lado,
buildings.gid
bem como pvanlagen.buildid
deve ser do tipo inteiro
(ou possivelmente bigint
se você queimar muito de valores de PK). numérico
é um absurdo caro. Vamos nos concentrar no problema central:
Consulta básica para encontrar o prédio mais próximo
O caso não é tão simples quanto parece. É um "vizinho mais próximo" problema, com a complicação adicional de atribuição única.
Esta consulta encontra o um mais próximo edifício para cada PV (abreviação de PV Anlage - linha em
pvanlagen
), onde nenhum é atribuído, ainda: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;
Para tornar essa consulta rápida, você precisa um índice GiST espacial e funcional em
edifícios
para torná-lo muito mais rápido:CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));
Não tenho certeza por que você não
Respostas relacionadas com mais explicações:
- Consulta espacial em uma tabela grande com várias autojunções com desempenho lento
- Como faço para consultar todas as linhas em um raio de 5 milhas de minhas coordenadas?
Leitura adicional:
- 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- você.html
Com o índice em vigor, não precisamos restringir as correspondências ao mesmo
gemname
para desempenho. Faça isso apenas se for uma regra real a ser aplicada. Se tiver que ser observado o tempo todo, inclua a coluna na restrição FK:Problema restante
Podemos usar a consulta acima em um
UPDATE
declaração. Cada PV é usado apenas uma vez, mas mais de um PV ainda pode encontrar o mesmo prédio estar mais próximo. Você só permite um PV por edifício. Então, como você resolveria isso? Em outras palavras, como você atribuiria objetos aqui?
Solução simples
Uma solução simples seria:
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;
Eu uso
DISTINCT ON (b_gid)
para reduzir exatamente um linha por edifício, escolhendo o PV com a menor distância. Detalhes:Para qualquer edifício que esteja mais próximo de mais um PV, apenas o PV mais próximo é atribuído. A coluna PK
gid
(alias pv_gid
) serve como desempate se dois estiverem igualmente próximos. Nesse caso, alguns PV são descartados da atualização e permanecem não atribuídos . Repetir a consulta até que todos os PV sejam atribuídos. Este ainda é um algoritmo simplista , no entanto. Olhando para o meu diagrama acima, isso atribui o edifício 4 ao PV 4 e o edifício 5 ao PV 5, enquanto 4-5 e 5-4 provavelmente seriam uma solução melhor em geral ...
Aparte:digite para dist
coluna
Atualmente você usa
numérico
por isso. sua consulta original atribuiu uma constante integer
, não adianta fazer em numérico
. Na minha nova consulta
ST_Distance()
retorna a distância real em metros como double precisão
. Se simplesmente atribuirmos isso, obteremos 15 ou mais dígitos fracionários no numérico
tipo de dados e o número não é esse exato para começar. Eu duvido seriamente que você queira desperdiçar o armazenamento. Prefiro salvar a
precisão dupla
original a partir do cálculo. ou melhor ainda , redondo conforme necessário. Se os medidores forem exatos o suficiente, apenas converta e salve um integer
(arredondando o número automaticamente). Ou multiplique por 100 primeiro para economizar cm:(ST_Distance(...) * 100)::int