假设已知坐标lat1,lng1,距离distance

方法1:定义数据库函数方法

DROP FUNCTION IF EXISTS `getDistance`;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` FUNCTION `getDistance`(lng1 float(10,7)   
    ,lat1 float(10,7)  
    ,lng2 float(10,7)   
    ,lat2 float(10,7)) RETURNS double
    READS SQL DATA
    COMMENT '计算两个坐标之间的距离'
begin  
    declare d double;  
    declare radius int;  
    set radius = 6378140; #假设地球为正球形,直径为6378140米  
    set d = (2*ATAN2(SQRT(SIN((lat1-lat2)*PI()/180/2)     
        *SIN((lat1-lat2)*PI()/180/2)+     
        COS(lat2*PI()/180)*COS(lat1*PI()/180)     
        *SIN((lng1-lng2)*PI()/180/2)     
        *SIN((lng1-lng2)*PI()/180/2)),     
        SQRT(1-SIN((lat1-lat2)*PI()/180/2)     
        *SIN((lat1-lat2)*PI()/180/2)     
        +COS(lat2*PI()/180)*COS(lat1*PI()/180)     
        *SIN((lng1-lng2)*PI()/180/2)     
        *SIN((lng1-lng2)*PI()/180/2))))*radius;  
    return d;  
end
;;
DELIMITER ;

这种方法用法: sql语句中直接写入 getDistance(lng1,lat1,lng,lat)即可;

方法2:使用mysql自带的 st_distance_sphere 函数,注意:使用st_distance_sphere需要Mysql数据库版本为5.7及以上

用法:st_distance_sphere(point (113.8064049, 22.7300434),point(lng,lat))

方法3:在程序中事先把经纬度的距离范围计算出来,然后再sql中检索时候直接缩小范围

以PHP为例 根据已知坐标和距离计算经纬度范围的函数

/**
 * 根据传入的经纬度,和距离范围,返回所有在距离范围内的经纬度的取值范围
 * @param float $lng 经度
 * @param float $lat 纬度
 * @param float $distance 距离(km)
 * @return array
 */
function getSquarePoint($lng, $lat, $distance)
{
    $earthRadius = 6371; //地球半径,km
    $d_lng = 2 * asin(sin($distance / (2 * $earthRadius)) / cos(deg2rad($lat)));
    $d_lng = rad2deg($d_lng);
    $d_lat = $distance / $earthRadius;
    $d_lat = rad2deg($d_lat);
    return array(
        'lng_start' => $lng - $d_lng,//经度开始
        'lng_end' => $lng + $d_lng, //经度结束
        'lat_start' => $lat - $d_lat,//纬度开始
        'lat_end' => $lat + $d_lat,//纬度结束
    );
}


/**
 * 计算两个已知经纬度之间的距离
 * @param float $lng1 经度1
 * @param float $lat1 纬度1
 * @param float $lng2 经度2
 * @param float $lat2 纬度2
 * @return float 距离(单位米)
 */
function getPointDistance($lng1, $lat1, $lng2, $lat2)
{
    $earthRadius = 6371; //地球平均半径,km

    //deg2rad()函数将角度转为弧度
    $radLat1 = deg2rad($lat1);
    $radLat2 = deg2rad($lat2);
    $radLng1 = deg2rad($lng1);
    $radLng2 = deg2rad($lng2);
    $d_lat = $radLat1 - $radLat2;
    $d_lng = $radLng1 - $radLng2;
    $distance = 2 * asin(sqrt(pow(sin($d_lat / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($d_lng / 2), 2))) * $earthRadius * 1000;
    return intval($distance / 1000);
}

先根据已知坐标算出来经度lat坐标的范围 lat_start ~ lat_end;纬度lng坐标的范围 lng_start ~ lng_end;然后sql里直接 lat between(lat_start,lat_end) and lng between(lng_start_lng_end) ;

这三种方法性能:方法3 > 方法2 > 方法1;同样的查询,在数据表数据在13.5万左右中测试:方法3比方法2快5倍左右,方法2比方法1快20倍左右

方法1是之前在么有方法2时候使用的,方法2是后来mysql版本加入的,理所当然自带的函数要优于自定义的函数,毕竟人家加入自带函数也是经过N多次试验出来的,方法3跟方法1 、2不同,方法1 、2都是先把表里的数据全部检索一遍计算距离,然后排序,方法3则在数据表检索时候已经把范围缩小了,检索数据量也小了,当数据表数据小的时候这3个方法差别不大,一旦数据表数据庞大的时候,检索快慢性能一下子就对比出来了。

因个人初学者能力有限,如果有不足之处或者更好的方法请大神赐教,万分感谢