PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Penugasan unik dari titik terdekat antara dua tabel

Skema tabel

Untuk menegakkan aturan Anda, cukup nyatakan pvanlagen.buildid UNIQUE :

ALTER TABLE pvanlagen ADD CONSTRAINT pvanlagen_buildid_uni UNIQUE (buildid);

building.gid adalah PK, seperti yang diungkapkan pembaruan Anda. Untuk juga menegakkan integritas referensial, tambahkan FOREIGN KEY kendala ke buildings.gid .

Anda telah menerapkan keduanya sekarang. Tetapi akan lebih efisien untuk menjalankan UPDATE besar di bawah sebelum Anda menambahkan batasan ini.

Ada banyak lagi yang harus ditingkatkan dalam definisi tabel Anda. Pertama, buildings.gid serta pvanlagen.buildid harus ketik integer (atau mungkin bigint jika Anda membakar banyak dari nilai PK). numeric adalah omong kosong yang mahal.

Mari kita fokus pada masalah inti:

Kueri Dasar untuk menemukan gedung terdekat

Kasusnya tidak sesederhana kelihatannya. Ini adalah "tetangga terdekat" masalah, dengan komplikasi tambahan dari tugas unik.

Kueri ini menemukan satu nearest terdekat bangunan untuk setiap PV (kependekan dari PV Anlage - baris dalam pvanlagen ), di mana keduanya belum ditetapkan:

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;

Untuk membuat kueri ini cepat, Anda membutuhkan indeks GiST spasial fungsional pada buildings untuk membuatnya banyak lebih cepat:

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

Tidak yakin mengapa kamu tidak

Jawaban terkait dengan penjelasan lebih lanjut:

Bacaan lebih lanjut:

Dengan indeks di tempat, kita tidak perlu membatasi kecocokan dengan gemname yang sama untuk kinerja. Hanya lakukan ini jika itu adalah aturan yang sebenarnya untuk ditegakkan. Jika harus selalu diperhatikan, sertakan kolom dalam batasan FK:

Sisa Masalah

Kita dapat menggunakan kueri di atas dalam UPDATE penyataan. Setiap PV hanya digunakan sekali, tetapi lebih dari satu PV mungkin masih menemukan gedung yang sama menjadi yang paling dekat. Anda hanya mengizinkan satu PV per gedung. Jadi bagaimana Anda mengatasinya?

Dengan kata lain, bagaimana Anda akan menetapkan objek di sini?

Solusi sederhana

Salah satu solusi sederhananya adalah:

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;

Saya menggunakan DISTINCT ON (b_gid) untuk mengurangi menjadi tepat satu baris per bangunan, memilih PV dengan jarak terpendek. Detail:

Untuk setiap bangunan yang terdekat untuk lebih dari satu PV, hanya PV terdekat yang ditetapkan. Kolom PK gid (alias pv_gid ) berfungsi sebagai tiebreak jika keduanya sama-sama dekat. Dalam kasus seperti itu, beberapa PV dihapus dari pembaruan dan tetap tidak ditetapkan . Ulangi kueri sampai semua PV ditetapkan.

Ini masih merupakan algoritme sederhana , meskipun. Melihat diagram saya di atas, ini menetapkan gedung 4 ke PV 4 dan gedung 5 ke PV 5, sementara 4-5 dan 5-4 mungkin akan menjadi solusi yang lebih baik secara keseluruhan ...

Selain:ketik untuk dist kolom

Saat ini Anda menggunakan numeric untuk itu. kueri asli Anda menetapkan integer constant yang konstan , tidak ada gunanya membuat numeric .

Dalam kueri baru saya ST_Distance() mengembalikan jarak sebenarnya dalam meter sebagai double precision . Jika kita hanya menetapkan bahwa kita mendapatkan 15 atau lebih digit pecahan dalam numeric tipe data, dan nomornya bukan itu tepat untuk memulai. Saya sangat ragu Anda ingin menyia-nyiakan penyimpanan.

Saya lebih suka menyimpan double precision asli dari perhitungan. atau, lebih baik lagi , bulat sesuai kebutuhan. Jika meter cukup tepat, cukup berikan dan simpan integer (membulatkan angka secara otomatis). Atau kalikan dengan 100 terlebih dahulu untuk menyimpan 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. Bisakah saya menggunakan kata kunci Postgres sebagai alias dalam daftar pilihan?

  2. kolom tidak ada kesalahan bahkan ketika menggunakan kata kunci 'sebagai'

  3. Cara Bekerja Dengan Database PostgreSQL

  4. Hitungan dan penjumlahan tidak valid dalam kueri tab silang menggunakan PostgreSQL

  5. pekerjaan pgAgent gagal dengan kesalahan otentikasi