Saya perlu mendapatkan pada setiap elemen pada satu tabel titik terdekat dari tabel lain. Tabel pertama berisi rambu lalu lintas dan yang kedua adalah Aula Masuk kota. Masalahnya adalah bahwa saya tidak dapat menggunakan fungsi ST_ClosestPoint dan saya harus menggunakan fungsi ST_Distance dan mendapatkan catatan min (ST_distance) tetapi saya cukup buntu dalam membangun kueri.
CREATE TABLE traffic_signs
(
id numeric(8,0) ),
"GEOMETRY" geometry,
CONSTRAINT traffic_signs_pkey PRIMARY KEY (id),
CONSTRAINT traffic_signs_id_key UNIQUE (id)
)
WITH (
OIDS=TRUE
);
CREATE TABLE entrance_halls
(
id numeric(8,0) ),
"GEOMETRY" geometry,
CONSTRAINT entrance_halls_pkey PRIMARY KEY (id),
CONSTRAINT entrance_halls_id_key UNIQUE (id)
)
WITH (
OIDS=TRUE
);
Saya perlu mendapatkan id dari entrnce_hall terdekat dari setiap traffic_sign.
Permintaan saya sejauh ini:
SELECT senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY") as dist
FROM traffic_signs As senal, entrance_halls As port
ORDER BY senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY")
Dengan ini saya mendapatkan jarak dari setiap traffic_sign ke setiap entrance_hall. Tetapi bagaimana saya bisa mendapatkan jarak minimun saja?
Salam,
Jawaban:
Anda hampir sampai. Ada sedikit trik untuk menggunakan operator berbeda Postgres , yang akan mengembalikan kecocokan pertama dari setiap kombinasi - karena Anda memesan oleh ST_Distance, secara efektif itu akan mengembalikan titik terdekat dari setiap senal ke setiap port.
Jika Anda tahu bahwa jarak minimum dalam setiap kasus tidak lebih dari jumlah x, (dan Anda memiliki indeks spasial pada tabel Anda), Anda dapat mempercepat ini dengan meletakkan
WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", distance)
, misalnya, jika semua jarak minumum diketahui tidak lebih dari 10 km, maka:Jelas, ini perlu digunakan dengan hati-hati, karena jika jarak minimum lebih besar, Anda tidak akan mendapatkan baris untuk kombinasi senal dan port.
Catatan: Pesanan demi pesanan harus sesuai dengan pesanan berbeda, yang masuk akal, karena berbeda adalah mengambil grup berbeda pertama berdasarkan beberapa pemesanan.
Diasumsikan bahwa Anda memiliki indeks spasial di kedua tabel.
EDIT 1 . Ada opsi lain, yaitu menggunakan operator <-> dan <#> Postgres, (masing-masing, perhitungan titik pusat dan kotak batas) yang menggunakan indeks spasial secara lebih efisien dan tidak memerlukan peretasan ST_DWithin untuk menghindari n ^ 2 perbandingan. Ada artikel blog bagus yang menjelaskan cara kerjanya. Hal umum yang perlu diperhatikan adalah bahwa kedua operator ini bekerja di klausa ORDER BY.
EDIT 2 . Karena pertanyaan ini telah menerima banyak perhatian dan k-tetangga terdekat (kNN) umumnya merupakan masalah yang sulit (dalam hal run-time algoritmik) di GIS, tampaknya ada gunanya untuk memperluas sedikit pada lingkup asli dari pertanyaan ini.
Cara standar untuk menemukan x tetangga terdekat dari satu objek adalah dengan menggunakan LATERAL JOIN (secara konseptual mirip dengan a untuk setiap loop). Meminjam tanpa malu dari jawaban dbaston , Anda akan melakukan sesuatu seperti:
Jadi, jika Anda ingin menemukan 10 port terdekat, dipesan berdasarkan jarak, Anda cukup mengubah klausa LIMIT di sub-kueri lateral. Ini jauh lebih sulit untuk dilakukan tanpa GABUNGAN LATERAL dan melibatkan penggunaan logika tipe ARRAY. Meskipun pendekatan ini bekerja dengan baik, itu dapat dipercepat jika Anda tahu Anda hanya perlu mencari jarak yang diberikan. Dalam contoh ini, Anda dapat menggunakan ST_DWithin (signs.geom, ports.geom, 1000) dalam subquery, yang karena cara pengindeksan bekerja dengan operator <-> - salah satu geometri harus berupa konstanta, bukan sebuah referensi kolom - mungkin jauh lebih cepat. Jadi, misalnya, untuk mendapatkan 3 pelabuhan terdekat, dalam jarak 10 km, Anda dapat menulis sesuatu seperti berikut ini.
Seperti biasa, penggunaan akan bervariasi tergantung pada distribusi dan kueri data Anda, jadi EXPLAIN adalah teman terbaik Anda.
Akhirnya, ada gotcha minor, jika menggunakan LEFT daripada CROSS JOIN LATERAL , Anda harus menambahkan ON TRUE setelah permintaan lateral alias, misalnya,
sumber
Ini dapat dilakukan dengan
LATERAL JOIN
di PostgreSQL 9.3+:sumber
Pendekatan dengan cross-join tidak menggunakan indeks dan membutuhkan banyak memori. Jadi pada dasarnya Anda punya dua pilihan. Pre 9.3 Anda akan menggunakan subquery yang berhubungan. 9.3+ Anda dapat menggunakan a
LATERAL JOIN
.KNN GIST dengan twist Lateral Segera hadir ke database di dekat Anda
(pertanyaan pasti akan segera menyusul)
sumber
ST_DISTANCE()
untuk menemukan poligon terdekat dan cross join menyebabkan server kehabisan memori. Kueri poligon terdekat masih AFAIK yang belum terpecahkan.@ John Barça
ORDER BY salah!
Kanan
jika tidak, ia tidak akan mengembalikan yang terdekat, hanya yang memiliki sedikit port id
sumber
SELECT DISTINCT ON (points.id) points.id, lines.id, ST_Distance(lines.geom, points.geom) as dist FROM development.passed_entries As points, development."de_muc_rawSections_cleaned" As lines ORDER BY points.id, ST_Distance(lines.geom, points.geom),lines.id;