2011-03-30 76 views
-1

我正在試圖對大約100萬條記錄的表執行查詢。 表與它的指標的結構是:MySQL查詢性能調優

CREATE TABLE `table` (
    `Id` int(11) NOT NULL, 
    `Name` varchar(510) DEFAULT NULL, 
    `Latitude` float NOT NULL DEFAULT '0', 
    `Longitude` float NOT NULL DEFAULT '0', 
    PRIMARY KEY (`Latitude`,`Longitude`,`Id`), 
    KEY `IX_Latitude_Longitude` (`Latitude`,`Longitude`), 
    KEY `IX_Latitude` (`Latitude`), 
    KEY `IX_Longitude` (`Longitude`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

我運行下面的查詢:

SELECT m.Id, m.Name, sqrt(69.1 * (m.Latitude - :latitude) * 69.1 * (m.Latitude - :latitude) + 
        53.0 * (m.longitude - :longitude) * 53.0 * (m.longitude - :longitude)) as Distance, 
m.Latitude as Latitude, m.Longitude as Longitude 
FROM table m 
WHERE sqrt(69.1 * (m.Latitude - :latitude) * 69.1 * (m.Latitude - :latitude) 
     + 53.0 * (m.longitude - :longitude) * 53.0 * (m.longitude - :longitude)) < :radius 
ORDER BY sqrt(69.1 * (m.Latitude - :latitude) * 69.1 * (m.Latitude - :latitude) + 
      53.0 * (m.longitude - :longitude) * 53.0 * (m.longitude - :longitude)) desc 
LIMIT 0, 100 

那想返回特定範圍的所有記錄(距離計算的信息:http://www.meridianworlddata.com/Distance-Calculation.asp

但查詢需要花費大量的時間... 這裏是解釋計劃,我得到:

id|select_type |table|type|possible_keys|key |key_len|ref |rows |Extra 
1 |SIMPLE  |m |ALL |{null}  |{null}|{null} |{null}|1264001|Using where; Using filesort 

我在做什麼錯? 我需要添加哪個索引才能使查詢使用它來代替表掃描? 我是否需要更改表格結構?

+1

@GEOCHET,你爲什麼要添加那些不需要的反引號? – 2011-03-30 21:43:30

+1

@nhnb:我沒有。 – GEOCHET 2011-03-30 21:48:45

+0

你在那張桌子上有足夠的索引嗎?至少有2個是多餘的,所以你可以刪除它們:IX_Latitude_Longitude和IX_Latitude – 2011-03-30 22:03:57

回答

4

您在WHERE子句中使用函數,因此它總是會導致表掃描。數據庫無法根據函數的結果進行索引。我認爲你最好的選擇是在嘗試評估距離算法之前想出一些限制結果的方法。

例如,對於給定的位置,您可以知道可以落在設定距離範圍內的最小和最大可能緯度,因此請首先進行過濾。緯度約爲69英里,所以如果你的搜索半徑是50英里,那麼任何距離緯度超過0.725度的地方都不可能落在你的位置50英里範圍內。由於這只是一個數值比較WHERE m.latitude > (:latitude - 0.725) AND m.latitude < (:latitude + 0.725),不是對函數的調用,數據庫將能夠使用您的索引來評估它。

經度更復雜,因爲每個度數的距離取決於位置發生的北/南位置有多遠,但取決於您想要投入多少工作,您可以按照經度好。

+0

是的,那樣做。還要注意緯度是你的主鍵的第一部分,所以對此的範圍掃描是最好的選擇(給定比InnoDB集羣主鍵) – MarkR 2011-03-30 22:17:49

+0

感謝您的答案。 如果我不需要通過接近度將結果排序到我所經過的位置,它就會有效。現在order by子句正在查詢。我不能僅排序查詢的結果,因爲我想按照它們的接近度返回100行排序...... – Shai 2011-03-31 09:16:42

+0

假設您使用的是支持子查詢的MySQL版本(肯定是5.0+),您可以使用FROM子句中的子查詢。因此,您將在外部查詢中根據距離公式的結果進行排序,並在內部查詢中「預過濾」結果。如果您使用的是不支持子查詢的MySQL版本,那麼我建議使用臨時表並對其進行查詢。 – 2011-03-31 16:28:11