2010-10-16 81 views
2

我正在尝试执行距离计算以返回特定距离内的地点列表。这是基于使用邮政编码数据库并确定从原点到每个位置的距离。我想要做的是将结果限制在离原点一定距离内,但是我的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)。

我的问题是我无法弄清楚在哪里包括(在哪里距离< = 50)到上面的查询,使其一切工作。我试过的所有东西都会给我一个错误信息。任何帮助都会很棒。

回答

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 
    

    这是更好的选择,因为它只需要一次通过e数据。不幸的是,它需要你复制逻辑 - 如果你使用的是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 
    
+0

这是完美的,你摇滚。我不会想这样试。谢谢! – user469626 2010-10-16 21:11:10

+1

您的第一个解决方案非常适合我,谢谢! - 尽管执行两次接近计算似乎有点浪费? – Haroldo 2011-07-20 10:38:43

+0

@Haroldo:SQL是基于SET而不是程序的。 – 2011-07-20 14:21:21

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

+0

声音很简单,但是当我尝试它时不起作用。我得到一个错误:#1248 - 每个派生表必须有它自己的别名 – user469626 2010-10-16 21:06:55

0

最简单的解决方案实际上是使用具有代替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