MySQL - 从数据库中查找半径范围内的点

10

我有一张表,其中包含一个POINT列,该列包含各个位置的纬度和经度。

然后,我还有来自浏览器地理位置的用户位置。

我需要做的是查找所有POINT值在10公里半径(或X公里半径)内的表记录,并按距离顺序排列,最近的排在前面。

我的表在POINT列上具有SPATIAL索引。


2
你无法使用索引在圆内查找点 - 但是如果您为索引查找定义一个边界框,然后根据距离中心的距离进行过滤,您将获得快速的结果。 - symcbean
1
这里提供的解决方案在处理大型数据集时表现不佳。如果有这个问题,请参见http://mysql.rjweb.org/doc.php/find_nearest_in_mysql - Rick James
3个回答

21

我正在做一个项目,计算多个位置之间的距离。我使用以下查询来选择在给定半径内的object_id。

SELECT id, 
( 6371 * 
    ACOS( 
        COS( RADIANS( db_latitude ) ) * 
        COS( RADIANS( $user_latitude ) ) * 
        COS( RADIANS( $user_longitude ) - 
        RADIANS( db_longitude ) ) + 
        SIN( RADIANS( db_latitude ) ) * 
        SIN( RADIANS( $user_latitude) ) 
    ) 
) 
AS distance FROM the_table HAVING distance <= $the_radius ORDER BY distance ASC"

我无法解释ACOS公式本身,因为我是通过研究获得的。

db_latitude = database latitude field
db_longitude = database longitude field
$user_latitude = browser latitude coördinate
$user_longitude = browser longitude coördinate
$the_radius = the radius that you want to search in

这是以公里为单位。


1
嗨@Bas,感谢您的回复。但是我的Lat/Long存储在MySQL的POINT字段中,而不是两个单独的浮点数。我理解的是这是存储此信息的正确方式,以便我可以使用SPATIAL索引来提高效率。 - tip2tail
1
嗨@tip2tail,很抱歉我忽略了那个细节。我不熟悉“POINT”字段。找到这篇堆栈帖子希望对你有帮助https://dev59.com/6WEi5IYBdhLWcg3wjs1j#21170928 - Bas van Dijk
1
如果您需要英里范围,请将“6371”替换为“3959”。 - Maverick_Java
这难道不需要一个一个地计算数据库中所有现有的点吗?如果你存储了数百万个点,似乎根本行不通? - Evren Yurtesen
1
@Bas van Dijk,你正在为每一行选择纬度和经度,然后计算到用户输入点的“距离”(更不用说,你正在使用昂贵的三角函数)。然后按半径过滤结果。是的,每次运行查询时,您都要遍历表中的每一行。使用EXPLAIN查看使用了多少行。但对于17k行,每次只有几个查询,这将起作用。当您拥有大量行和更多访问权限时,它根本无法扩展。我不会在生产中使用它。 - Evren Yurtesen
显示剩余6条评论

1
以下查询实际上对我有用:

$query = "SELECT *,
    ( 6371 * 
    acos( 
    cos( radians( ".$user_lat." ) ) * 
      cos( radians( lat ) ) * 
      cos( radians( lng ) - 
      radians( ".$user_lng." ) ) + 
        sin( radians( ".$user_lat." ) ) * 
          sin( radians( lat ) ) ) ) 
          AS distance FROM parkings 
          HAVING distance <= ".$radius." ORDER BY distance ASC";

  $stmt = $conn->execute($query);

  $rows = $stmt->fetchAll('assoc');

其中: $user_lat和$user_lng是浏览器的纬度和经度, $radius = 10, 表名为parkings


-1

也许这对你有帮助, https://ru.scribd.com/presentation/2569355/Geo-Distance-Search-with-MySQL

对于Django,我使用这个

    dist = 20 #дистанция 20 км
    mylon = 51.5289156201 # долгота центра
    mylat = 46.0209384922 # широта 
    lon1 = mylon-dist/abs(math.cos(math.radians(mylat))*111.0) # 1 градус широты = 111 км
    lon2 = mylon+dist/abs(math.cos(math.radians(mylat))*111.0)
    lat1 = mylat-(dist/111.0)
    lat2 = mylat+(dist/111.0)
    profiles = UserProfile.objects.filter(lat__range=(lat1, lat2)).filter(lon__range=(lon1, lon2))

它搜索20公里范围内的所有用户。


嗨,@Nickita感谢您的回复。这看起来与上面的答案非常相似,请查看我的评论。 - tip2tail

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接