sov*_*ndy 1 sql postgis ruby-on-rails postgresql-9.1
我正在使用postgis2.0和postgres 9.1 db.我的目标是尽可能接近优化查询,以获得特定半径范围内的附近位置,并将它们与距离顺序放在一起.Location模型具有latlong空间类型postgis扩展的属性和distance_from计算距离给定POINT(长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
Run Code Online (Sandbox Code Playgroud)
我可以看到我distance用order子句和map子句双重计算两次,但是想不出我应该如何存储sql查询的距离的立即值.所以在map{}我重新计算它.
`.order("ST_Distance_Sphere(ST_GeomFromEWKB(latlong) , ST_GeomFromText('POINT(#{long} #{lat})', 4326) ) ")`
Run Code Online (Sandbox Code Playgroud)
"distance" => ar.distance_from(lat, long)
如果我没有错,在我的情况下使用ST_DWithin可以帮助我快速得到一个位置是否在内而不是先计算距离.因此,如果说一个查询只返回10-100个位置,ST_DWithin将比纯粹使用STDistance有助于加快查询速度.
我还能提高多少?我的位置数据库大小约为10000条记录.感谢您的时间,谢谢.
目前我还在使用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))
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这些坐标是在latlon没有g;-)的情况下调用的
给我几分钟,我会试着弄清楚Postgres如何优化您的查询,以及是否需要手动优化它.
这个查询可以更快(如果有很多匹配),但也可以更慢,因为ST_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
Run Code Online (Sandbox Code Playgroud)
说明:
您的原始查询将首先使用对所有找到的对象的快速ST_DWithin和后续调用ST_Distance_Sphere来过滤结果.
我的查询将计算ST_Distance_Sphere数据库中的所有对象,然后使用整数比较过滤它们.
要在Rails中使用,您可以直接调用 Location.find_by_sql(...)
(调用我的表,调用measurement包含Point的列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
Run Code Online (Sandbox Code Playgroud)
我的查询:
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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的:22901中只有816个匹配的行.我的查询耗时更长.
如果我使距离更大,则两个查询都会快速相等.
如果所有行(= 22901行)都在搜索范围内,我的查询速度会快一点:180对210ms.
所以你可能会继续使用你的解决方案;)
另一个可能获得1-2%性能的建议:不要使用GeomFromText,你可以rgeo直接给你的数据库一个Point对象作为参数,而不是2个坐标.