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:
- Kueri spasial pada tabel besar dengan beberapa gabungan mandiri berkinerja lambat
- Bagaimana cara menanyakan semua baris dalam radius 5 mil dari koordinat saya?
Bacaan lebih lanjut:
- http://workshops.boundlessgeo.com/postgis-intro/knn. html
- http://www.postgresonline.com/journal/archives/306-KNN-GIST-with-a-Lateral-twist-Coming-soon-to-a-database-near- you.html
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