sql >> Databáze >  >> RDS >> PostgreSQL

Jedinečné přiřazení nejbližších bodů mezi dvěma tabulkami

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:

Další čtení:

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



  1. Chybí artefakt com.oracle:ojdbc6:jar:11.2.0 ?

  2. Proč date() nepřevádí YYMMDDHHMM do formátu data přijatelného pro MySQL správně?

  3. SQL Server - V klauzule s deklarovanou proměnnou

  4. Jaký je rozdíl mezi varchar a varchar2 v Oracle?