好吧,我想我有一个奇怪的问题,而且我一直在寻找答案。
我刚开始:
我有一张设备表:
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,以便如果有位置或半径的更新,它会自动重新计算存储的地理位置,但它抱怨递归规则......
那么,我有没有触及过一些非常简单的事情,或者我错过了一些微妙的观点?
我很困惑,有点沮丧,甚至可能超出我的深度。
有什么想法吗?
答案 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
添加到接近滤镜。