我有一个遗留的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 |
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+
非常感谢任何帮助。
答案 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
索引,因此至少需要在表格中添加一列。