คุณใกล้จะถึงแล้ว มีเคล็ดลับเล็กน้อยที่จะใช้โอเปอเรเตอร์ที่ชัดเจนของ Postgres ซึ่งจะคืนค่าการจับคู่ครั้งแรกของชุดค่าผสมแต่ละชุด - เมื่อคุณสั่งซื้อโดย ST_ ระยะทางได้อย่างมีประสิทธิภาพจะคืนค่าจุดที่ใกล้ที่สุดจากแต่ละ Senal ไปยังแต่ละพอร์ต
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");
หากคุณรู้ว่าระยะห่างต่ำสุดในแต่ละกรณีนั้นไม่เกินจำนวน x, (และคุณมีดัชนีเชิงพื้นที่บนโต๊ะของคุณ) คุณสามารถเร่งความเร็วได้โดยการใส่ a WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", distance)
เช่นถ้าระยะทางขั้นต่ำทั้งหมดรู้ว่าเป็น ไม่เกิน 10 กม. จากนั้น:
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");
เห็นได้ชัดว่าสิ่งนี้จำเป็นต้องใช้ด้วยความระมัดระวังราวกับว่าระยะทางขั้นต่ำนั้นมากกว่าคุณจะไม่ได้รับแถวสำหรับการรวมกันของ senal และพอร์ต
หมายเหตุ:การสั่งซื้อตามคำสั่งซื้อจะต้องตรงกับความแตกต่างในการสั่งซื้อซึ่งทำให้เข้าใจได้เนื่องจากความแตกต่างคือการใช้กลุ่มที่แตกต่างกันครั้งแรกตามการสั่งซื้อบางอย่าง
มันจะสันนิษฐานว่าคุณมีดัชนีเชิงพื้นที่บนทั้งสองตาราง
แก้ไข 1 มีตัวเลือกอื่นซึ่งใช้ตัวดำเนินการ <-> และ <#> ของ Postgres (จุดกึ่งกลางและการคำนวณระยะห่างของกล่องตามลำดับ) ซึ่งใช้ดัชนีดัชนีเชิงพื้นที่อย่างมีประสิทธิภาพมากขึ้นและไม่ต้องการแฮ็ค ST_D ภายในแฮ็คเพื่อหลีกเลี่ยง n เปรียบเทียบ ^ 2 มีบทความบล็อกที่ดีอธิบายวิธีการทำงาน สิ่งทั่วไปที่ควรทราบก็คือตัวดำเนินการทั้งสองนี้ทำงานในข้อ ORDER BY
SELECT senal.id,
(SELECT port.id
FROM entrance_halls as port
ORDER BY senal.geom <#> port.geom LIMIT 1)
FROM traffic_signs as senal;
แก้ไข 2 เนื่องจากคำถามนี้ได้รับความสนใจเป็นอย่างมากและเพื่อนบ้านที่ใกล้ที่สุด (kNN) เป็นปัญหาที่ยาก (ในแง่ของอัลกอริทึมรันไทม์) ใน GIS ดูเหมือนว่าคุ้มค่าที่จะขยายขอบเขตเดิมของคำถามนี้
วิธีมาตรฐานในการค้นหา x เพื่อนบ้านที่ใกล้ที่สุดของวัตถุหนึ่งคือการใช้ LATERAL JOIN (แนวคิดคล้ายกับ a สำหรับแต่ละลูป) การยืมคำตอบจากdbaston อย่างไร้ยางอายคุณจะทำสิ่งที่ชอบ:
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
ดังนั้นหากคุณต้องการค้นหา 10 พอร์ตที่ใกล้ที่สุดซึ่งเรียงลำดับตามระยะทางคุณเพียงแค่เปลี่ยนคำสั่ง LIMIT ในแบบสอบถามย่อยด้านข้าง สิ่งนี้ทำได้ยากกว่าหากไม่ได้เข้าร่วมในภายหลังและเกี่ยวข้องกับการใช้ตรรกะประเภท ARRAY ในขณะที่วิธีการนี้ใช้งานได้ดีคุณสามารถเร่งความเร็วได้อย่างมหาศาลหากคุณรู้ว่าคุณต้องค้นหาระยะทางที่กำหนด ในตัวอย่างนี้คุณสามารถใช้ST_DWithin (signs.geom, ports.geom, 1000) ในแบบสอบถามย่อยซึ่งเนื่องจากวิธีการทำดัชนีทำงานกับตัวดำเนินการ <-> หนึ่งในรูปทรงเรขาคณิตควรเป็นค่าคงที่แทนที่จะเป็น การอ้างอิงคอลัมน์ - อาจเร็วกว่ามาก ตัวอย่างเช่นในการรับพอร์ตที่ใกล้ที่สุด 3 แห่งภายในระยะทาง 10 กม. คุณสามารถเขียนสิ่งต่อไปนี้
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;
เช่นเคยการใช้งานจะแตกต่างกันไปขึ้นอยู่กับการกระจายข้อมูลและการสืบค้นของคุณดังนั้นจึงควรอธิบายถึงเพื่อนที่ดีที่สุดของคุณ
ในที่สุดก็มี gotcha เล็กน้อยหากใช้LEFTแทนที่จะเป็นCROSS JOIN LATERALคุณต้องเพิ่มค่าTRUEหลังจากการสืบค้นด้วยชื่อแทนด้านข้างเช่น
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;