Neredeyse oradasın. Postgres'in farklı operatörünü kullanacak ve her kombinasyonun ilk eşleşmesini geri döndürecek küçük bir hile var - ST_Distance tarafından sipariş verirken, her bir sinyalden her bir limana en yakın noktayı etkili bir şekilde döndürür.
SELECT
DISTINCT ON (senal.id) 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");
Her bir durumda minimum mesafenin x miktarından fazla olmadığını biliyorsanız (ve masanızda uzamsal bir dizine sahipseniz) WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", distance)
, örneğin, tüm minimum mesafelerin olduğu biliniyorsa , bunu hızlandırabilir o zaman en fazla 10km
SELECT
DISTINCT ON (senal.id) senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY") as dist
FROM traffic_signs As senal, entrance_halls As port
WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", 10000)
ORDER BY senal.id, port.id, ST_Distance(port."GEOMETRY", senal."GEOMETRY");
Açıkçası, bu asgari mesafe daha büyükse sanki senal ve limanın birleşimi için herhangi bir satır elde edemeyeceğiniz gibi dikkatli kullanılması gerekir.
Not: Siparişteki siparişte, siparişte belirtilen ilke göre farklı olan, siparişte belirgin olan ilk sıradaki gruba uyması gerekir.
Her iki tabloda da bir uzaysal indeksinizin olduğu varsayılmaktadır.
EDIT 1 . Mekansal indeksi daha verimli kullanan ve ST_DWithin kesmesini gerektirmeyen, Postgres'in <-> ve <#> işleçlerini (sırasıyla merkez nokta ve sınırlama kutusu mesafe hesaplamaları) kullanmak olan ve başka bir seçenek yoktur. 2 karşılaştırması. Nasıl çalıştıklarını açıklayan iyi bir blog yazısı var . Unutulmaması gereken genel şey, bu iki operatörün ORDER BY deyiminde çalıştığıdır.
SELECT senal.id,
(SELECT port.id
FROM entrance_halls as port
ORDER BY senal.geom <#> port.geom LIMIT 1)
FROM traffic_signs as senal;
EDIT 2 . Bu soru çok fazla dikkat çekti ve K-en yakın komşular (kNN) GIS'de genellikle zor bir sorun (algoritmik çalışma zamanı) olarak olduğundan, bu sorunun orijinal kapsamı üzerinde biraz genişlemeye değecek gibi görünüyor.
Bir nesnenin en yakın komşusunu bulmak için standart yol, bir LATERAL JOIN kullanmaktır (kavramsal olarak her bir döngüye benzer). Dbaston'ın cevabından utanmadan ödünç almak , şöyle bir şey yaparsın:
SELECT
signs.id,
closest_port.id,
closest_port.dist
FROM traffic_signs
CROSS JOIN LATERAL
(SELECT
id,
ST_Distance(ports.geom, signs.geom) as dist
FROM ports
ORDER BY signs.geom <-> ports.geom
LIMIT 1
) AS closest_port
Bu nedenle, mesafeye göre sıralanan en yakın 10 portu bulmak istiyorsanız, yanal sorguda LIMIT yan tümcesini değiştirmeniz yeterlidir. LATERAL JOINS olmadan yapmak çok zordur ve ARRAY tipi mantığı kullanmayı gerektirir. Bu yaklaşım iyi çalışsa da, yalnızca belirli bir mesafeyi araştırmanız gerektiğini biliyorsanız, çok büyük hız kazanabilir. Bu örnekte, alt sorguda ST_DWithin (signs.geom, ports.geom, 1000) kullanabilirsiniz. Dizinlemenin <-> operatörü ile çalışması nedeniyle - geometrilerden birinin sabit olması gerekir. sütun başvurusu - çok daha hızlı olabilir. Örneğin, en yakın 3 bağlantı noktasını 10 km içinde almak için aşağıdaki gibi bir şeyler yazabilirsiniz.
SELECT
signs.id,
closest_port.id,
closest_port.dist
FROM traffic_signs
CROSS JOIN LATERAL
(SELECT
id,
ST_Distance(ports.geom, signs.geom) as dist
FROM ports
WHERE ST_DWithin(ports.geom, signs.geom, 10000)
ORDER BY ST_Distance(ports.geom, signs.geom)
LIMIT 3
) AS closest_port;
Her zaman olduğu gibi, kullanım veri dağıtımınıza ve sorgularınıza bağlı olarak değişecektir, bu nedenle EXPLAIN en iyi arkadaşınızdır.
Son olarak, CROSS JOIN LATERAL yerine LEFT kullanıyorsanız , lateral sorgu takma adlarından sonra ON TRUE eklemeniz gerektiğinde , örneğin
SELECT
signs.id,
closest_port.id,
closest_port.dist
FROM traffic_signs
LEFT JOIN LATERAL
(SELECT
id,
ST_Distance(ports.geom, signs.geom) as dist
FROM ports
ORDER BY signs.geom <-> ports.geom
LIMIT 1
) AS closest_port
ON TRUE;