我有以下MySQL查询:
SELECT
a.*,
( 3959 * acos( cos( radians('47.3909') ) * cos( radians( a.lat ) ) * cos( radians( a.lng ) - radians('-122.2637') ) + sin( radians('47.3909') ) * sin( radians( a.lat ) ) ) ) AS distance
FROM zip_codes AS a
ORDER BY distance ASC
LIMIT 1;
这会让我的zip_codes
表中的邮政编码最接近我指定的坐标。
然而,这运行得很慢!大约1秒钟。所有类似的查询也会持续1秒左右。我想知道我是否可以优化我的表结构或查询以改善查询时间。
这是我的zip_codes
表的架构:
CREATE TABLE `zip_codes` (
`zip` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
`city` varchar(64) CHARACTER SET utf8 DEFAULT NULL,
`state` char(2) CHARACTER SET utf8 DEFAULT NULL,
`type` char(1) CHARACTER SET utf8 DEFAULT NULL,
`timezone` int(11) DEFAULT NULL,
`lat` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`lng` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`country` varchar(2) COLLATE utf8_unicode_ci DEFAULT '',
PRIMARY KEY (`zip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT;
更新我将lat
和lng
的字段类型更改为DECIMAL,现在查询实际上需要更长时间,令人惊讶!
答案 0 :(得分:1)
它不适用于美国的所有观点。例如,如果您在阿拉斯加某处选择一个点,距离表格中的每个邮政编码中心不超过50公里,则不会返回任何内容
它需要MyISAM存储引擎
in包含硬编码值(请参阅第1点~50 km)。它不完全是50公里,与经度不同。
<强>先决条件:强>
鉴于您发送的转储,您应该启动以下查询:
ALTER TABLE `zip_codes` ENGINE=MYISAM; -- changing your storage engine to MyISAM. It supports spatial indexes in MySQL
ALTER TABLE `zip_codes` ADD `pt` POINT NOT NULL; -- adding POINT() spatial datatype for zip cetner. Eventually, you may remove the old lat/lng decimal columns
ALTER TABLE `zip_codes` ADD `region` POLYGON NOT NULL; -- adding a rectangle over the center of the zip code. See below, this is something to utilize spatial index later in ST_Intersects function
// update the new columns with respective values
UPDATE `zip_codes` SET `pt` = POINT(lat,lng);
UPDATE `zip_codes` SET `region` = GEOMFROMTEXT(CONCAT('POLYGON((',lat-0.5,' ',lng-0.5,', ',lat+0.5,' ',lng-0.5,', ',lat+0.5,' ',lng+0.5,', ',lat-0.5,' ',lng+0.5,', ',lat-0.5,' ',lng-0.5,'))')); -- 0.5 is 0.5 degrees hardcode. There is a better approach and it's better to write a MySQL function that will increase the MBR with certain step until there is intersection (see my point #1 above, this is the best solution)
// create indexes on the newly created columns
ALTER TABLE `zip_codes` ADD SPATIAL INDEX(`region`);
ALTER TABLE `zip_codes` ADD SPATIAL INDEX(`pt`);
新查询
SELECT SQL_NO_CACHE zip,ST_Distance(`pt`,POINT('47.3909','-122.2637')) AS dst
FROM `zip_codes`
WHERE ST_Intersects(POINT('47.3909','-122.2637'),`region`)
ORDER BY `dst`
LIMIT 1;
在我的机器上需要大约0.011秒,这要好得多。
但是再次看到上面的评论在更新声明附近,你应该考虑两件事: