找到两个纬度/长点之间距离的最快方法

Rya*_*zel 216 mysql gis location

我目前在mysql数据库中的位置不到一百万个,都有经度和纬度信息.

我试图通过查询找到一个点和许多其他点之间的距离.它没有我想要的那么快,尤其是每秒100次点击.

是否有更快的查询或可能比mysql更快的系统?我正在使用此查询:

SELECT 
  name, 
   ( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) ) 
   * cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763)) 
   * sin( radians(locations.lat)))) AS distance 
FROM locations 
WHERE active = 1 
HAVING distance < 10 
ORDER BY distance;
Run Code Online (Sandbox Code Playgroud)

注意:提供的距离以英里为单位.如果您需要公里数,请使用6371而不是3959.

Qua*_*noi 112

,或者,在MySQL 5.1上面:

    SELECT  *
    FROM    table
    WHERE   MBRContains
                    (
                    LineString
                            (
                            Point (
                                    @lon + 10 / ( 111.1 / COS(RADIANS(@lat))),
                                    @lat + 10 / 111.1
                                  ),
                            Point (
                                    @lon - 10 / ( 111.1 / COS(RADIANS(@lat))),
                                    @lat - 10 / 111.1
                                  ) 
                            ),
                    mypoint
                    )
Run Code Online (Sandbox Code Playgroud)

这将选择框内大致的所有点(@lat +/- 10 km, @lon +/- 10km).

