PostGIS,索引相交生成的地理

PostGIS, Indexed Intersecting Generated Geography

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

我要开始了:

我有 table 台设备:

  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)); 这样的索引似乎是存在的,但是看看上面的查询,它实际上从未被使用过。

看explain的输出,显示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;

并在该视图的缓冲区字段上放置一个索引。 该查询命中索引。 好的。 这告诉我,我的缓冲区可以在交叉点中建立索引。

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

不过,我想要 table 中的位置和半径,因为应用程序的其他部分会查看此数据,并允许稍后对其进行调整。仅存储生成的地理位置没有帮助。

根据我在网上找到的东西,我试过:

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

这允许我做 SELECT devices.geog FROM devices,并为其编制索引,但要放置一个索引,例如:

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

在 select 查询中使用 devices.geog 时效果并没有更好。

所以,看来我可能必须将地理位置存储在 table 中,这很好,然后我可以对其进行索引。

不过,我不希望它被非规范化。

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

那么,是否有一些非常简单的东西我没有触及,或者我错过了一些微妙的地方?

我很困惑,有点沮丧,甚至可能超出了我的理解范围。

有什么想法吗?

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

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

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

如果您需要以降低精度为代价提高速度,请使用球形距离而不是椭球体,方法是将 use_spheroid=false 添加到邻近过滤器。