目前,我还在使用 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_Distance
ST_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 个坐标。