【问题标题】:PostGIS, Indexed Intersecting Generated GeographyPostGIS,索引相交生成的地理
【发布时间】:2015-06-23 11:18:31
【问题描述】:

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

我才开始:

我有一个设备表:

  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;

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

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

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

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

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 时效果不会更好。

所以,看来我可能必须将地理位置存储在表中,这很好,然后我可以索引它。

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

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

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

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

有什么想法吗?

【问题讨论】:

    标签: postgresql indexing gis postgis


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 我想,因为这两个点都有自己的半径,所以构造圆并询问它们是否接触更有意义,但我想我可以使用ST_DWithin(location, other_location, radius + other_radius),看看中心是否比彼此的半径之和更接近。这会降低查询的有效性吗?
    • 好的,我尝试运行查询,但无法让它使用索引,但即使另一个查询确实使用了索引,它也比另一个查询快得多。它没有返回相同的结果集,它返回了 4096 行而不是 4048 行,但我不知道其中哪一个是“正确的”。 4096 行当然不是表中的每一项。
    • 从他们的查询中取出半径会导致它命中索引,但显然我不能将位置和半径一起放入索引中,因为半径不能进入要点,它没有如果我不使用 gist,似乎不会达到索引。不过总的来说,没有索引的查询非常快,所以我认为我现在不在乎。
    • @psycotica0 对,我现在记得只有在距离固定时才使用索引。 ST_DWithin 可能会返回比缓冲区技术更多的结果,因为缓冲区不会绘制(例如)具有半径的完美圆。想象一个圆和一个直径相同的八边形:圆的面积总是最大的,因为在边缘附近会有空隙。
    • 当然。我们计划使用DT_DWithinradius + constant_maximum_device_radius 作为第一次通过,这允许它击中索引,然后以特定半径进行第二次通过。我觉得应该不错。
    猜你喜欢
    • 2023-03-29
    • 2017-04-05
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    • 2017-07-07
    • 1970-01-01
    • 2014-02-08
    • 1970-01-01
    相关资源
    最近更新 更多