PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Уникално присвояване на най-близките точки между две маси

Схема на таблица

За да наложите вашето правило, просто декларирайте pvanlagen.buildid УНИКАЛНО :

ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);

building.gid е PK, както разкри вашата актуализация. За да наложите референтна цялост, добавете ВЪНШЕН КЛЮЧ ограничение към buildings.gid .

Досега сте внедрили и двете. Но би било по-ефективно да стартирате голямото UPDATE по-долу преди вие добавяте тези ограничения.

Има още много неща, които трябва да се подобрят в дефиницията на вашата таблица. Например, buildings.gid както и pvanlagen.buildid трябва да е тип цяло число (или евентуално bigint ако изгаряте много на PK стойности). числов е скъпа глупост.

Нека се съсредоточим върху основния проблем:

Основна заявка за намиране на най-близката сграда

Случаят не е толкова прост, колкото може да изглежда. Това е "най-близък съсед" проблем, с допълнителното усложнение на уникалното присвояване.

Тази заявка намира най-близкия един сграда за всяка PV (съкратено от PV Anlage - ред в pvanlagen ), където нито един от тях все още не е присвоен:

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;

За да направите тази заявка бърза, витрябва пространствен, функционален GiST индекс на сгради за да го направимного по-бързо:

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

Не съм сигурен защо ти не

Свързани отговори с повече обяснения:

Допълнителна информация:

С индекса на място не е необходимо да ограничаваме съвпаденията до едно и също gemname за изпълнение. Направете това само ако това е действително правило, което трябва да се прилага. Ако трябва да се спазва през цялото време, включете колоната в ограничението FK:

Оставащ проблем

Можем да използваме горната заявка в АКТУАЛИЗАЦИЯ изявление. Всяка PV се използва само веднъж, но повече от една PV все пак може да намерят същата сграда да си най-близо. Разрешавате само един PV на сграда. И така, как бихте разрешили това?

С други думи, как бихте присвоили обекти тук?

Просто решение

Едно просто решение би било:

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;

Използвам DISTINCT ON (b_gid) да се намали точно до един ред на сграда, избирайки PV с най-късото разстояние. Подробности:

За всяка сграда, която е най-близка за повече един PV, се присвоява само най-близкият PV. PK колоната gid (псевдоним pv_gid ) служи като тайбрек, ако двама са еднакво близки. В такъв случай някои PV отпадат от актуализацията и остават неприсвоени . Повтаряне заявката, докато всички PV бъдат присвоени.

Това все още е опростен алгоритъм , обаче. Гледайки моята диаграма по-горе, това присвоява сграда 4 на PV 4 и сграда 5 на PV 5, докато 4-5 и 5-4 вероятно биха били по-добро решение като цяло ...

Отстрани:въведете за dist колона

В момента използвате numeric за него. първоначалната ви заявка присвои постоянно цяло число , без значение в numeric .

В новата ми заявка ST_Distance() връща действителното разстояние в метри като double прецизност . Ако просто присвоим това, получаваме около 15 дробни цифри в numeric тип данни и числото не е това точно като начало. Сериозно се съмнявам, че искате да губите хранилището.

Предпочитам да запазя оригиналния double precision от изчислението. или, още по-добре , кръгли според нуждите. Ако измервателните уреди са достатъчно точни, просто преобразувайте и запазете цяло число (автоматично закръгляване на числото). Или първо умножете със 100, за да спестите cm:

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Защо PostgreSQL комбинира сериите по грешен начин?

  2. Как да изчислим медиана в PostgreSQL

  3. Postgres ограничение за уникален диапазон от дата и час

  4. Масив от заявки за цикъл „for await“ за помощник за транзакции на postgresql

  5. Има ли начин да деактивирате актуализациите/изтриванията, но все пак да позволите на тригери да ги изпълняват?