2016-01-14 14 views
8

मैं सभी बिंदुओं के किसी भी सेट के भीतर दिए गए सभी बिंदुओं को पुनर्प्राप्त करना चाहता हूं। मान लें, किसी भी सबवे स्टेशन के 500 मीटर के भीतर सभी दुकानें खोजें।पोस्टगिस निकटतम पड़ोसियों की क्वेरी

मैं इस क्वेरी, जो काफी धीमी है लिखा था, और यह अनुकूलन करने के लिए करना चाहते हैं:

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) 

उसका वर्णन यहाँ (विश्लेषण, buffers) का परिणाम है:

यहाँ तालिका मेटाडाटा है

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

@ फ्रांसेस्को डी'एलेसीओ ज्यामिति – Chris

उत्तर

0

मैं अंत में निष्कर्ष पर आया कि मैं यथार्थवादी समय (< 1sec) के भीतर हजारों बिंदुओं और हजारों स्थानों के बीच दूरी की गणना नहीं कर सकता था।

तो इसके बजाय मैं सबकुछ पूर्ववत करता हूं: प्रत्येक बार जब कोई स्थान या पीओआई बनाया/अपडेट किया जाता है, तो मैं प्रत्येक स्थान और प्रत्येक प्रकार के पीओआई के बीच न्यूनतम दूरी को संग्रहीत करने के लिए "प्रश्न जो उत्तर के करीब हैं इस तरह के पीओआई से एक्स मीटर "।

यहाँ मॉड्यूल मैं इस उद्देश्य (यह अमृत में है, लेकिन मुख्य हिस्सा कच्चे एसक्यूएल है)

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

मुझे लगता है कि आपको एक समाधान बदलना चाहिए, पोस्टगिस अभी भी संरचित डेटाबेस में एक क्वेरी चला रहा है, यह शक्तिशाली है, लेकिन विशेष आवश्यकता में त्वरित नहीं है, आपको लोचदार खोज की आवश्यकता हो सकती है।

लोचदार खोज भौगोलिक खोज में अच्छा है, लेकिन भौगोलिक डेटा प्रक्रिया में अच्छा नहीं है, मुझे लगता है कि आपको दोनों की आवश्यकता है।

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

+0

आपके उत्तर के लिए धन्यवाद, लेकिन मैं वास्तव में पोस्टग्रेस से चिपकना चाहता हूं। यदि मेरी ज़रूरत को पूरा करने का कोई तरीका नहीं है, तो मैं एक अतिरिक्त स्टोरेज इंजन – 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.6sec (3sec के बजाए) चलाया गया है। यहां नई व्याख्या का विश्लेषण किया गया है: https://gist.github.com/cblavier/726139eda4cd574340bd – Chris

संबंधित मुद्दे