Mysql 具有适当缩放级别的附近位置

Mysql 具有适当缩放级别的附近位置,mysql,google-maps,geolocation,Mysql,Google Maps,Geolocation,我有一个包含13000个位置的mysql数据库。使用html geolocation,我通过定义半径(例如1 km)来查找用户的位置,我计算一个边界框,并使用该边界框查找该半径内的(sql)位置。输入:地理定位用户,输出:(已排序)半径1公里内的位置数组 如果在该半径内没有位置,则此操作无效。我想要的是显示附近大约10个位置,不管半径如何。这意味着谷歌地图的缩放级别应该是动态的,sql搜索也应该有所不同。输入:地理定位用户,输出:附近有10个位置,没有预定义的半径,位置应该在地图上或多或少可见-

我有一个包含13000个位置的mysql数据库。使用html geolocation,我通过定义半径(例如1 km)来查找用户的位置,我计算一个边界框,并使用该边界框查找该半径内的(sql)位置。输入:地理定位用户,输出:(已排序)半径1公里内的位置数组

如果在该半径内没有位置,则此操作无效。我想要的是显示附近大约10个位置,不管半径如何。这意味着谷歌地图的缩放级别应该是动态的,sql搜索也应该有所不同。输入:地理定位用户,输出:附近有10个位置,没有预定义的半径,位置应该在地图上或多或少可见->适当的地图缩放级别

我想从1公里开始,如果在该范围内没有位置,则增加+100米的半径,并保持循环,直到找到+/-10个位置(排序并将其放入阵列中)。然后找到距离用户位置和数组中最后一个位置的距离(最大距离),并从那里计算相应的谷歌地图缩放级别

我有一个问题:如果最近的位置在20公里以内怎么办?在每个回路中以100 m的增量循环计算200次!恐怕这会导致长时间的等待

我应该如何解决这个问题?有没有其他方法可以到达附近的地点

这是我的mysql表结构(其中formid表示一个位置的ID),总共189.031行

编辑:我尝试了奥利·琼斯的答案,这是我使用的脚本(我将计数器除以2,因为我有每个地址的语言副本):


注意在OQ发布了一些代码后,我发布了这个问题的另一个答案。请看。这比这个答案更切题

你的基本想法似乎还可以:也就是说,如果你在第一次尝试中没有得到足够的分数,就增加搜索半径

您建议每次尝试时将搜索半径增加100米。这似乎是一个非常非侵略性的搜索范围扩大策略

相反,为什么每次不将半径增加当前半径的41.4%(半径*sqrt(2))?这样,每次迭代搜索的地理区域将增加一倍。您的查询已经返回了最接近的10个点,因此即使您在一次迭代中突然获得了1000个点,也不会返回疯狂的结果

请注意,如果您的13000个点是邮政编码/邮政编码质心,则1km不是此搜索的很好起点。除了密集的城市地区,在任何特定的1公里半径范围内都不可能找到10个。你可能想从更大的开始

编辑感谢您更新问题,以包含有关表结构和查询的信息。这很有帮助

您提出了一个困难的优化问题。纬度和经度值存储在属性表中。它们可能存储为文本“-45.12345”,而不是浮点值。优化这些地理查询需要至少对纬度值使用顺序可扫描索引。也就是说,您需要能够在SQL中这样说

SELECT whatever FROM sometable 
 WHERE attribute = 'LAT'
   AND content BETWEEN ?lat-radius AND ?lat+radius
?lat
是候选点的纬度。)服务器需要能够通过随机访问从
?-radius
开始的索引,然后依次扫描到
?+radius
来满足该请求。服务器不能这样做:您的查询包含一个从文本到浮点的隐式类型转换,如下所示。这种类型转换不利于索引

SELECT whatever FROM sometable
 WHERE attribute = 'LAT'
   AND CAST(content AS FLOAT) BETWEEN ?lat-radius AND ?lat+radius
因此,除非您更改模式,使LAT和LONG可以是带有索引的浮点值,否则此查询速度将很慢,与搜索的
radius
无关。一个小的
半径
不会有帮助,而一个大的
半径
不会有伤害

因此:提高性能的最简单方法是执行一次查询。我第一次建议的半径的迭代扩展对于您的表结构是毫无意义的。使用大半径(50km),并使用“按距离ASC限制n排序”(ORDER BY dist ASC LIMIT n)获取距离候选点最近的点

这个问题还有另一类解决方案。它涉及使用某种触发器或其他更新方法创建一个阴影lat/long表。但这是一个很大的工作,你可能不适合它


仅供参考,这里有一篇关于解决地理定位问题的文章

一个解决方案是在半径为20公里的范围内没有位置时不显示位置。另一个解决方案是计算voronoi图。然后,您可以使用图表筛选附近的城市。您可以在这里阅读一个示例:。

注意:此答案是在将架构和代码添加到问题之前给出的。我会留下它,因为它可能会在将来帮助别人

下面的SQL查询用于计算坐标和表中坐标之间的距离。它将结果限制为10,并按距离排序

d=acos(sin(lat1).sin(lat2)+cos(lat1).cos(lat2).cos(lng2-lng1)).R

其中,
$center\u lat
$center\u lng
是位置坐标

查询使用


对于50068行的数据库,查询花费了0.2506秒

我喜欢这样的想法,即不只是随机增加100米,而是将搜索区域扩大一倍。关于你所说的1公里航程:确实是这个问题。这些地点主要是市区的商店(1公里的地图上可能有30个地点?),但如果你住在一个小村庄,你几乎看不到任何地点,因为谷歌地图缩放级别不合适。我刚刚尝试过这种方法,但这可能有点太“激进”。例如,当迭代前11个循环时,它会找到0个命中(6.5公里),而在第12个循环中,它会找到148个位置(9.05公里)。总执行时间为9秒!我把半径改为50公里,结果是:0.77秒!如果我从0,2km开始,以*sqrt(2)的增量迭代
SELECT whatever FROM sometable
 WHERE attribute = 'LAT'
   AND CAST(content AS FLOAT) BETWEEN ?lat-radius AND ?lat+radius
SELECT  name, lat, lng, ( 3959 * acos( cos( radians($center_lat) ) 
                        * cos( radians( lat ) ) * cos( radians( lng )
                        - radians($center_lng) ) + sin( radians($center_lat) ) 
                        * sin( radians( lat ) ) ) ) AS distance FROM table 
                        ORDER BY distance LIMIT 0 , 10