2011-05-08 83 views
4

我有一箇舊版innodb表,其中包含一個包含緯度/經度的業務。給定一個輸入緯度/經度(低於51.2167/4.41667),查詢將按照鄰近度(公里)的順序返回第一個啓用,啓用,未刪除的30個業務。與賬戶表的連接被用於檢查列表的有效性。使用經度/緯度索引mysql表進行地理查找

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                             | 
+----+-------------+---------+-------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+---------+------------------------+------+--------------------------------------------------------------------------------------------------------------------------------+ 

任何幫助,非常感謝。

回答

6

這需要很長時間,因爲您的查詢正在計算您的50k行表中每行的所有超越函數一次的大圓距離公式(兩次包含排序)。

您可以限制搜索的距離範圍嗎?您可能已經注意到,大多數商店搜索器網絡應用都有一個下拉菜單項,提供「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子句。但根據我的經驗,只要在搜索範圍內進行搜索,就可以獲得出色的結果。

+0

嗨Ollie,謝謝你的明確解釋。但是,到目前爲止,我沒有太多的運氣通過縮小搜索範圍來加快查詢速度,即使是在緯度上的索引。我會再試驗一下。 – javacoder 2011-05-09 22:47:47

+0

加快速度的是改變select:SELECT id,latitude,longitude而不是SELECT列表*,然後在列表上執行查詢以獲取其餘數據。 – javacoder 2011-05-09 22:48:51

+1

我想知道是否值得創建一個單獨的MyISAM表來存儲列表ID和用於搜索目的的GEOMETRY。有沒有人有這些空間索引速度的經驗? – javacoder 2011-05-09 22:54:42

1

你可能想看看this question。不幸的是,您不能在任何不是GEOMETRY類型的列上創建SPATIAL索引,因此至少您需要在表中添加一列。

+0

這是否也適用於InnoDB表? – javacoder 2011-05-08 21:47:01

+0

@javacoder - 不,我相信只有MyISAM支持 – jisaacstone 2011-05-08 22:16:58