PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Assegnazione univoca dei punti più vicini tra due tabelle

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:

Ulteriori letture:

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