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

jav*_*der 5 mysql database indexing geospatial latitude-longitude

我有一个遗留的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;
Run Code Online (Sandbox Code Playgroud)

表列表和帐户每个包含超过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                                                                                                                    |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

任何帮助是极大的赞赏.

O. *_*nes 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)
Run Code Online (Sandbox Code Playgroud)

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

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

您还可以在经度上包含BETWEEN子句.但根据我的经验,只需在BETWEEN搜索范围内进行搜索,即可获得出色的效果.