我正在使用PostGIS与Postgresql,以便能够通过存储在位置列Geometry/Point SRID: 4326
中的坐标来定位某个半径内的条目。以下是我正在尝试的两个查询:
第一个距离以米为单位且use_spheroid = true
EXPLAIN ANALYZE SELECT count(*) FROM "cars" WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(20, -30), 4326), 105000, true) LIMIT 1000;
QUERY PLAN
--------------
Limit (cost=11884.28..11884.30 rows=1 width=8) (actual time=18.843..18.844 rows=1 loops=1)
-> Aggregate (cost=11884.28..11884.30 rows=1 width=8) (actual time=18.842..18.843 rows=1 loops=1)
-> Seq Scan on cars (cost=0.00..11883.33 rows=381 width=0) (actual time=0.486..18.827 rows=38 loops=1)
Filter: (((location)::geography && '0101000020E610000000000000000034400000000000003EC0'::geography) AND ('0101000020E610000000000000000034400000000000003EC0'::geography && _st_expand((location)::geography, '105000'::double precision)) AND _st_dwithin((location)::geography, '0101000020E610000000000000000034400000000000003EC0'::geography, '105000'::double precision, true))
Rows Removed by Filter: 28549
Planning time: 0.166 ms
Execution time: 18.878 ms
(7 rows)
其次,我假设,接受度数的距离,默认情况下use_spheroid为false。更正:原来这仍然使用use_spheroid = true,但匹配此调用的函数签名需要几何和SRID单位,即4326度。
EXPLAIN ANALYZE SELECT count(*) FROM "cars" WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(20, -30), 4326), 1) LIMIT 1000;
QUERY PLAN
-----------------------------
Limit (cost=145.30..145.31 rows=1 width=8) (actual time=0.154..0.155 rows=1 loops=1)
-> Aggregate (cost=145.30..145.31 rows=1 width=8) (actual time=0.154..0.154 rows=1 loops=1)
-> Bitmap Heap Scan on cars (cost=4.59..145.29 rows=3 width=0) (actual time=0.050..0.147 rows=37 loops=1)
Recheck Cond: (location && '0103000020E6100000010000000500000000000000000033400000000000003FC000000000000033400000000000003DC000000000000035400000000000003DC000000000000035400000000000003FC000000000000033400000000000003FC0'::geometry)
Filter: (('0101000020E610000000000000000034400000000000003EC0'::geometry && st_expand(location, '1'::double precision)) AND _st_dwithin(location, '0101000020E610000000000000000034400000000000003EC0'::geometry, '1'::double precision))
Rows Removed by Filter: 11
Heap Blocks: exact=47
-> Bitmap Index Scan on cars_location_index (cost=0.00..4.59 rows=42 width=0) (actual time=0.037..0.037 rows=48 loops=1)
Index Cond: (location && '0103000020E6100000010000000500000000000000000033400000000000003FC000000000000033400000000000003DC000000000000035400000000000003DC000000000000035400000000000003FC000000000000033400000000000003FC0'::geometry)
Planning time: 0.280 ms
Execution time: 0.188 ms
(11 rows)
两个查询都返回类似的结果(+/-因为精度)。然而,第一个运行速度慢100倍。同样将use_spheroid
设置为false并不保证使用索引,当距离小(<0.4)或太大(> 45)时,它会回退到Seq Scan。这是它应该如何或我做错了什么?
附加:经过一些实验后,我将列类型更改为Geography.Point,现在它总是使用索引。问题似乎已经解决了,但我仍然对我用几何类型观察到的行为感到困惑。
答案 0 :(得分:4)
ST_DWithin文档指出第一个函数签名接受地理类型而非几何类型:
boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters, boolean use_spheroid);
因为(location,ST_SetSRID(ST_MakePoint(20,-30),4326))是所有几何,所以函数的执行是混乱的。 我认为你的第二个功能正常,因为你正在执行的是这个签名:
boolean ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);
正如您所说,将列类型切换为Geography而不是Geometry会解决问题,因为这样可以正确执行:
boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters);
boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters, boolean use_spheroid);
希望这有点帮助。
修改:
在documentation中找到了这一部分,表明数据输入
如果标准几何类型数据是SRID 4326
,则它将自动加入地理位置
这可以解释为什么Postgres接受了你对ST_DWithin()的第一次调用,因为postgis显然会将它转换为地理位置,这也解释了为什么执行需要更长时间而忽略了索引,因为每次转换都会导致一个新对象没有在原始列中编入索引。