首页 > 解决方案 > 在 Radius 中查找 GeographyPoints 的最佳方法

问题描述

我在 SQL Server 2019 标准上有一个包含数百万个位置的表和一个包含大约 10 万个兴趣点的表。现在我正在尝试将以下逻辑投射到查询中:“向我显示所有在半径 500m 到 2000m 范围内有公交车站的位置”(将来我想指定给定范围内的公交车站的确切数量半径)

我最初的方法是这样的:

SELECT
    L.ID,
    COUNT(*)
FROM
    dbo.Locations AS L
    INNER JOIN POI AS P ON P.GeographyPoint.STDistance(L.GeographyPoint) BETWEEN 500 AND 2000

这基本上是可行的,但执行时间对于 Web 应用程序来说是不可接受的,并且会随着距离和 POI 数量的增加而增加。我也试过加入

P.GeographyPoint.STBuffer(500).STContains(L.GeographyPoint) = 0 AND
P.GeographyPoint.STBuffer(2000).STContains(L.GeographyPoint) = 1

Filter(L.GeographyPoint) = 1STIntersects(L.GeographyPoint) = 1略有不同但仍不令人满意的结果。我还尝试在预先计算的缓冲地理列上为 1000m 半径创建空间索引,这需要一个小时并创建了一个性能仍然很差的巨大索引空间。缓存所有 POI 到所有位置的所有距离将导致大约 4000 亿行,所以这也是没有选择的。

任何帮助表示赞赏!

标签: sql-servergisspatial

解决方案


我认为您预先计算您所在位置周围区域的本能是一种很好的本能。但正如另一位评论者所说,空间查询优化假设了一些事情,而你的“这个和那个之间的距离”不符合这些假设。

然而!我想我想出了“一个奇怪的技巧”来让你得到你想要的东西。请注意以下事项:

declare @p geography = geography::Point(37.540830, -122.299880, 4236);

select @p.STBuffer(2000).STIntersection(@p.STBuffer(500).ReorientObject());

这将创建一个外半径为 2000m、内半径为 500m 的甜甜圈,以指定点为中心。如果您将其存储在预先计算的列中,您应该能够执行以下操作:

SELECT
    L.ID,
    COUNT(*)
FROM
    dbo.Locations AS L
    INNER JOIN POI AS P ON P.GeographyPoint.STIntersects(L.Donut) = 1;

为了使这种尝试变得便宜,您可以将这些甜甜圈放入一个单独的表中,该表只是(LocationID,Donut)。这样,您可以计算这些点,例如,您的 100 个点,并尝试无需更改实际位置表的空间查询。如果您走这条路线,您可以更改查询以针对此新表而不是针对位置。


推荐阅读