我目前在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;

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


当前回答

这里是一个非常详细的描述地理距离搜索MySQL的一个解决方案的基础上实现的Haversine公式到MySQL。完整的解决方案描述,包括理论、实现和进一步的性能优化。尽管空间优化部分在我的案例中没有正确工作。 http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

其他回答

一个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

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

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

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`

这里是一个非常详细的描述地理距离搜索MySQL的一个解决方案的基础上实现的Haversine公式到MySQL。完整的解决方案描述,包括理论、实现和进一步的性能优化。尽管空间优化部分在我的案例中没有正确工作。 http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

有阅读地理距离搜索MySQL,一个解决方案 基于Haversine公式对MySQL的实现。这是一个完整的解决方案 描述与理论,实现和进一步的性能优化。 尽管空间优化部分在我的案例中没有正常工作。

我发现其中有两个错误:

在p8上的select语句中使用abs。我只是省略了腹肌,而且很有效。 p27上的空间搜索距离函数不转换为弧度或经度乘以cos(纬度),除非他的空间数据是考虑到这一点而加载的(不能从文章的上下文中判断),但他在p26上的例子表明他的空间数据POINT没有加载弧度或角度。

一个快速,简单和准确(对于较小的距离)的近似可以用球面投影完成。至少在我的路由算法中,与正确的计算相比,我得到了20%的提升。在Java代码中,它看起来像:

public double approxDistKm(double fromLat, double fromLon, double toLat, double toLon) {
    double dLat = Math.toRadians(toLat - fromLat);
    double dLon = Math.toRadians(toLon - fromLon);
    double tmp = Math.cos(Math.toRadians((fromLat + toLat) / 2)) * dLon;
    double d = dLat * dLat + tmp * tmp;
    return R * Math.sqrt(d);
}

不太了解MySQL(对不起!)。

请确保您了解限制(assertEquals的第三个参数表示以公里为单位的精度):

    float lat = 24.235f;
    float lon = 47.234f;
    CalcDistance dist = new CalcDistance();
    double res = 15.051;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);

    res = 150.748;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 1, lon + 1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 1, lon + 1), 1e-2);

    res = 1527.919;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 10, lon + 10), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 10, lon + 10), 10);

使用mysql

SET @orig_lon = 1.027125;
SET @dest_lon = 1.027125;

SET @orig_lat = 2.398441;
SET @dest_lat = 2.398441;

SET @kmormiles = 6371;-- for distance in miles set to : 3956

SELECT @kmormiles * ACOS(LEAST(COS(RADIANS(@orig_lat)) * 
 COS(RADIANS(@dest_lat)) * COS(RADIANS(@orig_lon - @dest_lon)) + 
 SIN(RADIANS(@orig_lat)) * SIN(RADIANS(@dest_lat)),1.0)) as distance;

参见:https://andrew.hedges.name/experiments/haversine/

参见:https://stackoverflow.com/a/24372831/5155484

参见:http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

注意:LEAST用于避免null值,如https://stackoverflow.com/a/24372831/5155484上建议的注释