【问题标题】:Spatial Index not being used未使用空间索引
【发布时间】:2016-05-07 17:23:29
【问题描述】:

我在 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
参考:空
行数:612602
额外:使用 where

这是我的环境:
服务器类型: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

【问题讨论】:

    标签: mysql indexing mariadb spatial


    【解决方案1】:

    很遗憾,ST_Distance() &lt; 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() &lt; 10.0 带有 lng/lat 点做了一些奇怪的事情。

    此查询生成的结果存在一个缺陷;它返回边界框中的所有点,而不仅仅是在指定半径内。这可以通过单独的距离计算来解决。我已经写了所有这些in some detail here

    注意:对于 GPS 分辨率的经纬度,32 位 FLOAT 数据具有足够的精度。 DOUBLE 是 MySQL 的地理扩展使用的。当您以度为单位工作时,小数点后五位以上超出了 GPS 的精度。 DECIMAL() 不是 lat/lng 坐标的理想数据类型。

    【讨论】:

    • 哇。多么丰富的信息啊!今天晚些时候会仔细看看这个。完全有道理。谢谢奥利!我一直在寻找这些信息。
    • 在我今天早上跑去参加我女儿的篮球比赛之前必须做一个小测试(今晚会更深入一些)。因此,即使我在 10 英里内输入已知的纬度/经度,它也不会返回任何匹配项。我已经尝试了几个已知点,但仍然没有。
    • 如果表中的点使用 SRID(空间参考标识符),请确保添加与 GeomFromText() 函数的第二个(可选)参数相同的 SRID:例如 GeomFromText('...', 4326)
    • 当使用 4326 的 SRID(或类似的我猜)时,空间数据被定义为 X=lon 和 Y=lat,所以需要使用@latpoint 而不是@lngpoint,反之亦然,在上面的代码中。
    • 上面边界坐标的计算不太准确:janmatuschek.de/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-04
    • 1970-01-01
    相关资源
    最近更新 更多