【问题标题】:Sorting MySQL results by calculated distance (distance not stored in DB)按计算距离对 MySQL 结果排序(距离未存储在 DB 中)
【发布时间】:2015-09-13 22:19:17
【问题描述】:

我将“地点”存储在数据库中,我正在使用 PHP 来访问它们。我想要做的是返回所有按相对于某个地方的距离排序的地方。

这个地点将是来自 Android 应用程序的动态,即我想显示最接近用户位置的所有地点。

最好的方法是什么?将PHP中的所有位置检索到一个数组中,计算每个位置的距离,然后按距离对该数组进行排序是否有效/高效?或者有没有更简单/更快的方法来完成我的需要?

谢谢!

【问题讨论】:

    标签: php android mysql sorting geolocation


    【解决方案1】:

    您可以在 SQL 中通过即时计算距离来完成这一切。这是存储的lat/lon 字段与提供的$lat/$lon 之间距离的粗略近似值。

    $dlat = "(`lat`-$lat)";
    $dlon = "(`lon`-$lon)*".cos($lat*3.1415/180);
    $dist_sql = "$dlat*$dlat+$dlon*$dlon";
    $sql = "IF(`lat` IS NULL,1e20,$dist_sql)";
    

    然后像使用任何其他字段一样使用$sql,例如

    SELECT * FROM `table` ORDER BY $sql ASC
    

    这远非完美,但至少可以帮助您入门。

    cos() 之所以出现,是因为随着(绝对)纬度的增加,经度的一个度数会缩短距离。

    要从$sql 得到以公里为单位的实际值,除以 90 并乘以 10000 公里。 (再次:非常粗略的近似值!)

    【讨论】:

    • 太棒了!谢谢,不知道这可以在 SQL 中完成。我希望有更好的方法。谢谢你的开始,我会努力的。
    【解决方案2】:

    如果数据库中的每个位置都有纬度/经度字段,则在数据库中进行距离计算。以下存储过程需要进行一些调整以适合您的数据库,因为我们不知道架构 - 但它假定一个名为 places 的表具有一个包含位置纬度的字段和一个包含经度的字段。

    CREATE PROCEDURE `spNearestPlaces`(IN `_latitude` double, IN `_longitude` double, IN `_radius` INT)
        LANGUAGE SQL
        NOT DETERMINISTIC
        CONTAINS SQL
        SQL SECURITY DEFINER
        COMMENT ''
    proc:begin
        declare strsql varchar(10000);
        declare lat double default 0;
        declare lng double default 0;
        declare radius float default 0;
        declare earth_radius integer default 0;
        declare lon1 float;
        declare lat1 float;
        declare lon2 float;
        declare lat2 float;
    
    
    
        set @lat=_latitude;
        set @lng=_longitude;
    
        set @radius=cast(_radius as unsigned);
        set @earth_radius=3956;
    
        set @lon1 = @lng - @radius/ceil( cos( radians( @lat ) ) * 69 );
        set @lon2 = @lng + @radius/ceil( cos( radians( @lat ) ) * 69 );
        set @lat1 = @lat - ( @radius/69 );
        set @lat2 = @lat + ( @radius/69 );
    
    
    
    
        set @strsql=concat("select
                p.`id`,
                p.`latitude`,
                p.`longitude`,
                ROUND( @earth_radius * 2 * ASIN( SQRT( POWER( SIN( (@lat - p.`latitude`) * pi()/180 / 2), 2) +COS(@lat * pi()/180) * COS(p.`latitude` * pi()/180) *POWER(SIN((@lng - p.`longitude`) * pi()/180 / 2), 2) ) ),2) AS 'distance'
                from `places` p
                where   
                        p.`latitude` between @lat1 and @lat2
                        and 
                        p.`longitude` between @lon1 and @lon2
                having `distance` <= @radius 
                order by `distance`;");
    
        prepare stmt from @strsql;
        execute stmt;
        deallocate prepare stmt;
    end
    

    你会从 php 调用存储过程,例如:-

    $lat=56.564;
    $lng=-2.5465;
    $radius=5;
    
    call `spNearestPlaces`($lat,$lng,$radius);
    

    【讨论】:

    • 太棒了,谢谢,我会检查一下。截至目前,我的桌子没有纬度/经度,但如果需要,我可以轻松添加这些
    猜你喜欢
    • 2018-11-18
    • 2016-12-15
    • 2011-12-31
    • 1970-01-01
    • 1970-01-01
    • 2015-06-28
    • 1970-01-01
    • 2019-02-05
    • 1970-01-01
    相关资源
    最近更新 更多