我在 GEO_LOCATION 列上有一个空间索引,但是当我执行 EXPLAIN 时,它没有显示该索引正在被使用。谁能告诉我为什么吗?
EXPLAIN
SELECT AsText(GEO_LOCATION)
FROM PERSON
WHERE ST_Distance(POINT(-94.0724223,38.0234332), GEO_LOCATION) <= 10
id:1
选择类型:简单
表:人
类型:全部
可能的键:NULL
键:NULL
key_len: NULL
引用:NULL
行:612602
额外:使用位置
这是我的环境:
服务器类型:MariaDB
服务器版本:10.1.8-MariaDB - mariadb.org 二进制发行版
协议(protocol)版本: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
最佳答案
不幸的是 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()
然后可以利用漂亮的四叉树几何索引。
第三,ST_Distance()
,在 MySQL 中,不处理大圆纬度和经度计算。 ( PostgreSQL 有一个更全面的 GIS extension 。)MySQL 的愚蠢就像平原上的烙饼。它假设几何对象中的点以平面几何表示。所以ST_Distance() < 10.0
与 lng/lat 点做一些奇怪的事情。
此查询生成的结果存在一个缺陷;它返回边界框中的所有点,而不仅仅是指定半径内的点。这可以通过单独的距离计算来解决。我已经把这些写下来了 in some detail here .
注意:对于 GPS 分辨率纬度和经度,32 位 FLOAT
数据有足够的精度。 DOUBLE
这是 MySQL 的地理扩展所使用的。当您以度为单位时,小数点后五位以上超出了 GPS 的精度。 DECIMAL()
不是纬度/经度坐标的理想数据类型。
关于mysql - 未使用空间索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35093608/