ST_DWithin不使用非文字参数

时间:2016-12-01 19:59:26

标签: postgresql postgis

我在Amazon RDS上使用PostreSQL 9.3 w / PostGIS 2.1.8。我有一个名为project_location的表,它定义了" geo-fences" (每个基本上是一个坐标和半径)。使用名为" location"的几何列存储地理围栏。和一个名为' radius'的双列。我在位置列上有一个空间索引。

CREATE TABLE project_location
(
  ...
  location geography(Point,4326),
  radius double precision NOT NULL,
  ...
)
CREATE INDEX gix_project_location_location 
ON project_location USING gist (location);

该表目前有大约50,000条记录。如果我查询表以查找geo-fence包含点的所有project_locations,如

SELECT COUNT(*) 
FROM project_location 
WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(-84.1000, 34.0000),4326)::geography, radius);

我发现没有使用空间索引。 EXPLAIN的结果显示如下:

"Aggregate  (cost=11651.97..11651.98 rows=1 width=0)"
"  ->  Seq Scan on project_location  (cost=0.00..11651.97 rows=1 width=0)"
"        Filter: ((location && _st_expand('0101000020E610000066666666660655C00000000000004140'::geography, radius)) AND ('0101000020E610000066666666660655C00000000000004140'::geography && _st_expand(location, radius)) AND _st_dwithin(location, '0101000020E610000066666666660655C00000000000004140'::geography, radius, true))"

但是,如果半径是一个常数值,如下所示

SELECT COUNT(*) 
FROM project_location 
WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(-84.1000, 34.0000),4326)::geography, 1000);

空间索引用于EXPLAIN显示

"Aggregate  (cost=8.55..8.56 rows=1 width=0)"
"  ->  Index Scan using gix_project_location_location on project_location  (cost=0.28..8.55 rows=1 width=0)"
"        Index Cond: (location && '0101000020E610000066666666660655C00000000000004140'::geography)"
"        Filter: (('0101000020E610000066666666660655C00000000000004140'::geography && _st_expand(location, 1000::double precision)) AND _st_dwithin(location, '0101000020E610000066666666660655C00000000000004140'::geography, 1000::double precision, true))"

阅读了ST_DWithin如何使用索引,我理解为什么会这样。基本上,基于半径的边界框用于"预过滤"候选人指出在对这些点进行相对昂贵的距离计算之前确定可能的匹配。

我的问题是有没有办法进行这种类型的搜索,以便可以使用空间索引?基本上是一种用一堆可变半径地理围栏来查询表的方法吗?

3 个答案:

答案 0 :(得分:1)

PostGIS允许使用功能索引加快查询速度。我不确定如何在geography数据类型中执行此操作,因为那里没有ST_Expand,但如果将数据存储在某些Mercator投影中(例如,SRID=3857),查询将非常简单。 / p>

点子:

  • 生成一个展开radius个单位的框;
  • 在这些方框上建立索引;
  • 根据这些方框查询用户点;
  • 按精确半径重新检查。

project_location表格上:

create index on project_location using gist (ST_Expand(location, radius));

现在,您可以使用ST_Expand(location, radius),就像它是您的索引几何列一样。

select count(*) from project_location where ST_Intersects(ST_Expand(location, radius), <your_point>) and ST_Distance(location, <your_point>) < radius;

现在您正在跳过ST_DWithin,因为您希望重新检查从不尝试使用索引,并在几何函数上使用索引。

对于geography,您可以尝试使用ST_Envelope(ST_Buffer(geom, radius))存根ST_Expand。

答案 1 :(得分:0)

为了消除简单的事情,您可以尝试将半径转换为double precision

SELECT COUNT(*) 
FROM project_location 
WHERE ST_DWithin(
  location,
  ST_SetSRID(ST_MakePoint(-84.1000, 34.0000),4326)::geography,
  radius::double precision -- or CAST(radius AS double precision)
);

同时粘贴输出

  • \dfS ST_DWithin
  • \dfS _ST_DWithin

答案 2 :(得分:0)

我能想到的唯一方法就是创建一个fence_contain

 geofence_id    point_id

当然,您需要更新/创建地理围栏和点的触发器以保持表格更新。