我在GEO_LOCATION列上有空间索引,但是当我执行EXPLAIN时,它并没有显示正在使用索引。有谁能说出原因?
EXPLAIN
SELECT AsText(GEO_LOCATION)
FROM PERSON
WHERE ST_Distance(POINT(-94.0724223,38.0234332), GEO_LOCATION) <= 10
id:1
选择类型:SIMPLE
表:人员
类型:所有
possible_keys:NULL
key:NULL
key_len:NULL
ref:NULL
行:612602
额外:使用
这是我的环境:
服务器类型:MariaDB
服务器版本:10.1.8-MariaDB - mariadb.org二进制分发
协议版本:10
服务器字符集:UTF-8 Unicode(utf8)
Apache / 2.4.17(Win32)OpenSSL / 1.0.2d PHP / 5.6.14
数据库客户端版本:libmysql - mysqlnd 5.0.11-dev - 20120503
PHP扩展:mysqli文档
PHP版本:5.6.14
答案 0 :(得分:2)
不幸的是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。另一方面,丑陋的大丑项目产生了从边界矩形的西南角到东北角的对角线。使用您的数据点和十英里半径,它看起来像这样。
LINESTRING(37.8785 -94.2564,38.1684 -93.8884)
当您在GeomFromText()
的第一个参数中使用该对角线的MbrContains()
渲染时,它将用作边界矩形。然后MbrContains()
可以利用漂亮的四叉树几何索引。
第三,MySQL中的ST_Distance()
并不能处理大圆纬度和经度计算。 (PostgreSQL有一个更全面的GIS extension。)MySQL就像平地里的烙饼一样愚蠢。它假设您的几何对象中的点以平面几何体表示。所以带有lng / lat点的ST_Distance() < 10.0
会有些奇怪。
此查询生成的结果存在一个缺陷;它返回边界框中的所有点,而不仅仅是在指定的半径范围内。这可以通过单独的距离计算来解决。我已经写了所有这些in some detail here。
注意:对于GPS分辨率的纬度和经度,32位FLOAT
数据具有足够的精度。 DOUBLE
是MySQL的地理扩展使用的。当你以度数工作时,小数点后超过五个位置超出了GPS的精度。 DECIMAL()
不是lat / lng坐标的理想数据类型。