如何限制MySQL距离查询

时间:2010-10-16 18:49:28

标签: sql mysql

我正在尝试执行距离计算以返回特定距离内的地点列表。这是基于使用邮政编码数据库并确定从原点到每个位置的距离。我想要做的是将结果限制在离原点一定距离之内,但我的MySQL查询有问题。这是基本查询:

SELECT *, 
       ROUND(DEGREES(ACOS(SIN(RADIANS(42.320271)) * SIN(RADIANS(zip_latitude)) + COS(RADIANS(42.320271)) * COS(RADIANS(zip_latitude)) * COS(RADIANS(-88.462832 - zip_longitude))))) * 69.09 AS distance 
  FROM locations 
LEFT JOIN zip_codes USING (zip_code)  
 ORDER BY distance ASC

这很好用,并为我提供了每个位置的所有信息,包括与原始邮政编码的距离......正是我想要的。但是,我想将结果限制在一定距离内(即,距离<= 50)。

我的问题和问题是我无法弄清楚在上面的查询中包含(WHERE距离&lt; = 50)以使其全部工作。我尝试的一切都给了我一个错误信息。任何帮助都会很棒。

3 个答案:

答案 0 :(得分:1)

您有两种选择:

  1. 重写WHERE子句中的逻辑,以便您可以按它进行过滤:

       SELECT *, 
              ROUND(DEGREES(ACOS(SIN(RADIANS(42.320271)) * SIN(RADIANS(zip_latitude)) + COS(RADIANS(42.320271)) * COS(RADIANS(zip_latitude)) * COS(RADIANS(-88.462832 - zip_longitude))))) * 69.09 AS distance 
         FROM locations 
    LEFT JOIN zip_codes USING (zip_code)  
        WHERE (ROUND(DEGREES(ACOS(SIN(RADIANS(42.320271)) * SIN(RADIANS(zip_latitude)) + COS(RADIANS(42.320271)) * COS(RADIANS(zip_latitude)) * COS(RADIANS(-88.462832 - zip_longitude))))) * 69.09) <= 50
     ORDER BY distance 
    

    这是更好的选择,因为它只需要传递一次数据。遗憾的是,它需要您复制逻辑 - 如果您使用的是GROUP BYHAVING子句中的信息,MySQL支持在这些中引用列别名。

  2. 使用子查询:

      SELECT x.* 
        FROM (SELECT *, 
                     ROUND(DEGREES(ACOS(SIN(RADIANS(42.320271)) * SIN(RADIANS(zip_latitude)) + COS(RADIANS(42.320271)) * COS(RADIANS(zip_latitude)) * COS(RADIANS(-88.462832 - zip_longitude))))) * 69.09 AS distance 
                FROM locations 
           LEFT JOIN zip_codes USING (zip_code)) x
       WHERE x.distance <= 50 
    ORDER BY x.distance 
    

答案 1 :(得分:0)

一个简单的解决方案是将查询包装在另一个查询中,并将条件和顺序放在那里。这应该有效:

SELECT * FROM (
SELECT *, ROUND(DEGREES(ACOS(SIN(RADIANS(42.320271)) * SIN(RADIANS(zip_latitude)) + COS(RADIANS(42.320271)) * COS(RADIANS(zip_latitude)) * COS(RADIANS(-88.462832 - zip_longitude))))) * 69.09 AS distance FROM locations LEFT JOIN zip_codes USING (zip_code)
) WHERE distance <= 50 ORDER BY distance ASC

中间一行是没有ORDER BY

的查询

答案 2 :(得分:0)

最简单的解决方案实际上是使用HAVING而不是WHERE,我也将ROUND函数的最后一个括号移到了69.09后面,这样可以提高计算精度,但仍然会返回一个舍入数字:

SELECT *, 
    ROUND(DEGREES(ACOS(SIN(RADIANS(42.320271)) * SIN(RADIANS(zip_latitude))
    + COS(RADIANS(42.320271)) * COS(RADIANS(zip_latitude)) 
    * COS(RADIANS(-88.462832 - zip_longitude)))) * 69.09,2) AS distance 
FROM locations 
LEFT JOIN zip_codes USING (zip_code)
HAVING distance <= 50
ORDER BY distance ASC

所以基本上你的查询很好,你只需要添加HAVING子句,你可能想要修复该舍入以获得更好的结果。在我的项目中,我使用了非常相似的公式,丢弃外部DEGREES函数并乘以地球周长:

ROUND(ACOS(SIN(RADIANS('42.320271')) * SIN(RADIANS(zip_latitude)) 
+ COS(RADIANS('42.320271')) * COS(RADIANS(zip_latitude))
* COS(RADIANS(zip_longitude - '-88.462832'))) * 3963.190592,3) AS distance

并且通过更改末尾的乘数,你有公里而不是里程:

ROUND(ACOS(SIN(RADIANS('42.320271')) * SIN(RADIANS(zip_latitude)) 
+ COS(RADIANS('42.320271')) * COS(RADIANS(zip_latitude))
* COS(RADIANS(zip_longitude - '-88.462832'))) * 6378.137,3) AS distance