2016-01-14 22 views
8

Tôi muốn truy xuất tất cả các điểm trong phạm vi nhất định của một nhóm điểm khác. Giả sử, tìm tất cả các cửa hàng trong phạm vi 500m của bất kỳ ga tàu điện ngầm nào.Bài đăngGần truy vấn lân cận gần nhất

tôi đã viết truy vấn này, đó là khá chậm, và muốn tối ưu hóa nó:

SELECT DISCTINCT ON(locations.id) locations.id FROM locations, pois 
WHERE pois.poi_kind = 'subway' 
AND ST_DWithin(locations.coordinates, pois.coordinates, 500, false); 

Tôi đang chạy trên các phiên bản mới nhất của Postgres và PostGIS (Postgres 9.5, PostGIS 2.2.1)

đây là siêu dữ liệu bảng:

          Table "public.locations" 
     Column  |   Type    |      Modifiers 
--------------------+-----------------------------+-------------------------------------------------------- 
id     | integer      | not null default nextval('locations_id_seq'::regclass) 
coordinates  | geometry     | 
Indexes: 
    "locations_coordinates_index" gist (coordinates) 


             Table "public.pois" 
    Column |   Type    |      Modifiers 
-------------+-----------------------------+--------------------------------------------------- 
id   | integer      | not null default nextval('pois_id_seq'::regclass) 
coordinates | geometry     | 
poi_kind_id | integer      | 
Indexes: 
    "pois_pkey" PRIMARY KEY, btree (id) 
    "pois_coordinates_index" gist (coordinates) 
    "pois_poi_kind_id_index" btree (poi_kind_id) 
Foreign-key constraints: 
    "pois_poi_kind_id_fkey" FOREIGN KEY (poi_kind_id) REFERENCES poi_kinds(id) 

Dưới đây là kết quả của GIẢI THÍCH (phân tích, bộ đệm):

Unique (cost=2407390.71..2407390.72 rows=2 width=4) (actual time=3338.080..3338.252 rows=918 loops=1) 
Buffers: shared hit=559 
-> Sort (cost=2407390.71..2407390.72 rows=2 width=4) (actual time=3338.079..3338.145 rows=963 loops=1) 
     Sort Key: locations.id 
     Sort Method: quicksort Memory: 70kB 
     Buffers: shared hit=559 
     -> Nested Loop (cost=0.00..2407390.71 rows=2 width=4) (actual time=2.466..3337.835 rows=963 loops=1) 
      Join Filter: (((pois.coordinates)::geography && _st_expand((locations.coordinates)::geography, 500::double precision)) AND ((locations.coordinates)::geography && _st_expand((pois.coordinates)::geography, 500::double precision)) AND _st_dwithin((pois.coordinates)::geography, (locations.coordinates)::geography, 500::double precision, false)) 
      Rows Removed by Join Filter: 4531356 
      Buffers: shared hit=559 
      -> Seq Scan on locations (cost=0.00..791.68 rows=24168 width=36) (actual time=0.005..3.100 rows=24237 loops=1) 
        Buffers: shared hit=550 
      -> Materialize (cost=0.00..10.47 rows=187 width=32) (actual time=0.000..0.009 rows=187 loops=24237) 
        Buffers: shared hit=6 
        -> Seq Scan on pois (cost=0.00..9.54 rows=187 width=32) (actual time=0.015..0.053 rows=187 loops=1) 
         Filter: (poi_kind_id = 3) 
         Rows Removed by Filter: 96 
         Buffers: shared hit=6 
Planning time: 0.184 ms 
Execution time: 3338.304 ms 
(20 rows) 
+0

Họ hình học hoặc địa lý? – fradal83

+0

https://wiki.postgresql.org/wiki/Slow_Query_Questions –

+0

@ FrancescoD'Alesio geometry – Chris

Trả lời

0

Cuối cùng tôi đã đi đến kết luận tôi không thể tính toán khoảng cách giữa hàng nghìn điểm quan tâm và hàng nghìn địa điểm trong một khoảng thời gian thực tế (< 1 giây). Vì vậy, thay vì tôi tính toán trước mọi thứ: mỗi lần một địa điểm hoặc một POI được tạo/cập nhật, tôi lưu trữ khoảng cách tối thiểu giữa mỗi vị trí và từng loại POI để có thể trả lời câu hỏi "vị trí nào gần hơn X mét từ loại POI này ".

