加入空间mysql索引

Oro*_*102 11 mysql indexing optimization geospatial

我有两个表:一个有点,另一个有多边形.

CREATE TABLE `points` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `point` point NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM;

CREATE TABLE `ranges` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `poly` polygon NOT NULL,
  PRIMARY KEY (`id`),
  SPATIAL KEY `poly` (`poly`)
) ENGINE=MyISAM;
Run Code Online (Sandbox Code Playgroud)

我希望将范围加入到多边形内的点上.查询看起来很简单:

SELECT * 
  FROM points 
    LEFT JOIN ranges 
      ON MBRCONTAINS(poly, point) 
  WHERE points.id = 2;
Run Code Online (Sandbox Code Playgroud)

此查询工作正常并使用索引,解释的一部分:

table  | type  | possible_keys | key  | key_len
ranges | range | poly          | poly | 34

但是,当我尝试从表中连接几行时points:

SELECT * 
  FROM points 
   LEFT JOIN ranges 
    ON MBRCONTAINS(poly, point) 
  WHERE points.id IN (1,2,3);
Run Code Online (Sandbox Code Playgroud)

一切都崩溃了:

+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table      | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |
+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+
|  1 | SIMPLE      | points     | range | PRIMARY       | PRIMARY | 4       | NULL |      3 | Using where |
|  1 | SIMPLE      | ranges     | ALL   | poly          | NULL    | NULL    | NULL | 155183 |             |
+----+-------------+------------+-------+---------------+---------+---------+------+--------+-------------+

添加FORCE INDEX (poly)没有帮助.

用于测试查询的示例数据(对不起,只有php版本,我不常见SQL程序):

//points
for($i=0;$i<=500;$i++) {
    $point = mt_rand();
    mysql_query('INSERT INTO points (point) VALUES (POINTFROMWKB(POINT('.$point.', 0)))');
}

$qty = 20000;
$max = mt_getrandmax();
$add = $max / $qty
$end = 0;

//polys
while($end < $max) {
    $start = $end;
    $end = mt_rand($start, $start + $add);
    mysql_query('INSERT INTO ranges (poly) VALUES (
        GEOMFROMWKB(POLYGON(LINESTRING(
            POINT('.$start.', -1),
            POINT('.$end.',   -1),
            POINT('.$end.',    1),
            POINT('.$start.',  1),
            POINT('.$start.', -1)
          )))
    )');
}
Run Code Online (Sandbox Code Playgroud)

Ale*_*mov 6

我相信这是因为MySQL不支持合并空间索引.不确定它是否仍然是真的但我过去曾在某处读过它.如果您有OR语句,则不使用空间索引

在你的情况下,你在哪里做points.id = 1,这是一个直接选择,返回一个结果,用于mbrcontains.那使用索引.

当你添加points.in(1,2,3)时,它返回3个结果,每个都需要映射到范围表,因此不起作用

结果

id  select_type     table   type    possible_keys   key     key_len     ref     rows    filtered    Extra
1   SIMPLE  points  range   PRIMARY     PRIMARY     4   NULL    3   100.00  Using where
1   SIMPLE  ranges  ALL     poly    NULL    NULL    NULL    6467418     100.00   
Run Code Online (Sandbox Code Playgroud)

您可以在没有点表的情况下简化测试:SELECT*FROM range其中mbrcontains(poly,GEOMFROMWKB(POINT(0,0)))

id  select_type     table   type    possible_keys   key     key_len     ref     rows    filtered    Extra
1   SIMPLE  ranges  range   poly    poly    34  NULL    1   100.00  Using where
Run Code Online (Sandbox Code Playgroud)

现在这个; SELECT*FROM范围,其中mbrcontains(poly,GEOMFROMWKB(POINT(0,0)))或mbrcontains(poly,GEOMFROMWKB(POINT(10,10)))

结果

id  select_type     table   type    possible_keys   key     key_len     ref     rows    filtered    Extra
1   SIMPLE  ranges  ALL     poly    NULL    NULL    NULL    6467418     100.00  Using where
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,您不是使用索引而只是扫描.

您可以通过为每个特定点创建UNION来强制查询使用索引,但我不确定这是否会更快.我在本地做了一些测试,它比你的第一个查询慢一点.

EXPLAIN EXTENDED 
SELECT *
FROM points
FORCE INDEX (PRIMARY )
LEFT JOIN ranges
FORCE INDEX ( poly ) ON mbrcontains( poly, point )
WHERE points.id = 1
UNION DISTINCT
SELECT *
FROM points
FORCE INDEX (PRIMARY )
LEFT JOIN ranges
FORCE INDEX ( poly ) ON mbrcontains( poly, point )
WHERE points.id = 2
UNION DISTINCT
SELECT *
FROM points
FORCE INDEX (PRIMARY )
LEFT JOIN ranges
FORCE INDEX ( poly ) ON mbrcontains( poly, point )
WHERE points.id = 3
Run Code Online (Sandbox Code Playgroud)

结果

id  select_type     table   type    possible_keys   key     key_len     ref     rows    filtered    Extra
1   PRIMARY     points  const   PRIMARY     PRIMARY     4   const   1   100.00   
1   PRIMARY     ranges  range   poly    poly    34  NULL    1   100.00  Using where
2   UNION   points  const   PRIMARY     PRIMARY     4   const   1   100.00   
2   UNION   ranges  range   poly    poly    34  NULL    1   100.00  Using where
3   UNION   points  const   PRIMARY     PRIMARY     4   const   1   100.00   
3   UNION   ranges  range   poly    poly    34  NULL    1   100.00  Using where
NULL    UNION RESULT    <union1,2,3>    ALL     NULL    NULL    NULL    NULL    NULL    NULL     
Run Code Online (Sandbox Code Playgroud)