ST_DWithin有时不使用索引

Iva*_*rov 4 gis postgresql postgis

我将PostGIS与Postgresql一起使用,以便能够通过存储在location列中的坐标在某个半径内找到条目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)
Run Code Online (Sandbox Code Playgroud)

其次,我假设接受距离(以度为单位),默认情况下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)
Run Code Online (Sandbox Code Playgroud)

两个查询都返回相似的结果(由于精度,+ /-)。但是第一个运行速度慢了100倍。同样设置use_spheroid为false不能保证使用索引,当距离太小(<0.4)或太大(> 45)时,它会退回到Seq Scan。这是应该的样子还是我做错了什么?

补充:经过更多的实验,我将列类型更改为Geography.Point,现在它始终使用索引。问题似乎已经解决,但是我仍然对我在“几何”类型中观察到的行为感到困惑。

M.H*_*san 5

ST_DWithin文档指出第一个函数签名接受地理类型不是几何类型:

boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters, boolean use_spheroid);
Run Code Online (Sandbox Code Playgroud)

由于(location,ST_SetSRID(ST_MakePoint(20,-30),4326))都是几何图形,因此该函数的执行是混乱的是有意义的。我认为您的第二个功能正常运行,因为您正在执行的是此签名:

boolean ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);
Run Code Online (Sandbox Code Playgroud)

如您所述,将列类型切换为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);
Run Code Online (Sandbox Code Playgroud)

希望这个对你有帮助。

编辑:

文档中找到该部分,指出在输入数据时

如果标准几何类型数据属于SRID 4326,则将自动广播到地理区域

这可以解释为什么Postgres接受您对ST_DWithin()的第一次调用,因为postgis显然会将其转换为地理位置,并且还解释了为什么执行时间更长并且忽略索引,因为每次转换都会导致一个新对象未在索引中进行索引您原来的专栏。