这实际上不是一个盒子,而是一个球形矩形:球体的纬度和经度束缚段.这可能与Franz Joseph Land上的一个普通矩形不同,但在大多数有人居住的地方都非常接近.

  • 应用其他过滤以选择圆内的所有内容(不是方形)

  • 可能应用额外的精细过滤来考虑大圆距离(对于大距离)

  • @all,编辑了解决Dave发现错误的答案. (19认同)
  • @Quassnoi:一些修正:你可能想要将坐标的顺序切换为lat,long.此外,纵向距离与*纬度*的余弦成比例,而不是经度.你需要将它从乘法变为除法,所以你的第一个坐标将被校正为`@lon - 10 /(111.1/cos(@lat))`(并且一旦一切正确,它就成为第二个坐标. (14认同)
  • **警告**:答案正文未经过编辑,符合@M的有效评论.戴夫奥亚扬 附加说明:如果感兴趣的圆(a)包括一个极点或(b)与+/- 180度经度子午线相交,则此方法会变形.使用`cos(lon)`也只适用于小距离.见http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates (8认同)
  • @ashays:纬度大致为111.(1)`km.`mypoint`是表中存储坐标的字段. (4认同)
  • 有什么方法可以让我们深入了解常数(10,111.11,@ lalat,@ nelon,mypoint)代表什么?我假设10是公里距离,@ lalat和@lon代表提供的格子和经度,但111.11和mypoint在例子中代表什么? (3认同)

Bin*_*ier 97

不是MySql特定的答案,但它会提高你的sql语句的性能.

你有效地做的是计算到表中每个点的距离,看它是否在给定点的10个单位内.

在运行这个sql之前你可以做的是创建四个点,在一侧绘制一个20个单位的框,你的点位于中心即.(x1,y1)...(x4,y4),其中(x1,y1)是(给定长+10个单位,给定为+10个单位)...(给定长 - 10个单位,给予-10个单位). 实际上,你只需要两个点,左上角和右下角称它们(X1,Y1)和(X2,Y2)

现在你的SQL语句使用这些点从你的给定点排除肯定超过10u的行,它可以使用纬度和经度上的索引,因此将比你现有的快几个数量级.

例如

select . . . 
where locations.lat between X1 and X2 
and   locations.Long between y1 and y2;
Run Code Online (Sandbox Code Playgroud)

框方法可以返回误报(您可以从框中获取距离给定点大于10u的点),因此您仍需要计算每个点的距离.然而,这将再次快得多,因为你已经大大限制了要测试的点数.

我把这种技术称为"在盒子里面思考":)

编辑:这可以放在一个SQL语句中吗?

我不知道mySql或Php能做什么,抱歉.我不知道最好的地方是建立四个点,或者如何将它们传递给Php中的mySql查询.但是,一旦获得了这四点,就没有什么能阻止你将自己的SQL语句与我的结合起来了.

select name, 
       ( 3959 * acos( cos( radians(42.290763) ) 
              * cos( radians( locations.lat ) ) 
              * cos( radians( locations.lng ) - radians(-71.35368) ) 
              + sin( radians(42.290763) ) 
              * sin( radians( locations.lat ) ) ) ) AS distance 
from locations 
where active = 1 
and locations.lat between X1 and X2 
and locations.Long between y1 and y2
having distance < 10 ORDER BY distance;
Run Code Online (Sandbox Code Playgroud)

我知道使用MS SQL我可以构建一个声明四个浮点数(X1,Y1,X2,Y2)并在"主"选择语句之前计算它们的SQL语句,就像我说的,我不知道这是否可以用MySQL的.但是我仍然倾向于在C#中构建四个点并将它们作为参数传递给SQL查询.

抱歉,我无法提供更多帮助,如果有人能够回答MySQL和Php的具体部分,请随时编辑此答案.

  • 要以公里而不是里程搜索,请将3959替换为6371. (34认同)
  • 您可以在此演示文稿中找到此方法的mysql过程:http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL (4认同)
  • +1,很棒的选择; 添加框将我的查询从4s减少到0.03s avg. (3认同)
  • @Binary Worrier:因此,X1,X2和Y1,Y2分别是此处的示例的经度最小值和最大值以及纬度最小值和最大值:http://blog.fedecarg.com/2009/02/08/geo-proximity -搜索haversine方程/请告知。 (2认同)

Bra*_*rks 13

这篇博客文章中,发布了以下MySql函数.我没有对它进行过多次测试,但是根据我从帖子中收集到的内容,如果您的纬度和经度字段已编入索引,这可能对您有用:

DELIMITER $$

DROP FUNCTION IF EXISTS `get_distance_in_miles_between_geo_locations` $$
CREATE FUNCTION get_distance_in_miles_between_geo_locations(
  geo1_latitude decimal(10,6), geo1_longitude decimal(10,6), 
  geo2_latitude decimal(10,6), geo2_longitude decimal(10,6)) 
returns decimal(10,3) DETERMINISTIC
BEGIN
  return ((ACOS(SIN(geo1_latitude * PI() / 180) * SIN(geo2_latitude * PI() / 180) 
    + COS(geo1_latitude * PI() / 180) * COS(geo2_latitude * PI() / 180) 
    * COS((geo1_longitude - geo2_longitude) * PI() / 180)) * 180 / PI()) 
    * 60 * 1.1515);
END $$

DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

示例用法: 假设一个名为Places的表格包含字段纬度和经度:

从地点选择get_distance_in_miles_between_geo_locations(-34.017330,22.809500,纬度,经度)作为distance_from_input;

从这篇文章中抓住了所有人

  • 或者直接选择:从get_distance_in_miles_between_geo_locations(-34.017330,22.809500,纬度,经度)> 5000的地方选择*; (2认同)
  • 返回仪表:`SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() ) / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS距离` (2认同)

小智 9

SELECT * FROM (SELECT *,(((acos(sin((43.6980168*pi()/180)) * 
sin((latitude*pi()/180))+cos((43.6980168*pi()/180)) * 
cos((latitude*pi()/180)) * cos(((7.266903899999988- longitude)* 
pi()/180))))*180/pi())*60*1.1515 ) as distance 
FROM wp_users WHERE 1 GROUP BY ID limit 0,10) as X 
ORDER BY ID DESC
Run Code Online (Sandbox Code Playgroud)

这是MySQL中的点之间的距离计算查询,我在一个长数据库中使用它,它工作得很完美!注意:根据您的要求进行更改(数据库名称,表名称,列等).


Abh*_*yan 8

set @latitude=53.754842;
set @longitude=-2.708077;
set @radius=20;

set @lng_min = @longitude - @radius/abs(cos(radians(@latitude))*69);
set @lng_max = @longitude + @radius/abs(cos(radians(@latitude))*69);
set @lat_min = @latitude - (@radius/69);
set @lat_max = @latitude + (@radius/69);

SELECT * FROM postcode
WHERE (longitude BETWEEN @lng_min AND @lng_max)
AND (latitude BETWEEN @lat_min and @lat_max);
Run Code Online (Sandbox Code Playgroud)

资源

  • 请引用你的消息来源.这来自:http://blog.fedecarg.com/2009/02/08/geo-proximity-search-the-haversine-equation/ (11认同)
  • 1 Latittude的公里数为111公里.1 Latittude的英里距离是69英里.和69英里= 111公里.这就是我们在转换中使用参数的原因. (2认同)

小智 7

如果你使用MySQL 5.7.*,那么你可以使用st_distance_sphere(POINT,POINT).

Select st_distance_sphere(POINT(-2.997065, 53.404146 ), POINT(58.615349, 23.56676 ))/1000  as distcance
Run Code Online (Sandbox Code Playgroud)


小智 6

   select
   (((acos(sin(('$latitude'*pi()/180)) * sin((`lat`*pi()/180))+cos(('$latitude'*pi()/180)) 
    * cos((`lat`*pi()/180)) * cos((('$longitude'- `lng`)*pi()/180))))*180/pi())*60*1.1515) 
    AS distance
    from table having distance<22;
Run Code Online (Sandbox Code Playgroud)


Rob*_*ert 5

一个MySQL函数,它返回两个坐标之间的米数:

CREATE FUNCTION DISTANCE_BETWEEN (lat1 DOUBLE, lon1 DOUBLE, lat2 DOUBLE, lon2 DOUBLE)
RETURNS DOUBLE DETERMINISTIC
RETURN ACOS( SIN(lat1*PI()/180)*SIN(lat2*PI()/180) + COS(lat1*PI()/180)*COS(lat2*PI()/180)*COS(lon2*PI()/180-lon1*PI()/180) ) * 6371000
Run Code Online (Sandbox Code Playgroud)

要以其他格式返回值,请6371000在您选择的单位中将函数中的替换为地球半径。例如,公里为公里6371,英里为3959

要使用该函数,只需像在MySQL中的其他任何函数一样调用它即可。例如,如果您有一个表格city,则可以找到每个城市到每个其他城市之间的距离:

SELECT
    `city1`.`name`,
    `city2`.`name`,
    ROUND(DISTANCE_BETWEEN(`city1`.`latitude`, `city1`.`longitude`, `city2`.`latitude`, `city2`.`longitude`)) AS `distance`
FROM
    `city` AS `city1`
JOIN
    `city` AS `city2`
Run Code Online (Sandbox Code Playgroud)


Mār*_*ovs 5

我需要解决类似的问题(按距单个点的距离来过滤行),并通过将原始问题与答案和注释结合起来,我想出了对MySQL 5.6和5.7都适用的解决方案。

SELECT 
    *,
    (6371 * ACOS(COS(RADIANS(56.946285)) * COS(RADIANS(Y(coordinates))) 
    * COS(RADIANS(X(coordinates)) - RADIANS(24.105078)) + SIN(RADIANS(56.946285))
    * SIN(RADIANS(Y(coordinates))))) AS distance
FROM places
WHERE MBRContains
    (
    LineString
        (
        Point (
            24.105078 + 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 + 15 / 111.133
        ),
        Point (
            24.105078 - 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 - 15 / 111.133
        )
    ),
    coordinates
    )
HAVING distance < 15
ORDER By distance
Run Code Online (Sandbox Code Playgroud)

coordinates是具有类型的字段,POINT并且具有用于计算距离的SPATIAL索引
6371(以公里
56.946285为单位)是中心点的纬度是中心点
24.105078的经度
10是以公里为单位的最大距离

在我的测试中,MySQL在coordinates字段上使用SPATIAL索引来快速选择矩形内的所有行,然后计算所有已过滤位置的实际距离,以排除矩形角中的位置,而仅将圆内的位置保留。

这是我的结果的可视化:

地图

灰色星状图显示地图上的所有点,黄色星状图是MySQL查询返回的点。矩形的角(但在圆的外部)内的灰星MBRContains()HAVING子句选择,然后取消选择。

  • @user606669 这是要点(lng,lat) (3认同)
  • 对此赞不绝口。使用此方法搜索包含大约 500 万条记录和空间索引的表,在旧的 A8 处理器上搜索时间为 0.005 秒。我知道 6371 可以替换为 3959 以获得以英里为单位的结果,但是 111.133 和 111.320 的值是否需要调整或者它们普遍恒定? (2认同)