8

Я хотел бы получить все точки в пределах заданного диапазона другого набора точек. Скажем, найдите все магазины в пределах 500 м от любой станции метро.Запрос ближайших соседей PostGis

Я написал этот запрос, который довольно медленно, и хотел бы, чтобы оптимизировать его:

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

Я бегу на последних версиях Postgres и PostGIS (Postgres 9.5, PostGIS 2.2.1)

Здесь метаданные таблицы:

          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) 

Вот результат EXPLAIN (ПРОАНАЛИЗИРУЙТЕ, буферные):

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

ли они геометрии или географии? – fradal83

+0

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

+0

@ Геометрия FrancescoD'Alesio – Chris

ответ

0

В конце концов я пришел к выводу, что не мог жить - вычислить расстояние между тысячами точек интереса и тысячами мест в течение реалистичного промежутка времени (< 1сек).

Поэтому я предопределяю все: каждый раз, когда создается или обновляется местоположение или POI, я сохраняю минимальное расстояние между каждым местоположением и каждым видом POI, чтобы иметь возможность ответить на вопрос «какие местоположения ближе, чем X метров от такого рода POI ".

Вот модуль я закодирован для этой цели (это в эликсире, но основная часть сырой SQL)

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

Я думаю, вам следует изменить решение, postgis все еще выполняет запрос в Structured Database, он мощный, но не быстрый по специальному требованию, возможно, вам нужен elasticsearch.

elasticsearch хорош в поиске местоположения Geo, но не очень хорош в процессе обработки данных Geo, я думаю, что вам нужны они оба.

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

+0

Спасибо за ваш ответ, но я действительно хотел бы придерживаться postgres. Если нет возможности удовлетворить мою потребность, я рассмотрю возможность использования дополнительного механизма хранения данных. – Chris

0

Я думаю, что вы используете версию географии st_dwithin, из-за четвертый параметр.

Попробуйте изменить запрос к этому:

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

Если это не решит, пожалуйста, выкладываю объяснить анализ еще раз.

+0

Удаление параметра FALSE запустило запрос 8.6 сек (вместо 3сек). Вот новый анализ объяснения: https://gist.github.com/cblavier/726139eda4cd574340bd – Chris

 Смежные вопросы

  • Нет связанных вопросов^_^