Làm thế nào để tìm điểm hiệu quả nhất trên đường dữ liệu?


10

Tôi có một bảng PostgreSQL 9.1 với hàng trăm ngàn ĐIỂM PostGIS. Đối với mỗi trong số này, tôi muốn tìm điểm gần nhất trong một bảng ĐIỂM khác. Các điểm trong bảng thứ hai đại diện cho một lưới trên toàn thế giới, vì vậy tôi biết sẽ luôn có một trận đấu trong vòng 1 độ. Đây là truy vấn tôi đang sử dụng cho đến nay, sử dụng các chỉ mục GIST, do đó, nó khá nhanh (tổng cộng khoảng 30 giây).

SELECT DISTINCT ON (p.id)
    p.id, ST_AsText(p.pos)
    , ST_AsText(first_value(g.location) OVER (PARTITION BY p.id ORDER BY ST_Distance(p.pos, g.location::geography)))
FROM point p
JOIN grid g ON ST_DWithin(p.pos::geometry, g.location, 1)

Vấn đề duy nhất là dòng thời gian. Các điểm lưới chỉ có vĩ độ 180, không -180. Khi sử dụng phiên bản hình học của ST_Distance, điều này không trả về các điểm ở phía bên kia của đường dữ liệu. Ví dụ. nếu p.pose là POINT(-179.88056 -16.68833)điểm lưới gần nhất có thể POINT(180 -16.25), nhưng truy vấn trên không trả về nó. Cách tốt nhất để khắc phục điều này là gì?

Tôi thực sự không muốn có hai tọa độ cho một điểm lưới đơn (-180 và +180). Tôi đã thử thêm vào hàm riêng của mình để kiểm tra trường hợp cụ thể này, nhưng sau đó truy vấn không trả về sau 5 phút, có lẽ vì nó không thể sử dụng chỉ mục nữa. Tôi cũng đã thử sử dụng phiên bản địa lý của ST_DWithin và truy vấn đó cũng không trở lại sau 5 phút.


Câu hỏi hay (và hack thông minh trong câu trả lời của bạn!). Tuy nhiên, người ta phải tự hỏi: nếu phần mềm không thể nhận ra rằng -180 = 180 cho kinh độ, thì có lẽ họ đang giả vờ đây là các tọa độ được chiếu và đang sử dụng thuật toán Euclide để tìm các điểm gần nhất, sẽ tạo ra lỗi (gần tinh tế đường xích đạo, rất lớn gần các cực và kinh tuyến + -180). Tôi không biết liệu điều đó có dẫn đến các vấn đề quan trọng trong ứng dụng của bạn hay không, nhưng ở nhiều người khác thì điều đó sẽ xảy ra, và công việc đó sẽ không khắc phục được các lỗi.
whuber

Điểm hay, nhưng trong trường hợp này, ứng dụng khách sẽ không thực hiện các phép tính "gần nhất" khác - nó sẽ chỉ nhận được một số dữ liệu liên quan đến điểm lưới được trả về từ truy vấn của tôi.
EM0

Câu trả lời:


6

OK, cuối cùng tôi đã tìm ra một cách để hack nó không chỉ hoạt động xung quanh vấn đề dateline, mà còn nhanh hơn.

CREATE OR REPLACE FUNCTION nearest_grid_point(point geography(Point))
RETURNS integer
AS $BODY$
    SELECT pointid
    FROM
    (
            -- The normal case
        SELECT pointid, location
        FROM grid
        WHERE ST_DWithin($1::geometry, location, 1)

        UNION ALL

            -- The dateline hack
        SELECT pointid, location
        FROM grid
        WHERE (ST_X($1::geometry) < -178.75 AND longitude = 180)
    ) sub
    ORDER BY ST_Distance($1, location::geography)
    LIMIT 1;
$BODY$ LANGUAGE SQL STABLE;

SELECT p.id, ST_AsText(p.pos), g.pointid, ST_AsText(g.location)
FROM point p
JOIN grid g ON nearest_grid_point(p.pos) = g.pointid

Tôi đã rất ngạc nhiên khi thấy rằng chức năng này, được gọi cho mỗi hàng, nhanh hơn chức năng cửa sổ ban đầu, nhưng nó - nhanh hơn 10 lần. Hiệu suất PostgreSQL thực sự là một nghệ thuật đen!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.