【问题标题】:ST_DWithin sometimes doesn't use indexST_DWithin 有时不使用索引
【发布时间】:2018-10-22 10:57:07
【问题描述】:

我将 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 并不能保证使用索引,当距离太小(45)时,它会退回到Seq Scan。这是应该的还是我做错了什么?

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

【问题讨论】:

    标签: postgresql gis postgis


    【解决方案1】:

    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 显然会将其转换为地理,这也解释了为什么执行需要更长的时间并忽略索引,因为每次转换都会产生一个新对象未在您的原始列中编入索引。

    【讨论】:

    • 嗯,这是真的,我在仔细查看签名时发现了这一点。但根据我的经验,postgres 非常严格,所以每次它无法推断类型时都会抛出异常,而且它似乎产生了正确的结果,只是不使用 index.html 。这很令人困惑。
    猜你喜欢
    • 1970-01-01
    • 2017-04-16
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 2011-09-15
    • 2011-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多