我正在尝试对包含大约100万条记录的表运行查询。 表及其索引的结构是:
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
我做错了什么? 我需要添加哪个索引才能使查询使用它而不是表扫描? 我是否需要更改表格结构?
答案 0 :(得分:4)
您正在使用WHERE子句中的函数,因此它将始终导致表扫描。数据库无法对函数的结果进行索引。我认为你最好的选择是在尝试评估距离算法之前想出一些限制结果的方法。
例如,对于给定的位置,您可以知道可以落在设定距离内的最小和最大可能纬度,因此请先按此过滤。纬度为~69英里,因此如果您的搜索半径为50英里,那么任何超过0.725度的纬度都不可能在距离您所在位置50英里的范围内。由于这只是一个数字比较WHERE m.latitude > (:latitude - 0.725) AND m.latitude < (:latitude + 0.725)
,而不是对函数的调用,因此数据库将能够使用您的索引来评估它。
经度更复杂,因为每个度数的距离取决于该位置的北/南有多远,但是根据您想要投入多少工作量,您也可以对经度做同样的事情。