我正在运行一个MySQL查询,它根据位置返回结果。但是我最近注意到它真的放慢了我的PHP应用程序。我使用CodeIgniter,探查器显示查询需要4.2秒。 geoname表有500,000行。我在关键列上有一些索引,还有什么可以加快这个查询?
这是我的SQL:
SELECT `products`.`product_name`
, `geoname`.`geonameid`
, `geoname`.`latitude`
, `geoname`.`longitude`
, `products`.`product_id`
, AVG(ratings.vote) as rating
, count(comments.comment_id) as total_comments
, (6371 * acos(cos(radians(38.7666667))
* cos(radians(geoname.latitude))
* cos(radians(geoname.longitude) - radians(-3.3833333))
+ sin(radians(38.7666667))
* sin(radians(geoname.latitude)))
) AS distance
FROM (`foods`)
JOIN `geoname` ON `geoname`.`geonameid` = `products`.`geoname_id`
LEFT JOIN `ratings`
ON `ratings`.`var_id` = `products`.`product_id`
LEFT JOIN `comments`
ON `comments`.`var_id` = `products `.`product_id`
WHERE `products`.`product_id` != 82
GROUP BY `products`.`product_id`
HAVING `distance` < 99
ORDER BY `distance`
LIMIT 10
答案 0 :(得分:3)
让我们从查询本身开始 cos(radians(geoname.latitude))和其他函数看起来像一个不变量,所以我们可以做一些预处理并将计算出的值存储在表中。 (计算触发函数主要涉及使用昂贵的系列扩展)。
6371 * acos(cos(弧度(38.7666667)) - 这等于弧度(38.76667)* 6371所以我们为什么呢?它的成本。
其次,如果你不太关心精度你可以预先计算弧度本身,让我们说从0到pi / 2的10000点 - 这应该给出一个很好的近似值,最多4个十进制数,例如小于1 km < / p>
(6371 * acos(cos(radians(38.7666667))
* cos(radians(geoname.latitude))
* cos(radians(geoname.longitude) - radians(-3.3833333))
+ sin(radians(38.7666667))
* sin(radians(geoname.latitude))))
还记得sin(a)当a&gt; pi / 2和a&lt; pi等于罪(pi - a) 当a> pi和a&lt; 3/2 pi等于-sin(a-pi)并且当a> 1时3/2 pi和a&lt; 2pi它等于-sin(2pi-a)。可以为cos函数制作类似的函数。
试试这个,看看是否有帮助。 路加
答案 1 :(得分:0)
如果你问MySQL解析计划,我想你会发现距离计算会使你的索引变得无用。您正在强制查询引擎执行TABLE SCAN。
保存情况的唯一方法是将距离放在单独的列中并将其编入索引。
答案 2 :(得分:0)
如果您可以将任何搜索位置近似,例如1000个空间中的10000点,您实际上可以按照以下方式在辅助表中存储距离:
create table distance (
position1_id int,
position2_id int,
distance int -- probably precise enough
)
带有position1_id和距离的索引。该表可以有10 ^ 6到10 ^ 8行,但是使用索引数据,我想你可以快速检索最近的position2_id。即使这对你来说不够精确(因为必须达到有限的分辨率),它也可以让你快速消除在特定情况下你不关心的大约99%的位置。
答案 3 :(得分:0)
你可以通过简单地除以57.29577951来激活radians()函数。这将消除每行六次数学计算。对于大型集合上的关系查询连接,该公式通常不友好。尽管如此,这是一个不同的查询,试图在加入之前缩小视图。我不确定它是否会在没有测试和调整的情况下运行得更快或更慢。最终,我决定在主键上构建一个统计表,并在其他表上配置触发器来维护它,这样你的最终距离计算查询就会立即针对一个非常小的表运行。为了真正令人敬畏,我将建立一个类似于统计表的审计表来总结趋势。
select p.product_name,
g.geonameid,
g.latitude,
g.longitude,
p.product_id,
avg(r.votes) as rating,
c.total_comments,
g.distance
(select product_id, geoname_id, product_name from products where product_id != 82) p
inner join
(select geonameid, latitude, longitude, (6371 * acos(cos(38.7666667/57.29577951)
* cos(latitude/57.29577951)
* cos((longitude/57.29577951) - (-3.3833333/57.29577951))
+ sin(38.7666667/57.29577951)
* sin(latitude/57.29577951))
) AS distance
from geoname group by geonameid having distance < 99) g on p.geoname_id = g.geonameid
left join
(select var_id, count(vote) votes from ratings group by var_id) r on p.product_id = r.var_id
left join
(select var_id, count(comment_id) total_comments from comments group by var_id) c on p.product_id = c.var_id
group by p.product_id
order by g.distance
limit 10