2016-01-14 151 views
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(ANALYZE,缓冲剂)的结果:

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之间的最小距离,以便能够回答“哪些位置比这类POI的X米“。

这里是我编写用于此目的(它在灵药,但主要部分是原始的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仍然在结构化数据库中运行一个查询,它是强大的,但在特殊需求上并不快,可能你需要elasticsearch。

elasticsearch擅长地理位置搜索,但不擅长地理数据处理,我认为你需要他们两个。

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