MYSQL无法选择点之间距离(​​lat,lng)= 0的记录

时间:2013-07-30 17:41:08

标签: php mysql floating-point latitude-longitude trigonometry

我有不满的mysql查询

SELECT de.geoId,(6371 * ACOS( COS( RADIANS(zde.latitude) )* 
    COS(RADIANS( de.latitude ) ) * COS( RADIANS( de.longitude ) - RADIANS(zde.longitude) ) + 
    SIN( RADIANS(zde.latitude) ) * SIN( RADIANS( de.latitude ) ) ) )  AS distance 
FROM tbl_zipde AS zde 
    LEFT JOIN  tbl_country_de AS de 
             ON (de.admin1_code=zde.admin_code1)  
                 Where zde.id=8 and  de.geoId=24 having distance<1

此查询应返回表tbl_zountry_de中的记录,该记录具有与来自tbl_zipde的id = 8的记录相同的纬度和经度值,但由于计算出的这两点之间的距离为0,因此mysql将0识别为NULL,因此它不要返回任何记录,但是如果我删除“有距离&lt; 1”,那么它从tbl_country_de返回id = 24的corect记录,但是表列距离为NULL值。

如何编写mysql查询以便mysql返回两点之间(纬度和经度值)之间距离等于0的记录

为什么mysql为距离而不是'0'返回NULL?

我已将纬度和经度数据类型从十进制(10,7)更改为浮点数(10,7),我的工作原理。 不知道那是不是真正的问题?

4 个答案:

答案 0 :(得分:3)

这个问题已存在一段时间了,但尚未正确回答。

DECIMAL是地理定位数据的不合适数据类型。

问题中的公式称为球面余弦定律公式。如果你仔细看看这个公式,你会注意到当你处理非常接近的点时,它取一个数的反余弦(ACOS())消失得接近1那是一个数字上不稳定的操作。此上下文中的不稳定意味着如果输入中的错误较小,则函数的输出可能会有很大差异。

对于称为Vincenty公式的球体表面的距离,有一个更好的公式。它的最后一个操作是ATAN2():对于微小角度,它的数值更稳定。就是这样:

111.045 * DEGREES(ATAN2(SQRT(
    POW(COS(RADIANS(lat2))*SIN(RADIANS(lon2-lon1)),2) +
    POW(COS(RADIANS(lat1))*SIN(RADIANS(lat2)) -
         (SIN(RADIANS(lat1))*COS(RADIANS(lat2)) *
          COS(RADIANS(lon2-lon1))) ,2)),
  SIN(RADIANS(lat1))*SIN(RADIANS(lat2)) +
  COS(RADIANS(lat1))*COS(RADIANS(lat2))*COS(RADIANS(lon2-lon1))))

这是一个MySQL存储过程:http://www.plumislandmedia.net/mysql/vicenty-great-circle-distance-formula/

您可以在此处查看数学:http://en.wikipedia.org/wiki/Great-circle_distance

另外,请记住,MySQL中的trig函数(余弦,正弦,反正切等)是使用服务器计算机的浮点数学子系统实现的。如果以DECIMAL格式向它们提供数据,则数据将转换为DOUBLE,计算,然后转换回来。这将导致它失去精确度。 FLOAT数据类型(IEEE-488单精度浮点)为商业GPS风格的纬度和经度数据提供了足够的精度。

问题中提到的NULL结果可能是由于在DOUBLE和DECIMAL之间转换时固有的错误导致ACOS的输入无限大于1。反余弦值1.00001失败并产生NULL。切换到FLOAT和Vincenty公式将消除这种失败的根源。

答案 1 :(得分:1)

它不应该返回null。你有一个左连接而不是内连接,所以你可能在你的表中有一些记录没有匹配的管理代码在tbl_country_de

或者您的数据中是否有空值?

答案 2 :(得分:0)

我已经将纬度和经度数据类型从十进制(10,7)更改为float(10,7)并且它可以工作。 不知道那是不是真正的问题?

答案 3 :(得分:0)

我知道这是一个非常古老的线程,但我刚刚在一些遗留代码中遇到过这个问题。我通过在距离计算周围使用合并函数来解决问题。

select coalesce(*distance caclulation*, 0) as distance from ...

因此我从计算中得到0而不是null。我确定有人会告诉我这是一种骇人的,可怕的做法,但它对我们来说也不是一个关键的操作,或者有人会在此之前发现错误。