我有一个用于简单反向地理编码的数据库。数据库依赖于包含纬度,经度和地名的表格。每当经纬度不存在时,或者更好,每当搜索到的纬度,经度与现有纬度,经度相差太大时,我使用GoogleMaps反向地理编码服务添加新行。 在代码下面生成地址表:
CREATE TABLE `data_addresses` (
`ID` int(11) NOT NULL COMMENT 'Primary Key',
`LAT` int(11) NOT NULL COMMENT 'Latitude x 10000',
`LNG` int(11) NOT NULL COMMENT 'Longitude x 10000',
`ADDRESS` varchar(128) NOT NULL COMMENT 'Reverse Geocoded Street Address'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `data_addresses`
ADD PRIMARY KEY (`ID`),
ADD UNIQUE KEY `IDX_ADDRESS_UNIQUE_LATLNG` (`LAT`,`LNG`),
ADD KEY `IDX_ADDRESS_LAT` (`LAT`),
ADD KEY `IDX_ADDRESS_LNG` (`LNG`);
ALTER TABLE `data_addresses`
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key';
正如您所看到的,诀窍是在纬度和经度上使用两个索引。由于通常纬度和经度是浮点数,我们使用它们的值乘以10000,因此每对夫妇的纬度/经度都是唯一的。这意味着大约50米的分辨率满足了我的需求。
现在问题是:每当我需要知道给定的纬度/经度(MyLat,MyLon)是否已经存在时,我执行以下查询:
SELECT `id`, ROUND(SQRT(POW(ABS(`LAT`-ROUND(MyLat*10000)),2)+POW(ABS(`LNG`-ROUND(MyLon*10000)),2))) AS R FROM splc_smarttrk.`data_addresses` ORDER BY R ASC LIMIT 1
此查询将返回给我最近的点并且还会给我R(评级):较小的R表示最接近的近似值,所以说每当我发现R大于10时我需要添加一个新行地址表。 目前地址表包含大约615k行。
问题在于,尽管我放置了索引,但此查询太慢(在2x Xeon服务器上大约需要2秒)。在解释结果下面:
答案 0 :(得分:2)
您无法通过查找附近纬度和经度的固定数据集并计算评级(R)并在此固定数据集上选择最小评级来优化此项。
未测试的p.s可能包含排序错误。但它可能会帮助你。
SELECT
id
, ROUND(SQRT(POW(ABS(`LAT`-ROUND([LAT]*10000)),2)+POW(ABS(`LNG`- ROUND([LNG]*10000)),2))) AS R
FROM (
SELECT
LAT
FROM
data_addresses
WHERE
LAT <= [LAT]
ORDER BY
LAT DESC
LIMIT 100
UNION ALL
SELECT
LAT
FROM
data_addresses
WHERE
LAT >= [LAT]
ORDER BY
LAT ASC
LIMIT 100
SELECT
LNG
FROM
data_addresses
WHERE
LNG <= [LNG]
ORDER BY
LNG DESC
LIMIT 100
UNION ALL
SELECT
LNG
FROM
data_addresses
WHERE
LNG >= [LNG]
ORDER BY
LNG ASC
LIMIT 100
)
AS data_addresses_range
ORDER BY
R ASC
LIMIT 1
答案 1 :(得分:1)
不是计算距离(或除了),而是提供一个&#34;边界框&#34;。这会快得多。
这里复杂的代码还是更快:mysql.rjweb.org/doc.php/latlng
获得UNIQUE KEY IDX_ADDRESS_UNIQUE_LATLNG (LAT, LNG)
后,无需KEY IDX_ADDRESS_LAT (LAT)
* 10000可以放在MEDIUMINT
中。它大约16米或52英尺。
答案 2 :(得分:0)
根据Raymond Nijland的建议,我将查询修改如下:
SELECT `id` AS ID,
ROUND(SQRT(POW(ABS(`LAT`-ROUND(NLat*10000)), 2) +
POW(ABS(`LNG`-ROUND(NLon*10000)), 2))
) AS RT INTO ADDR_ID, RATING
FROM splc_smarttrk.`data_addresses`
WHERE (`LAT` BETWEEN (ROUND(NLat*10000)-R) AND (ROUND(NLat*10000)+R))
AND (`LNG` BETWEEN (ROUND(NLon*10000)-R) AND (ROUND(NLon*10000)+R))
ORDER BY RT ASC
LIMIT 1;
这个技巧在最坏的情况下将数据集减少到10条记录,因此尽管有ORDER BY子句,速度仍然很好。事实上,我并不需要知道距离现有点的距离,我只需要知道该距离是否高于给定限制(此处如果在10x10矩形内,这意味着R = 5)。