2014-09-19 109 views
1

我有以下SQL。这需要大约95秒的时间来执行。表中有大约2500万条记录。MySQL使用组查询性能问题

SET @lat=(select latitude from skoovy_prd.pins where user_id=0 and board_id=0 limit 1); 
SET @lng=(select longitude from skoovy_prd.pins where user_id=0 and board_id=0 limit 1); 
SELECT category_id, MAX(pin_id), pin_id 
FROM skoovy_prd.pins 
WHERE (3959 * acos(cos(radians(@lat)) * cos(radians(latitude)) 
* cos(radians(longitude) - radians(@lng)) + sin(radians(@lat)) * sin(radians(latitude)))) <=25 
GROUP BY category_id DESC 
LIMIT 12; 

category_id,纬度,经度,pin_id都是BTREE索引。

有没有更有效的方法来写这个,所以我可以更快地获得记录?这样做的目的是让我获得一组记录,其中每条记录都是一个独特的类别。我发布这个问题后得到的SQL这里:这mysql selecting records but ensuring data in one column is distinct它被标记为的Retrieving the last record in each group

重复有通过newtlover在回答这使我我已经写在这里发布的SQL列表提供了解决方案。 (尽管我并不是真的在寻找每组中的最后一条记录,但至少可以获得记录集中category_id不同的记录。

我希望有一种方法可以提高此查询的性能。如果有人有任何建议来解决每组中的最后一个记录,那也是值得赞赏的。我不是一个SQL人,所以我在这里抓住吸管

+0

你确定这应该是'GROUP BY category_id DESC'? – andy 2014-09-19 22:22:11

+0

有或没​​有DESC是相同的性能明智的,并产生可接受的正确结果 – kambythet 2014-09-19 22:50:13

+0

当您不使用分组使用'SELECT DISTINCT category_id,pin_id'时,您可能会得到更快的结果。我不确定是否提供了您期望的结果,但查询可以在12次访问后完成,不像'GROUP BY'版本,所有带有找到的category_id的记录都需要考虑'MAX(pin_id)'。 – andy 2014-09-19 23:01:20

回答

2

你可以'如果您在表达式中深度引用了索引列,那么期望SQL表达式可以使用索引,这会破坏索引的使用,因为优化器无法知道表达式的结果是否与顺序具有相同的排序顺序的指数。

距离公式尤其难以用B树进行优化,因为B树主要沿着一个轴进行排序。

问题是您的WHERE子句必须评估所有2500万行上的昂贵的trig函数,而不是使用索引来减少结果集。

一个解决方案是使用边界框来减少搜索范围。也就是说,如果您知道@lat,那么您可以使用WHERE latitude BETWEEN @lat-25 AND @lat+25 AND ...trig expression...因为AND只在左操作数为真时才计算右操作数,所以这将有助于更有效地减少可能的匹配。

不幸的是,即使您使用复合索引,但不能同时使用单个B树查​​找同时对经度和纬度进行过滤。考虑一下:我要求你在电话簿中查找姓名,姓氏以“S”开头的任何人的名字都以“J”开头。电话簿就像姓氏名字上的索引,但姓氏不会排序在一起。您最终不得不搜索所有“S”姓氏,就好像您只有该列索引一样。

还有其他的技术,除了B树,这使得这些类型的多维搜索更容易。一个是狮身人面像搜索。请参阅An introduction to distance-based searching in Sphinx

另一种方法是使用MySQL 5.6的一些内置功能,但只有在MyISAM中存储数据(即I usually recommend against using)时才会对其进行索引。在MySQL地理空间搜索

见亚历山大·鲁宾的优秀资源:

+0

非常感谢你,比尔。我看过你的其他帖子,而且你总是乐于助人。我们正在使用狮身人面像,但由于Sphinx的最大记录限制,我无法利用它。但是其他的一切,我会花一些时间阅读,我可能会看看空间函数(我有一个表已经有一个POINT列,其中有相应的纬度/长度坐标值的点值) – kambythet 2014-09-19 23:27:48

+0

我会使用MyISAM表只是为了索引目的而存储数据的*副本,而原始数据安全地存储在事务性存储引擎中 – 2014-09-20 00:02:57

+0

我一定会这么做的,我知道MyISAM在这样的查询上速度更快,所以我要去 – kambythet 2014-09-20 00:04:44

0

数学导致每次全表扫描。如果你有可能存储它的结果,例如。每个cronjob比你应该这样做。 另一种方法是在算术前添加一些其他索引条件以减少检查行的数量。