Bạn đang ở gần đó. Có một mẹo nhỏ đó là sử dụng toán tử riêng biệt của Postgres , nó sẽ trả về trận đấu đầu tiên của mỗi kết hợp - khi bạn đặt hàng theo ST_Distance, nó sẽ trả lại điểm gần nhất từ mỗi senal cho mỗi cổng.
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");
Nếu bạn biết rằng khoảng cách tối thiểu trong mỗi trường hợp không nhiều hơn một lượng x, (và bạn có chỉ số không gian trên các bảng của mình), bạn có thể tăng tốc độ này bằng cách đặt một WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", distance)
, ví dụ: nếu tất cả các khoảng cách tối thiểu được biết là không quá 10km, sau đó:
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");
Rõ ràng, điều này cần phải được sử dụng một cách thận trọng, vì nếu khoảng cách tối thiểu lớn hơn, bạn sẽ không nhận được hàng nào cho sự kết hợp của senal và cổng.
Lưu ý: Thứ tự theo thứ tự phải khớp với phân biệt theo thứ tự, điều này có ý nghĩa, vì khác biệt là lấy nhóm riêng biệt đầu tiên dựa trên một số thứ tự.
Giả định rằng bạn có một chỉ mục không gian trên cả hai bảng.
CHỈNH SỬA 1 . Có một tùy chọn khác, đó là sử dụng các toán tử <-> và <#> của Postgres, (tính toán khoảng cách giữa điểm trung tâm và giới hạn hộp), giúp sử dụng hiệu quả hơn chỉ số không gian và không yêu cầu hack ST_DWithin để tránh n ^ 2 so sánh. Có một bài viết blog tốt giải thích cách họ làm việc. Điều chung cần lưu ý là hai toán tử này hoạt động trong mệnh đề 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;
CHỈNH SỬA 2 . Vì câu hỏi này đã nhận được rất nhiều sự chú ý và hàng xóm gần nhất (kNN) nói chung là một vấn đề khó khăn (về thời gian chạy thuật toán) trong GIS, có vẻ đáng để mở rộng phần nào phạm vi ban đầu của câu hỏi này.
Cách tiêu chuẩn để tìm x hàng xóm gần nhất của một đối tượng là sử dụng LATITH THAM GIA (về mặt khái niệm tương tự như đối với mỗi vòng lặp). Mượn không biết xấu hổ từ câu trả lời của dbaston , bạn sẽ làm một cái gì đó như:
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
Vì vậy, nếu bạn muốn tìm 10 cổng gần nhất, được sắp xếp theo khoảng cách, bạn chỉ cần thay đổi mệnh đề LIMIT trong truy vấn con bên. Điều này khó thực hiện hơn nhiều nếu không có THAM GIA LATITH và liên quan đến việc sử dụng logic loại ARRAY. Trong khi phương pháp này hoạt động tốt, nó có thể được tăng tốc rất lớn nếu bạn biết bạn chỉ phải tìm kiếm ở một khoảng cách nhất định. Trong trường hợp này, bạn có thể sử dụng ST_DWithin (Sign.geom, port.geom, 1000) trong truy vấn phụ, do cách lập chỉ mục hoạt động với toán tử <-> - một trong những hình học nên là một hằng số, thay vì một tham chiếu cột - có thể nhanh hơn nhiều. Vì vậy, ví dụ, để có được 3 cổng gần nhất, trong vòng 10km, bạn có thể viết một cái gì đó như sau.
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;
Như mọi khi, việc sử dụng sẽ thay đổi tùy thuộc vào phân phối dữ liệu và truy vấn của bạn, vì vậy GIẢI THÍCH là người bạn tốt nhất của bạn.
Cuối cùng, có một vấn đề nhỏ, nếu sử dụng TRÁI thay vì CROSS THAM GIA LATITH trong đó bạn phải thêm TRUE sau các bí danh truy vấn bên, ví dụ:
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;