PostGIS,Indexed Intersecting Generated Geography

时间:2015-04-16 20:12:48

标签: postgresql indexing gis postgis

好吧,我想我有一个奇怪的问题,而且我一直在寻找答案。

我刚开始:

我有一张设备表:

  Column  |   Type    |                      Modifiers                       
----------+-----------+------------------------------------------------------
 id       | integer   | not null default nextval('devices_id_seq'::regclass)
 location | geography | 
 radius   | integer   |

表示一堆设备的位置和位置分辨率。

我想找到哪个设备在区域范围内。

所以,我可以进行如下查询:

SELECT count(id) FROM devices WHERE ST_intersects(ST_buffer(ST_GeographyFromText('POINT(-80.519142 43.460270)'), 20000), ST_buffer(location, radius));

硬编码位置最终来自连接。

所以,我有不确定的设备和一个大区域,我想知道哪个设备可能在该区域内。 此查询有效,但在测试100000台设备时,需要28秒。

所以我想索引它。 这是一整天,我知道的事情,但不是正确的答案。

首先,像CREATE INDEX device_buffer ON devices USING gist (st_buffer(location, radius));这样的索引似乎存在,但是看看上面的查询它实际上从未被使用过。

查看解释的输出,它显示geography(st_transform(st_buffer(st_transform(geometry(location), _st_bestsrid(location, location)), (radius)::double precision), 4326)),这似乎是扩展版本。

对其进行索引似乎也无济于事。

所以,要弄清楚麻烦是ST_INTERSECT还是我,我做了:

CREATE MATERIALIZED VIEW device_buffer_view AS SELECT id, ST_BUFFER(location, radius) as buffer FROM devices;

并在该视图的缓冲区字段上添加索引。 该查询命中了索引。 好。 这告诉我,我的缓冲区可以在交叉点索引。

但是,这仍然不是很好,因为我实际上并不想要物化视图。

我想要表格中的位置和半径,因为应用程序的其他部分会查看此数据,并允许稍后进行调整。仅存储生成的地理位置无济于事。

基于我在网上找到的东西,我试过了:

CREATE FUNCTION geog(rec devices) RETURNS geography IMMUTABLE LANGUAGE SQL AS 'SELECT ST_BUFFER($1.location, $1.radius);';

这允许我做SELECT devices.geog FROM devices并对其进行索引,但是将索引设置为:

CREATE INDEX device_geog ON devices USING GIST ((devices.geog));

在选择查询中使用devices.geog时效果不佳。

所以,似乎我可能不得不将地理存储在表中,这很好,然后我可以将其编入索引。

但我不希望它被非规范化。

我试图制作一个RULE,以便如果有位置或半径的更新,它会自动重新计算存储的地理位置,但它抱怨递归规则......

那么,我有没有触及过一些非常简单的事情,或者我错过了一些微妙的观点?

我很困惑,有点沮丧,甚至可能超出我的深度。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

这不是一个奇怪的问题。使用缓冲区作为邻近搜索对于新用户来说是一个非常常见的错误。它慢得多,不使用空间索引,并且不太精确(因为缓冲区通常每季度有16个段)。通过半径缓冲特征是直观的,但对于邻近搜索而言计算过度。

要使查询更可靠,更快,请使用ST_DWithin。此函数查找与其他要素指定距离内的要素。它还将使用GiST空间索引。

SELECT count(id)
FROM devices
WHERE ST_DWithin(location, ST_MakePoint(-80.519142, 43.460270)::geography, radius);

如果您需要更高的速度以降低精度,请使用球形距离代替球体,方法是将use_spheroid=false添加到接近滤镜。