如何有效地存储DB中的城镇之间的距离

时间:2012-10-02 20:18:38

标签: mysql database-design architecture nosql graph-databases

我需要能够从用户选择的特定位置显示到 n 城市/城镇的距离。它就像点击地图并获得100英里范围内的所有目的地一样,只是它不是地图而是网页上的链接。

我需要选择一个可以从州内扩展到全球范围的解决方案 - 这意味着从数千到数十万个位置。

我虽然存储了CITY1_ID,CITY2_ID&关系数据库表中的DISTANCE,但我怀疑它是否可以很好地扩展到Web应用程序(数百万行)。

使用NoSQL数据库或图形数据库可以更有效地完成这项工作吗?或者RDBMS是否足以通过适当的设计解决这个问题?

补充:如果我不存储在数据库中,那么我将如何得到类似的信息: 让我距离圣何塞100英里以内的所有城市?

7 个答案:

答案 0 :(得分:4)

你应该为每个城市存储一个city_id, latitude, longitude - 然后根据运行时输入计算距离。

答案 1 :(得分:2)

不是计算两个城市之间的距离,而是计算一个100英里的边界框,然后你有4个浮点变量插入你的数据库 - 浮点数比数据库中的距离计算快得多。下行是你在角落里得到更多的距离。

用于计算边界框的PHP函数

function getBoundingBox($lat_degrees,$lon_degrees,$distance_in_miles)
{
       $radius = 3963.1; // of earth in miles

        // bearings
        $due_north = 0;
        $due_south = 180;
        $due_east = 90;
        $due_west = 270;

        // convert latitude and longitude into radians
        $lat_r = deg2rad($lat_degrees);
        $lon_r = deg2rad($lon_degrees);

        // find the northmost, southmost, eastmost and westmost corners $distance_in_miles away
        // original formula from
        // http://www.movable-type.co.uk/scripts/latlong.html

        $northmost  = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_north));
        $southmost  = asin(sin($lat_r) * cos($distance_in_miles/$radius) + cos($lat_r) * sin ($distance_in_miles/$radius) * cos($due_south));

        $eastmost = $lon_r + atan2(sin($due_east)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r));
        $westmost = $lon_r + atan2(sin($due_west)*sin($distance_in_miles/$radius)*cos($lat_r),cos($distance_in_miles/$radius)-sin($lat_r)*sin($lat_r));

        $northmost = rad2deg($northmost);
        $southmost = rad2deg($southmost);
        $eastmost = rad2deg($eastmost);
        $westmost = rad2deg($westmost);

        //return 2 points NW corner and SE corner
        return array($northmost,$westmost,$southmost,$eastmost);
}

那么你的SQL是

SELECT * FROM table WHERE latitude <= $northmost AND longitude >= $westmost AND latitude >= $southmost AND longitude <= $eastmost

答案 2 :(得分:0)

不要存储它,用经度和纬度计算运行时间。极其可扩展,与保存城市之间的所有距离相反。

你有一个参考点(圣何塞)并循环遍历你所有的城市记录并计算它的运行时间(如果有很多记录,客户可以用javascript或其他东西完成这个计算,因为如果你有服务器这样做,很快就会造成损失)。 JavaScript可能看起来像这样:

var R = 6371; // Radius of the earth in km
var dLat = (lat2-lat1).toRad();  // Javascript functions in radians
var dLon = (lon2-lon1).toRad(); 
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) * 
        Math.sin(dLon/2) * Math.sin(dLon/2); 
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
var d = R * c; // Distance in km

以上代码来自here

注意:因为我是荷兰人,因此使用公制系统

答案 3 :(得分:0)

我使用Neo4J来表示类似的东西,它可以很好地扩展到可以表示为图形的任何类型的数据。

答案 4 :(得分:0)

正如其他人所说,您可以为每个条目存储Lat / Long坐标,并在运行时使用类似于以下内容的距离来计算距离,这提供了km /英里距离输出:

function distance($lat1, $lng1, $lat2, $lng2, $miles = true)
{
        $pi80 = M_PI / 180;
        $lat1 *= $pi80;
        $lng1 *= $pi80;
        $lat2 *= $pi80;
        $lng2 *= $pi80;

        $r = 6372.797; // mean radius of Earth in km
        $dlat = $lat2 - $lat1;
        $dlng = $lng2 - $lng1;
        $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
        $km = $r * $c;

        return ($miles ? ($km * 0.621371192) : $km);
}

编辑:这不适合在半径搜索中的 n 匹配。考虑到给定半径内的城镇密度,最好将距离计算移动到SQL中,因为它的速度要快得多,你可以匹配 x km / miles内的那些。

答案 5 :(得分:0)

我多次使用的简单解决方案(但不是使用mysql)创建了一个用户定义函数some_distance_function,其中包含四个参数latitude1longitude1latitude2longitude2返回距离,然后只针对该距离函数测试所有内容,并查看每个项目,无论距离是否小于或等于给定值。如果你只有几千个地点,这是非常好的和有效的。

如果您需要针对数百万条记录运行此查询,您可能希望查看哪些GIS(地理信息系统)扩展可用于您选择的数据库,因为有更好的(至少在搜索能力方面)用于搜索大量位置的持久数据结构。

编辑: 要举例说明Microsoft如何执行此操作,请参阅http://technet.microsoft.com/en-us/library/bb964712(v=sql.105).aspx

看起来MySQL通常支持空间扩展:

http://dev.mysql.com/doc/refman/5.0/en/gis-introduction.html
http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html

编辑II:

看起来这个问题可能也会有所帮助。

Find the distance between two points in MYSQL. (using the Point Datatype)

答案 6 :(得分:0)

这是使用RDBMS的解​​决方案。保留两张桌子

  • CityByLat {latitude,city_id}在纬度和
  • 上有聚集索引
  • 具有经度聚集索引的CityByLng {logitude,city_id}

当您需要从给定的纬度和经度查找特定半径范围内的城市时,您可以对两个表格进行有效范围查询,以获得一定纬度和经度范围内的城市。然后,您可以仅计算检索到的城市的实际距离。