mysql中如何根据经纬度优化这个距离查询
Posted
技术标签:
【中文标题】mysql中如何根据经纬度优化这个距离查询【英文标题】:How optimize this distance query based on latitude and longitude in mysql 【发布时间】:2021-07-16 23:52:47 【问题描述】:我有一个带有纬度和经度值的英国邮政编码数据库。我有一个查询,允许我传入一个以英里为单位的数字、lat 和 lng 值,它会找到以英里为单位的半径内的所有行。这可行,但查询很慢。
表中有 1 778 632 行,我为所有列添加了索引,但对执行速度没有任何影响。
mysql 5.7.29 版本正在使用中。
有什么方法可以加快以下逐字查询的执行速度?
SELECT id,postcode, ROUND((
3959 * acos (
cos ( radians(56.007165000) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-3.784005000) )
+ sin ( radians(56.007165000) )
* sin( radians( latitude ) )
)
),1) AS distance
FROM postcodelatlng
HAVING distance <= 10
ORDER BY distance
所以我更新了原始查询以添加这样的绑定框:
SELECT id,postcode, ROUND((
3959 * acos (
cos ( radians(56.007165000) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-3.784005000) )
+ sin ( radians(56.007165000) )
* sin( radians( latitude ) )
)
),1) AS distance
FROM postcodelatlng
WHERE latitude BETWEEN 56.007165000 - 10/69.172
AND 56.007165000 + 10/69.172
AND longitude BETWEEN -3.784005000 - 10/69.172 / COS(RADIANS(56.007165000))
AND -3.784005000 + 10/69.172 / COS(RADIANS(56.007165000))
HAVING distance <= 10
ORDER BY distance
--
-- Table structure for table `postcodelatlng`
--
CREATE TABLE IF NOT EXISTS `postcodelatlng` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`postcode` varchar(8) NOT NULL,
`latitude` decimal(12,9) NOT NULL,
`longitude` decimal(12,9) NOT NULL,
PRIMARY KEY (`id`)
) AUTO_INCREMENT=1;
--
-- Indexes for table `postcodelatlng`
--
ALTER TABLE `postcodelatlng`
ADD PRIMARY KEY (`id`),
ADD KEY `postcode` (`postcode`),
ADD KEY `latitude` (`latitude`,`longitude`),
ADD KEY `longitude` (`longitude`,`latitude`) USING BTREE;
这里是数据示例:
INSERT INTO postcodelatitudelongitude (postcode,latitude,longitude) VALUES ('AB10', '57.13514','-2.11731');
INSERT INTO postcodelatitudelongitude (postcode,latitude,longitude) VALUES ('AB11', '57.13875','-2.09089');
INSERT INTO postcodelatitudelongitude (postcode,latitude,longitude) VALUES ('AB12', '57.10100','-2.11060');
INSERT INTO postcodelatitudelongitude (postcode,latitude,longitude) VALUES ('AB13', '57.10801','-2.23776');
dbfiddle https://www.db-fiddle.com/f/mi4D1937k9WpeM3ubMBLjw/0
【问题讨论】:
关于查询加速的问题很难回答,因为您没有提供查询绑定的数据。这可能是由于许可权的限制,但请理解,除非该场景可重现,否则您可能无法获得问题的适当答案。 数据在这里免费提供:freemaptools.com/download-uk-postcode-lat-lng.htmukpostcodesmysql.zip 我要做的就是优化查询 听起来不错,为什么不创建一个 dbfiddle 来证明您的问题呢?应该可以轻松链接它(至少)。 (如果您允许我发表评论,您想优化我所说的查询已经在问题中突出) dbfiddle 会允许这种大小的数据库吗? 234MB 【参考方案1】:从 5.7 (.9; GA: 2015-10-21) 开始,ST_DISTANCE_SPHERE
function 提供可用距离(也:MariaDB)。
例子:
SELECT
id,postcode,
ST_DISTANCE_SPHERE(POINT(-3.784005000, 56.007165000), POINT(longitude, latitude)) / 1000 AS distance
FROM postcodelatlng
#HAVING distance <= 10
ORDER BY distance
见dbfiddle
或者,切换到 geometry 数据类型并使用a spacial index。
【讨论】:
当我在我的机器上运行它时,我得到: st_distance_sphere 的参数不正确 然后你复制了错误的代码,因为你看到它在 dbfiddle 中工作 我看到它在小提琴中工作,但它不在我的机器上。完全复制粘贴 可能是某些行在数据库中的纬度/经度坐标无效,导致此错误。但我无法通过并修复它们。 mysql Ver 14.14 Distrib 5.7.29,适用于使用 EditLine 包装器的 Linux (x86_64)【参考方案2】:使用 lat/lng 优化“查找最近”的第一步是在 WHERE
子句中包含一个“边界框”。
我在http://mysql.rjweb.org/doc.php/find_nearest_in_mysql 中讨论了 5 个“查找最近”算法您的代码是第一个,即蛮力。如果有 180 万行,其他任何行都将至少快几个数量级。
【讨论】:
我已更新原始问题以添加边界框,如您笔记中第二个示例中概述的那样。我添加了 INDEX(lat, lng) INDEX(lng, lat) 等,它看起来确实更快。我做的对吗? @user794846 - 看起来不错。 (使用 4 行,加速不会明显;使用 1.8M,应该很明显。) 是的,上面的查询需要 0.1249 秒。而不是 4.6 秒!以上是关于mysql中如何根据经纬度优化这个距离查询的主要内容,如果未能解决你的问题,请参考以下文章