【问题标题】:How to INDEX this MySQL table for use with GEO data?如何索引这个 MySQL 表以用于 GEO 数据?
【发布时间】:2011-08-19 13:08:39
【问题描述】:

我的桌子目前是这样的:

CREATE TABLE IF NOT EXISTS `x_geodata` (
  `post_id` int(11) NOT NULL,
  `post_type` varchar(20) NOT NULL,
  `lat` float(10,6) NOT NULL,
  `lng` float(10,6) NOT NULL,
  PRIMARY KEY  (`post_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

我如何正确有效地索引这个表?

在这张表上运行的唯一查询如下:

if(!empty($_SESSION['s_property_radius'])) {$dist = $_SESSION['s_property_radius'];}else{$dist = 50;}
$orig_lat = $_SESSION['s_property_address_lat'];
$orig_lon = $_SESSION['s_property_address_lng'];
$lon1 = $orig_lon - $dist / abs( cos( deg2rad( $orig_lat ) ) * 69 );
$lon2 = $orig_lon + $dist / abs( cos( deg2rad( $orig_lat ) ) * 69 );
$lat1 = $orig_lat - ( $dist / 69 );
$lat2 = $orig_lat + ( $dist / 69 );


$sql = "

SELECT `t`.`post_id`, 3956 * 2 * ASIN( SQRT( POWER( SIN( ( ".$orig_lat." - `t`.`lat` ) * pi() / 180 / 2), 2 ) + COS( ".$orig_lat." * pi() / 180) * COS( `t`.`lat` * pi() / 180 ) * POWER( SIN( ( ".$orig_lon." - `t`.`lng` ) * pi() / 180 / 2 ), 2 ) ) ) AS `distance` FROM (
    SELECT `post_id`, `lat`, `lng` FROM `x_geodata` WHERE `post_type` = 'some post type' AND `lng` BETWEEN '".$lon1."' AND '".$lon2."' AND `lat` BETWEEN '".$lat1."' AND '".$lat2."'
) AS `t` HAVING `distance` <= ".$dist."

";

查询检查以确保我们正在查看正确的帖子类型,然后对 lat 和 lng 进行方形半径检查。然后将返回的结果通过圆形半径检查。

我正在寻找的是更新 CREATE TABLE SQL 或 UPDATE TABLE SQL 以正确获取此 INDEXED。

非常感谢任何帮助。

编辑: 根据 arnep 答案对我的查询进行了解释,我得到了这个:

id    select_type    table    type    possible_keys    key    key_len    ref    rows    Extra
1    PRIMARY    <derived2>    ALL    NULL    NULL    NULL    NULL    3   
2    DERIVED    zch_geodatastore    range    post_type    post_type    70    NULL    3    Using where

虽然不知道是什么意思...

【问题讨论】:

  • 您的EXPLAIN 有两部分:一是外部选择,二是内部选择。内部选择通过使用您的 WHERE 子句按预期使用您的新索引,它返回 3 行。外部选择使用这个没有任何索引的“派生”表......但不要担心:因为它正在为每一行计算一些东西,所以不需要索引。有关内部选择的更多信息,您可以在其上执行另一个 EXPLAIN。顺便说一句:你的CREATE TABLE 使用lon,你的SELECT 使用lng ...令人困惑。而且您的内部选择不会返回latlng ...缺少某些东西?
  • 我已经更新了我的代码。表应该是 lng 而不是 lon。我已将latlng 添加到内部查询中。不知道我发布此代码时是如何错过它们的。

标签: mysql geolocation latitude-longitude


【解决方案1】:

MySQL 为 MySQL 提供了特殊的 SPATIAL 键。
见:http://dev.mysql.com/tech-resources/articles/4.1/gis-with-mysql.html

引用自:http://dev.mysql.com/doc/refman/5.1/en/create-index.html

MyISAM、InnoDB、NDB 和 ARCHIVE 存储引擎支持空间列,例如(POINT 和 GEOMETRY。(第 11.17 节,“空间扩展”,描述空间数据类型。)

空间键是:
•仅适用于 MyISAM 表。为其他存储引擎指定 SPATIAL INDEX 会导致错误。
•索引列不能为空。
• 在 MySQL 5.1 中,列前缀长度是被禁止的。每列的全宽都被索引。

您知道吗,这是一个专门针对此类问题的 https://gis.stackexchange.com/ 论坛,
我建议您(双重)在那里发布您的问题。

【讨论】:

  • 关于在 GIS 上发帖,您可以对dba.stackexchange.com 说同样的话。毕竟这是一个数据库问题。但是因为这是三者中的一部分,所以我认为在这里发布更合适。但是,如果管理员觉得有必要移动这篇文章,请这样做。
  • 我知道使用 SPATIAL INDEX,但是对于我需要这张表的简单要求,这不是有点 OTT 吗?另外,我只会使用 POINT。那么空间数据类型的开销可能会因所有从文本表示到文本表示的转换而过多?
【解决方案2】:

由于您的内部SELECTWHERE 子句中使用post_typelatlon,我建议对它们进行索引。

使用EXPLAIN [QUERY]查看是否使用了索引以及从中获得什么好处。

【讨论】:

  • 如何在这些上添加索引?像这样:KEY post_type (post_type,lat,lng)?
  • ALTER TABLE x_geodata ADD INDEX new_index (post_type,lat,lng); 我建议使用 MySQL Query Browser 或 PHPMyAdmin 之类的工具来创建索引等,并查看创建的 SQL 输出。或阅读 mysql.com :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-12
  • 2017-12-04
  • 1970-01-01
  • 2012-03-18
相关资源
最近更新 更多