在MySQL中切割SELECT查询时间

时间:2015-04-19 12:34:44

标签: mysql sql query-optimization

我正在使用CodeIgniter 2,在我的数据库模型中,我有一个查询,它根据与给定地理位置的距离连接两个表并过滤行。

SELECT users.id,
       (3959 * acos(cos(radians(42.327612)) *
          cos(radians(last_seen.lat)) * cos(radians(last_seen.lon) -
          radians(-77.661591)) + sin(radians(42.327612)) * 
          sin(radians(last_seen.lat)))) AS distance 
FROM users 
JOIN last_seen ON users.id = last_seen.seen_id 
WHERE users.age >= 18 AND users.age <= 30 
HAVING distance < 50

我不确定distance是否会使此查询花费的时间特别长。我的users table中确实有超过300,000行。我last_seen表中的金额相同。我确信它会发挥作用。

但是,users表中的age列与id列一起编入索引。 lat表格中的lonlast_seen列也会被编入索引。

有没有人知道为什么这个查询需要这么长时间以及如何改进它?

更新

事实证明,这个查询实际上运行得非常快。当我在PHPMyAdmin中执行此查询时,需要0.56秒。还不错。但是,当我尝试使用像SequelPro这样的第三方SQL客户端执行此查询时,至少需要20秒,而我的mac上的所有其他应用程序都会变慢。当通过jQuery的load()方法加载脚本来执行查询时,它需要大约相同的时间。

在Google Chrome的开发者工具中查看我的网络标签后,加载这么长时间的原因似乎是因为所谓的TTFB或时间到第一个字节。这是永远的。

Developer tools screen shot

2 个答案:

答案 0 :(得分:1)

为了使此查询更快,您需要在实际计算每个和每个上的距离之前使用索引来限制行数。为此,您可以根据纬度/经度和所需距离的粗略公式限制last_seen的行。

这个想法是,如果经度与参考经度相距一定距离,那么纬度与参考纬度相同的位置将在50英里距离内,反之亦然。 对于50英里的距离,RefLat + -1和RefLon + -1将是在实际计算精确距离之前限制行的良好开端。

last_seen.lat     BETWEEN 42.327612  - 1 AND  42.327612 + 1
AND last_seen.lon BETWEEN -77.661591 - 1 AND -77.661591 + 1

答案 1 :(得分:0)

对于此查询:

SELECT users.id, (3959 * acos(cos(radians(42.327612)) * cos(radians(last_seen.lat)) * cos(radians(last_seen.lon) - radians(-77.661591)) + sin(radians(42.327612)) * sin(radians(last_seen.lat)))) AS distance 
FROM users JOIN
     last_seen
     ON users.id = last_seen.seen_id 
WHERE users.age >= 18 AND users.age <= 30 
HAVING distance < 50;

最佳指数为users(age, id)last_seen(seen_id)。不幸的是,距离计算需要一段时间,因为必须为每一行计算距离。您可能需要考虑使用MySQL的GIS扩展来帮助进行此类查询。