1

postgres 9.1 db で postgis2.0 を使用しています。私の目標は、最適化されたクエリにできるだけ近く書いて、特定の半径内の近くの場所を取得し、それらを距離順で出力することです。Location モデルにはlatlong、空間タイプの postgis 拡張の属性とdistance_from、特定の POINT (long lat) からの距離を計算するメソッドがあります。Railsコードで次のようにクエリを書きました:

def self.nearby(lat, long, radius)
    nearby = Location.where("ST_DWithin(ST_GeomFromEWKB(latlong), ST_GeomFromText('POINT(#{long} #{lat})', 4326),?, false )", radius)
    .order("ST_Distance_Sphere(ST_GeomFromEWKB(latlong) , ST_GeomFromText('POINT(#{long} #{lat})', 4326) ) ")
    .map{|ar| 
      { "id" => ar.id,
        "distance" => ar.distance_from(lat, long)
      } 
    }
end

order句とmap句を使用して2回計算していることがわかりdistanceますが、SQLクエリからの距離の即時値をどのように保存すればよいかわかりません。だからmap{}私はそれを再計算します。

 `.order("ST_Distance_Sphere(ST_GeomFromEWKB(latlong) , ST_GeomFromText('POINT(#{long} #{lat})', 4326) ) ")`

"distance" => ar.distance_from(lat, long)

私が間違っていなければ、私の場合 ST_DWithin を使用すると、最初に距離を計算するのではなく、場所が範囲内にあるかどうかをすばやく知ることができます。したがって、1 つのクエリで 10 ~ 100 の場所しか返されない場合、ST_DWithin を使用すると、純粋に STDistance を使用するよりもクエリを高速化できます。

あとどれくらい改善できますか?私の場所のデータベース サイズは、約 10000 レコードになります。お時間をいただき、ありがとうございます。

4

1 に答える 1

6

現在、Rails & PostGIS を使用したアプリケーションにも取り組んでいます。:-)

複雑なクエリについては、ActiveRecords メソッドを使用する代わりに、単純な SQL を記述する方法を選択しました。これにより、管理が少し簡単になります。

あなたのものは:

SELECT
  *
FROM location
WHERE
  ST_DWithin(ST_GeomFromEWKB(latlong),
    ST_GeomFromText('POINT(#{long} #{lat})', 4326), ?, false)
ORDER BY
  ST_Distance_Sphere(ST_GeomFromEWKB(latlong),
    ST_GeomFromText('POINT(#{long} #{lat})', 4326))

ちなみに、これらの座標は;-)latlonなしで呼び出されますg

Postgres がクエリをどのように最適化するか、手動で最適化する必要があるかどうかを確認しますので、少々お待ちください。


このクエリは高速になる可能性があります (一致が多数ある場合) が、またはST_DWithinよりもはるかに高速であるため、遅くなる可能性もあります。したがって、膨大な量のデータでテストしてください。ST_DistanceST_Distance_Sphere

SELECT
  *
FROM ( 
  SELECT
    l.*,
    (
      ST_DISTANCE_SPHERE(ST_GeomFromEWKB(latlong),
        ST_GeomFromText('POINT(#{long} #{lat})', 4326))
    ) AS d
  FROM location l
) x
WHERE d < ?
ORDER BY d

説明:

元のクエリは、最初に fast を使用して結果をフィルタリングし、その後、見つかったすべてのオブジェクトをST_DWithin呼び出します。ST_Distance_Sphere

私のクエリはデータベース内のすべてのオブジェクトを計算ST_Distance_Sphereし、その後整数比較を使用してそれらをフィルタリングします。


Rails で使用する場合は、単純に呼び出すことができます。Location.find_by_sql(...)


説明する 分析する

(私のテーブルが呼び出されmeasurement、ポイントを含む列が呼び出されますgroundtruth)

クエリ:

Sort  (cost=341.05..341.06 rows=1 width=172) (actual time=3.676..3.731 rows=816 loops=1)
  Sort Key: (_st_distance(geography(groundtruth), '0101000020E6100000EE7C3F355EF24F4019390B7BDA011940'::geography, 0::double precision, false))
  Sort Method: quicksort  Memory: 139kB
  ->  Bitmap Heap Scan on measurement m  (cost=9.67..341.04 rows=1 width=172) (actual time=0.330..3.257 rows=816 loops=1)
        Recheck Cond: (groundtruth && '01030000000100000005000000EE7C3F355E724D4064E42CEC6907F43FEE7C3F355E724D408C9C853DED80264077BE9F1A2F3951408C9C853DED80264077BE9F1A2F39514064E42CEC6907F43FEE7C3F355E724D4064E42CEC6907F43F'::geometry)
        Filter: (('0101000000EE7C3F355EF24F4019390B7BDA011940'::geometry && st_expand(groundtruth, 5::double precision)) AND _st_dwithin(groundtruth, '0101000000EE7C3F355EF24F4019390B7BDA011940'::geometry, 5::double precision))
        ->  Bitmap Index Scan on groundtruth_idx  (cost=0.00..9.67 rows=189 width=0) (actual time=0.186..0.186 rows=855 loops=1)
              Index Cond: (groundtruth && '01030000000100000005000000EE7C3F355E724D4064E42CEC6907F43FEE7C3F355E724D408C9C853DED80264077BE9F1A2F3951408C9C853DED80264077BE9F1A2F39514064E42CEC6907F43FEE7C3F355E724D4064E42CEC6907F43F'::geometry)
Total runtime: 3.932 ms

私のクエリ:

Sort  (cost=9372.84..9391.92 rows=7634 width=172) (actual time=19.256..19.312 rows=816 loops=1)
  Sort Key: (st_distance(m.groundtruth, '0101000000EE7C3F355EF24F4019390B7BDA011940'::geometry))
  Sort Method: quicksort  Memory: 139kB
  ->  Seq Scan on measurement m  (cost=0.00..8226.01 rows=7634 width=172) (actual time=0.040..18.863 rows=816 loops=1)
        Filter: (st_distance(groundtruth, '0101000000EE7C3F355EF24F4019390B7BDA011940'::geometry) < 5::double precision)
Total runtime: 19.396 ms

ご覧のとおり、22901 から一致する行は 816 行しかありませんでした。そして、私のクエリにはさらに長い時間がかかりました。

距離を大きくすると、両方のクエリが等しく高速になります。

すべての行 (= 22901 行) が検索範囲内にある場合、私のクエリは少し速くなります: 180 対 210 ミリ秒。

したがって、おそらくあなたのソリューションにとどまるでしょう;)


1 ~ 2% のパフォーマンスが得られる可能性がある別の提案: GeomFromText を使用しないでくださいrgeo。2 つの座標ではなく、Point オブジェクトをパラメーターとしてデータベースに直接指定するために使用できます。

于 2013-03-04T11:03:02.243 に答える