用于计算坐标邻近度的 SQL 查询

SQL query to calculate coordinate proximity

提问人:deceze 提问时间:9/18/2008 最后编辑:Drewdeceze 更新时间:5/18/2021 访问量:6598

问:

我正在使用此公式来计算我的 (My)SQL 数据库中具有十进制格式的纬度和经度字段的条目之间的距离:

6371 * ACOS(SIN(RADIANS( %lat1% )) * SIN(RADIANS( %lat2% )) + 
COS(RADIANS( %lat1% )) * COS(RADIANS( %lat2% )) * COS(RADIANS( %lon2% ) - 
RADIANS( %lon1% )))

适当地替换 %lat1% 和 %lat2% 它可以在 WHERE 子句中用于查找另一个条目的特定半径内的条目,在 ORDER BY 子句中将其与 LIMIT 一起使用将找到最近的 x 个条目等。

我写这篇文章主要是作为我自己的笔记,但改进总是受欢迎的。:)

注意:正如 Valerion 在下面提到的,这是以公里为单位计算的。将 6371 替换为适当的替代数字,以使用米、英里等。

SQL 地理编码

评论


答:

2赞 Adam Hopkinson 9/18/2008 #1

我认为这是 Haversine 公式是对的吗?

评论

0赞 deceze 9/18/2008
我发现它是“大圆距离公式”,但看着它,是的,它很可能是。
1赞 Valerion 9/18/2008 #2

我在车辆跟踪应用程序上使用完全相同的方法,并且已经使用了多年。它工作得很好。快速检查一些旧代码表明,我将结果乘以6378137,如果内存可用,则转换为米,但我已经很久没有碰过它了。

我相信 SQL 2008 有一种新的空间数据类型,我想它允许在不知道这个公式的情况下进行这些类型的比较,并且还允许空间索引,这可能很有趣,但我还没有研究过它。

7赞 David 9/24/2008 #3

对于不支持三角函数的数据库(如 SQLite),可以使用勾股定理。

即使您的数据库支持三角函数,这也是一种更快的方法,但需要注意以下几点:

  • 您需要将坐标存储在 x,y 网格中,而不是(或以及)lat,lng;
  • 计算假设“平坦的地球”,但这对于相对本地的搜索来说很好。

这是我正在做的一个 Rails 项目的例子(中间重要的一点是 SQL):

class User < ActiveRecord::Base
  ...
  # has integer x & y coordinates
  ...

  # Returns array of {:user => <User>, :distance => <distance>}, sorted by distance (in metres).
  # Distance is rounded to nearest integer.
  # point is a Geo::LatLng.
  # radius is in metres.
  # limit specifies the maximum number of records to return (default 100).
  def self.find_within_radius(point, radius, limit = 100)

    sql = <<-SQL
      select id, lat, lng, (#{point.x} - x) * (#{point.x} - x) + (#{point.y} - y) * (#{point.y} - y) d 
      from users where #{(radius ** 2)} >= d 
      order by d limit #{limit}
    SQL
    
    users = User.find_by_sql(sql)
    users.each {|user| user.d = Math.sqrt(user.d.to_f).round}
    return users
  end

评论

0赞 Panagiotis Korros 10/20/2008
有没有办法将经度和纬度转换为 x、y 坐标?
1赞 2pha 7/24/2012 #4

我一直在使用它,忘记我从哪里得到它。

SELECT n, SQRT(POW((69.1 * (n.field_geofield_lat - :lat)) , 2 ) + POW((53 * (n.field_geofield_lon - :lon)), 2)) AS distance FROM field_revision_field_geofield n ORDER BY distance ASC