2015-04-19 68 views
1

我使用的是CodeIgniter 2,在我的數據庫模型中,我有一個查詢連接兩個表並根據距給定地理位置的距離過濾行。切割MySQL中的SELECT查詢時間

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列一起編入索引。 last_seen表中的latlon列也被編入索引。

有沒有人有任何想法,爲什麼這個查詢需要這麼長時間,我可以改善它?

UPDATE

事實證明,這個查詢實際運行很快。當我在PHPMyAdmin中執行這個查詢時,它需要0.56秒。不錯。但是,當我嘗試使用第三方SQL客戶端(如SequelPro)執行此查詢時,它至少需要20秒,並且我的Mac上的所有其他應用程序都會變慢。當通過jQuery的load()方法加載腳本來執行查詢時,需要大約相同的時間。

在Google Chrome開發人員工具中查看我的網絡標籤後,似乎加載時間很長的原因是因爲所謂的TTFB或Time To First Byte。這是永恆的。

Developer tools screen shot

+0

添加'explain select ...'的輸出 –

+0

lat和long的索引不會幫助,因爲您在過濾之前對它們進行了計算。 – Barmar

+0

是否對'last_seen.seen_id'建立了索引? –

回答

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)。不幸的是,距離計算需要一段時間,因爲它們必須針對每一行進行計算。你可能想要考慮一個GIS擴展到MySQL來幫助這種類型的查詢。

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