很遗憾,ST_Distance() < threshold 不是 sargable 搜索条件。为了满足这个查询,MySQL 必须计算表中每一行的函数值,然后将其与阈值进行比较。所以它必须进行全表扫描(或者可能是全索引扫描)。
要利用索引来加速此查询,您将需要一个边界框标准。查询要复杂得多,但也快得多。假设几何图形中的 x/y 点以度数表示纬度/经度,则查询可能如下所示:
set @latpoint = 38.0234332;
set @lngpoint = -94.0724223;
set @r = 10.0; /* ten mile radius */
set @units=69.0; /* 69 statute miles per degree */
SELECT AsText(geo)
FROM markers
WHERE MbrContains(GeomFromText(
CONCAT('LINESTRING(', @latpoint-(@r/@units),' ',
@lngpoint-(@r /(@units* COS(RADIANS(@latpoint)))),
',',
@latpoint+(@r/@units) ,' ',
@lngpoint+(@r /(@units * COS(RADIANS(@latpoint)))),
')')),
geo)
这是如何工作的?一方面,MbrContains(bound,item) 函数是 sargable。另一方面,又大又丑的 concat 项目产生了一条从边界矩形的西南角到东北角的对角线。使用您的数据点和十英里半径,它看起来像这样。
LINESTRING(37.8785 -94.2564,38.1684 -93.8884)
当您在MbrContains() 的第一个参数中使用该对角线的GeomFromText() 渲染时,它用作边界矩形。然后MbrContains() 可以利用漂亮的四叉树几何索引。
第三,ST_Distance(),在 MySQL 中,不处理大圆经纬度计算。 (PostgreSQL 有一个更全面的GIS extension。)MySQL 就像平地里的烙饼一样愚蠢。它假定几何对象中的点以平面几何表示。所以 ST_Distance() < 10.0 带有 lng/lat 点做了一些奇怪的事情。
此查询生成的结果存在一个缺陷;它返回边界框中的所有点,而不仅仅是在指定半径内。这可以通过单独的距离计算来解决。我已经写了所有这些in some detail here。
注意:对于 GPS 分辨率的经纬度,32 位 FLOAT 数据具有足够的精度。 DOUBLE 是 MySQL 的地理扩展使用的。当您以度为单位工作时,小数点后五位以上超出了 GPS 的精度。 DECIMAL() 不是 lat/lng 坐标的理想数据类型。