sql >> Database >  >> RDS >> PostgreSQL

Unieke toewijzing van dichtstbijzijnde punten tussen twee tabellen

Tabelschema

Om uw regel af te dwingen, declareert u eenvoudig pvanlagen.buildid UNIEK :

ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);

building.gid is de PK, zoals je update onthulde. Om ook referentiële integriteit af te dwingen, voegt u een BUITENLANDSE SLEUTEL beperking naar buildings.gid .

Beide heb je inmiddels geïmplementeerd. Maar het zou efficiënter zijn om de grote UPDATE . uit te voeren hieronder voor u voegt deze beperkingen toe.

Er is nog veel meer dat moet worden verbeterd in uw tabeldefinitie. Ten eerste, buildings.gid evenals pvanlagen.buildid moet type geheel getal . zijn (of mogelijk bigint als je veel verbrandt van PK-waarden). numeriek is dure onzin.

Laten we ons concentreren op het kernprobleem:

Basisquery om het dichtstbijzijnde gebouw te vinden

De zaak is niet zo eenvoudig als het lijkt. Het is een "dichtstbijzijnde buur" probleem, met de extra complicatie van een unieke toewijzing.

Deze zoekopdracht vindt de dichtstbijzijnde een gebouw voor elke PV (afkorting van PV Anlage - rij in pvanlagen ), waar geen van beide is toegewezen, maar:

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;

Om deze zoekopdracht snel uit te voeren, heeft u nodig een ruimtelijke, functionele GiST-index op gebouwen om er veel van te maken sneller:

CREATE INDEX build_centroid_gix ON buildings USING gist (ST_Transform(centroid, 31467));

Niet zeker waarom jij niet

Gerelateerde antwoorden met meer uitleg:

Verder lezen:

Met de index op zijn plaats, hoeven we overeenkomsten niet te beperken tot dezelfde edelsteennaam voor prestaties. Doe dit alleen als het een echte regel is die moet worden gehandhaafd. Als het te allen tijde moet worden nageleefd, neem dan de kolom op in de FK-beperking:

Overig probleem

We kunnen de bovenstaande query gebruiken in een UPDATE uitspraak. Elke PV wordt slechts één keer gebruikt, maar meer dan één PV kan nog steeds het hetzelfde gebouw vinden het dichtst bij zijn. Je staat slechts één toe PV per gebouw. Dus hoe zou je dat oplossen?

Met andere woorden, hoe zou u hier objecten toewijzen?

Eenvoudige oplossing

Een eenvoudige oplossing zou zijn:

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;

Ik gebruik DISTINCT ON (b_gid) terugbrengen tot precies één rij per gebouw en kies de PV met de kortste afstand. Details:

Voor elk gebouw dat het dichtst bij meerdere PV's ligt, wordt alleen de dichtstbijzijnde PV toegewezen. De PK-kolom gid (alias pv_gid ) dient als tiebreak als twee even dicht bij elkaar liggen. In een dergelijk geval worden sommige PV uit de update verwijderd en blijven niet-toegewezen . Herhalen de vraag totdat alle PV zijn toegewezen.

Dit is nog steeds een simplistisch algoritme , hoewel. Kijkend naar mijn diagram hierboven, wijst dit gebouw 4 toe aan PV 4 en gebouw 5 aan PV 5, terwijl 4-5 en 5-4 over het algemeen waarschijnlijk een betere oplossing zouden zijn ...

Terzijde:typ voor dist kolom

Momenteel gebruikt u numeriek ervoor. je oorspronkelijke zoekopdracht heeft een constant geheel getal toegewezen gekregen , geen punt maken in numeriek .

In mijn nieuwe zoekopdracht ST_Distance() geeft de werkelijke afstand in meters terug als double precisie . Als we dat gewoon toewijzen, krijgen we ongeveer 15 breukcijfers in de numerieke gegevenstype, en het nummer is niet dat precies om mee te beginnen. Ik betwijfel ten zeerste of je de opslagruimte wilt verspillen.

Ik bewaar liever de originele dubbele precisie uit de berekening. of, beter nog , rond naar behoefte. Als de meters exact genoeg zijn, cast dan gewoon een integer . en sla deze op (het getal automatisch afronden). Of vermenigvuldig eerst met 100 om cm te besparen:

(ST_Distance(...) * 100)::int



  1. Mysql, PHP, zoeken naar meerdere woorden

  2. Hoe de helling in SQL te berekenen

  3. QMYSQL-stuurprogramma installeren

  4. MySQL-volgorde op problemen