使用纬度/经度索引mysql表以进行地理查找

时间:2011-05-08 21:05:58

标签: mysql database geospatial latitude-longitude indexing

我有一个遗留的innodb表列表,其中包含具有纬度/经度的业务。给定输入纬度/经度(低于51.2167 / 4.41667),查询将按接近度(公里)的顺序返回第一个活动的,启用的,未删除的30个业务。与帐户表的连接用于检查列表的有效性。

select 
    listing.*
from
    listing listing ,
    account account 
where
    listing.account_id = account.id 
    and listing.active = 1 
    and listing.approved = 1 
    and listing.deleted = 0 
    and listing.enabled = 1 
    and account.enabled = 1 
    and account.activated_by_user = 1 
group by
    listing.id
having
     111.222569*degrees(acos(sin(radians(listing.latitude))*sin(radians( 51.2167)) +cos(radians(listing.latitude))*cos(radians( 51.2167))*cos(radians(listing.longitude - 4.41667)))) < 250
order by
     111.222569*degrees(acos(sin(radians(listing.latitude))*sin(radians( 51.2167)) +cos(radians(listing.latitude))*cos(radians( 51.2167))*cos(radians(listing.longitude - 4.41667))))
limit 30;

表的列表和帐户每个包含超过50,000行,但查询仍然需要24秒才能运行。没有订单,需要17秒。

我已经尝试在活动,已批准,已删除,已启用时设置一些索引。我可以重写查询或添加某些索引来有效地执行此查询 - 而无需更改表结构吗?

+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
| id | select_type | table   | type        | possible_keys                                                                                   | key                                                             | key_len | ref                    | rows | Extra                                                                                                                          |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
|  1 | SIMPLE      | listing | index_merge | FKB4DC521D9306A80C,listing_active,listing_approved,listing_enabled,listing_deleted,index_test_1 | listing_active,listing_approved,listing_enabled,listing_deleted | 1,1,1,1 | NULL                   | 3392 | Using intersect(listing_active,listing_approved,listing_enabled,listing_deleted); Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | account | eq_ref      | PRIMARY,account_enabled,account_activated_by_user,index_test_2                                  | PRIMARY                                                         | 8       | ctm.listing.account_id |    1 | Using where                                                                                                                    |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:8)

这需要很长时间,因为您的查询正在为50k行表中的每一行计算一个包含所有超越函数的大圆距离公式(当您包括排序时两次)。

您可以限制搜索的距离范围吗?您可能已经注意到,大多数商店查找器Web应用程序都有一个下拉菜单项,可以选择“5英里内”,“10英里内”,等等。

如果您可以这样做,您应该在搜索中添加WHERE子句,并通过在LATITUDE列上添加索引来优化它。假设您使用RANGELIMIT值作为搜索范围限制,以英里为单位。

试试这个条款

WHERE LISTING.LATITUDE BETWEEN LOCATION.LATITUDE - (RANGELIMIT * 1.1508/60) 
                           AND LOCATION.LATITUDE + (RANGELIMIT * 1.1508/60)

这是有效的,因为海里几乎完全等于纬度的一分钟(1/60)。 1.1508因子将海里转换为法定里程。

我建议的条款将使用纬度指数来缩小搜索范围,并且您将不那么频繁地计算大圆距离。

您还可以在经度中包含BETWEEN子句。但根据我的经验,只需在纬度BETWEEN搜索中获得优异的结果。

答案 1 :(得分:1)

您可能需要查看this question。遗憾的是,您无法在任何非SPATIAL类型的列上创建GEOMETRY索引,因此至少需要在表格中添加一列。