我有不满的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),我的工作原理。 不知道那是不是真正的问题?
答案 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。我确定有人会告诉我这是一种骇人的,可怕的做法,但它对我们来说也不是一个关键的操作,或者有人会在此之前发现错误。