Dưới đây là mô-đun mã hóa tôi cho mục đích này (đó là trong Elixir, nhưng phần chính là SQL thô)

defmodule My.POILocationDistanceService do 

    alias Ecto.Adapters.SQL 
    alias My.Repo 

    def delete_distance_for_location(location_id) do 
    run_query!("DELETE FROM poi_location_distance WHERE location_id = $1::integer", [location_id]) 
    end 

    def delete_distance_for_poi_kind(poi_kind_id) do 
    run_query!("DELETE FROM poi_location_distance WHERE poi_kind_id = $1::integer", [poi_kind_id]) 
    end 

    def insert_distance_for_location(location_id) do 
    sql = """ 
    INSERT INTO poi_location_distance(poi_kind_id, location_id, poi_id, distance) 
    SELECT 
     DISTINCT ON (p.poi_kind_id) 
     p.poi_kind_id as poi_kind_id, 
     l.id as location_id, 
     p.id as poi_id, 
     MIN(ST_Distance_Sphere(l.coordinates, p.coordinates)) as distance 
    FROM locations l, pois p 
    WHERE 
     l.id = $1 
     AND ST_DWithin(l.coordinates, p.coordinates, $2, FALSE) 
    GROUP BY p.poi_kind_id, p.id, l.id 
    ORDER BY p.poi_kind_id, distance; 
    """ 

    run_query!(sql, [location_id, max_distance]) 
    end 

    def insert_distance_for_poi_kind(poi_kind_id, offset \\ 0, limit \\ 10_000_000) do 
    sql = """ 
    INSERT INTO poi_location_distance(poi_kind_id, location_id, poi_id, distance) 
    SELECT 
     DISTINCT ON(l.id, p.poi_kind_id) 
     p.poi_kind_id as poi_kind_id, 
     l.id as location_id, 
     p.id as poi_id, 
     MIN(ST_Distance_Sphere(l.coordinates, p.coordinates)) as distance 
    FROM pois p, (SELECT * FROM locations OFFSET $1 LIMIT $2) as l 
    WHERE 
     p.poi_kind_id = $3 
     AND ST_DWithin(l.coordinates, p.coordinates, $4, FALSE) 
    GROUP BY l.id, p.poi_kind_id, p.id; 
    """ 

    run_query!(sql, [offset, limit, poi_kind_id, max_distance]) 
    end 

    defp run_query!(query, params) do 
    SQL.query!(Repo, query, params) 
    end 

    def max_distance, do: 5000 

end 
0

Tôi nghĩ bạn nên thay đổi giải pháp, postgis vẫn đang chạy truy vấn trong cơ sở dữ liệu có cấu trúc, nó mạnh mẽ, nhưng không nhanh trong yêu cầu đặc biệt, có thể bạn cần elasticsearch.

elasticsearch giỏi ở vị trí địa lý, nhưng không tốt ở quy trình dữ liệu địa lý, tôi nghĩ bạn cần cả hai.

https://www.elastic.co/blog/geo-location-and-search

+0

Cảm ơn câu trả lời của bạn, nhưng tôi thực sự muốn dính vào postgres. Nếu không có cách nào để đáp ứng nhu cầu của tôi, tôi sẽ xem xét sử dụng thêm một công cụ lưu trữ – Chris

0

Tôi nghĩ rằng bạn đang sử dụng phiên bản địa lý của st_dwithin, vì tham số thứ tư.

Hãy thử thay đổi truy vấn của bạn bên dưới:

SELECT DISCTINCT ON(locations.id) locations.id FROM locations, pois 
WHERE pois.poi_kind = 'subway' 
AND ST_DWithin(locations.coordinates, pois.coordinates, 500); 

Nếu nó không giải quyết được, xin vui lòng gửi Các giải thích phân tích một lần nữa.

+0

Xóa tham số FALSE làm cho truy vấn chạy 8.6sec (thay vì 3 giây). Đây là giải thích mới phân tích: https://gist.github.com/cblavier/726139eda4cd574340bd – Chris

Các vấn đề liên quan