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

我可以看到我distance用 order 子句和 map 子句双重计算了两次,但想不出我应该如何存储距离 sql​​ 查询的即时值。所以在map{}我重新计算它。

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

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

如果我没记错的话,在我的情况下使用 ST_DWithin 可以帮助我快速获得位置是否在其中而不是先计算距离的答案。因此,如果说一个查询将仅返回 10-100 个位置,则 ST_DWithin 将有助于加快查询速度,而不是纯粹使用 STDistance。

我还能提高多少?我的位置数据库大小约为 10000 条记录。感谢您的时间,谢谢。

4

1 回答 1

6

目前,我还在使用 Rails 和 PostGIS 的应用程序中工作。:-)

对于复杂的查询,我选择了编写普通 SQL 的方式而不是使用 ActiveRecords 方法,这使得事情更容易维护。

你的是:

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 将如何优化您的查询,以及是否需要手动优化它。


此查询可以更快(如果有很多匹配项),但也可以更慢,因为它比orST_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

解释:

您的原始查询将首先使用快速过滤结果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给您的数据库一个 Point 对象作为参数,而不是 2 个坐标。

于 2013-03-04T11:03:02.243 回答