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中如何根据经纬度优化这个距离查询的主要内容,如果未能解决你的问题,请参考以下文章

如何通过经纬度计算距离来优化 SQL 查询?

mysql中怎么根据经纬度计算距离

Java,Mysql-根据一个给定经纬度的点,进行附近500米地点查询–合理利用算法

mysql使用sql语句根据经纬度计算距离排序

优化表格以搜索地理位置

如何根据用户位置查询 mySQL [关闭]