消除子查询的平均数值

时间:2010-05-06 06:17:23

标签: sql mysql ansi-sql

任务

查询选择以“温哥华”开头的所有点,并且距离以“温哥华”开头的所有地点的中心都在5分钟范围内。例如,温哥华南弗雷泽,温哥华锦绣和温哥华巴兰兰广场W的纬度和经度在其平均纬度和经度的5分钟内。纬度和经度存储为(4915,12311)整数对(意思是49.15'N和123.11'W)。

SQL代码

以下SQL憎恶可以解决问题:

SELECT
  NAME
FROM
 STATION
WHERE
      DISTRICT_ID = '110'
  AND NAME LIKE 'Vancouver%'
  AND LATITUDE BETWEEN
    (SELECT round((min(LATITUDE) + max(LATITUDE)) / 2)-5 FROM STATION WHERE DISTRICT_ID = '110' AND NAME LIKE 'Vancouver%')
    and
    (SELECT round((min(LATITUDE) + max(LATITUDE)) / 2)+5 FROM STATION WHERE DISTRICT_ID = '110' AND NAME LIKE 'Vancouver%')
  AND LONGITUDE BETWEEN
    (SELECT round((min(LONGITUDE) + max(LONGITUDE)) / 2)-5 FROM STATION WHERE DISTRICT_ID = '110' AND NAME LIKE 'Vancouver%')
    and
    (SELECT round((min(LONGITUDE) + max(LONGITUDE)) / 2)+5 FROM STATION WHERE DISTRICT_ID = '110' AND NAME LIKE 'Vancouver%')
ORDER BY
  LATITUDE

问题

如何在不使用视图的情况下简化此查询以删除冗余?

限制

数据库是MySQL,但ANSI SQL总是很好。

谢谢!

5 个答案:

答案 0 :(得分:2)

select 
  name
from 
  (select 
    round((min(LATITUDE) + max(LATITUDE)) / 2) as LATITUDE,
    round((min(LONGITUDE) + max(LONGITUDE)) / 2) as LONGITUDE
   from STATION 
   where DISTRICT_ID = '110' 
     AND NAME LIKE 'Vancouver%') AS center
  inner join STATION s
where
  s.DISTRICT_ID = '110' 
  and s.NAME like 'Vancouver%'
  and s.LATITUDE between center.LATITUDE - 5 and center.LATITUDE + 5
  and s.LONGITUDE between center.LONGITUDE - 5 and center.LONGITUDE + 5

答案 1 :(得分:2)

首先请注意,你的定义'彼此在5分钟内'并没有定义单个解决方案,你的(MIN()+ MAX())/ 2不是平均值,而是简单地在min和max的中间。您可能正在查询子查询中的AVG()。

其次,你没有在5秒内获得结果,但是经度和纬度最多为10秒的条目(在对角线上可以接近14)。

在mysql中,您可以使用会话变量,例如:

SET @avg_lat := (SELECT round(avg(LATITUDE)) FROM STATION WHERE DISTRICT_ID = '110' AND NAME LIKE 'Vancouver%');
SET @avg_long := (SELECT round(avg(LONGITUDE)) FROM STATION WHERE DISTRICT_ID = '110' AND NAME LIKE 'Vancouver%');

SELECT
  NAME
FROM
 STATION
WHERE
  DISTRICT_ID = '110'
  AND pow(LATITUDE-@avg_lat,2)+pow(LONGITUDE-@avg_long,2)<25
ORDER BY
  LATITUDE

即使没有必要(如上面所写的查询,两个变量只出现一次)。

编辑:哎呀,错过了这个问题。它是中心的半径 - 所以用100代替25(并且在它决定你是否想要使用更少或相等)。此外,如果center是边界框的中心,那么你的(min()+ max())/ 2是正确的公式而不是我的建议。仍然“所有位置的中心”有点模糊,所以我留下我的答案(很容易改变它)。

EDIT2:刚刚注意到我的查询中的单位不正确,如果纬度以厘米为单位存储,那么比较也应该是厘米(10 * 100)^ 2 = 1000000

最后,你决定坚持使用(min()+ max())/ 2将导致你可能有一行远离max和min的情况,这可能会导致查询错过任何结果(并且可能发生,通常所有具有相似名称的位置彼此相邻,但是以相同名称开头的另一个位置并不常见,这是一个远离地点集团的孤立地点)

对于5分钟区域,要完全准确地说它是10x10分钟区域,那就是查询将返回的区域。

EDIT3:如果你离开赤道,上面用于距离的公式不是很精确。 这是距离formula的更好近似值 对于认真的工作,您可能需要this

之类的东西

答案 2 :(得分:1)

使用公用表格表达式...

with cte as
 (  SELECT round((min(LATITUDE) + max(LATITUDE)) / 2)-5 min_lat
           , round((min(LATITUDE) + max(LATITUDE)) / 2)+5 max_lat
           , round((min(LONGITUDE) + max(LONGITUDE)) / 2)-5 min_long
           , round((min(LONGITUDE) + max(LONGITUDE)) / 2)+5 max_long
           , DISTRICT_ID
           ,  'Vancouver%' AS NAME 
    FROM STATION 
    WHERE DISTRICT_ID = '110' 
AND NAME LIKE 'Vancouver%'
group by DISTRICT_ID,  'Vancouver%')
SELECT
  NAME
FROM
 STATION , cte
WHERE
      station.DISTRICT_ID = cte.DISTRICT_ID
  AND station.NAME LIKE cte.NAME
  AND station.LATITUDE BETWEEN cte.min_lat AND cte.max_lat
   AND station.LONGITUDE BETWEEN cte.min_long AND cte.max_long
ORDER BY
  station.LATITUDE

注意:我现在无法访问数据库,所以我无法对此进行测试。因此,我不能保证它是有效的。我会尽力测试。原则 成立。

答案 3 :(得分:0)

我假设原始查询为您的目的提供了准确的结果。如果是这种情况,那么您可以通过将终点的计算放入子查询来合并查询。

Select ...
From Station As S
    Cross Join  (
                Select Round( (Min(S1.Latitude)  + Max(S1.Latitude)) / 2 ) As Latitude
                    , Round( (Min(S1.Longitude)  + Max(S1.Longitude)) / 2 ) As Longitude
                From Station As S1
                Where S1.District_Id = '110'
                    And S1.Name Like 'Vancouver%'
                ) As S2
Where S.District_Id = '110'
    And S.Name Like 'Vancouver%'
    And  S.Latitude Between (S2.Latitude - 5) And (S2.Latitude + 5)
    And  S.Longitude Between (S2.Longitude - 5) And (S2.Longitude + 5)
Order By S.Latitude

答案 4 :(得分:0)

无论好老毕达哥拉斯发生了什么(好吧我知道它并不适用于曲面 - 但应该是一个足够好的近似值)。如果你正在寻找坐标对集合中心(实际上是基于物理学家而不是几何学家应用的解释的质心),那么你不应该使用MIN和MAX,尽管你可能考虑基于MIN和MAX限制搜索。美中不足的唯一一个是存储坐标角度的字符串表示的整数表示。

考虑:

SELECT b.name
FROM
(SELECT AVG(CALC(a.lattitude)) AS c_lat, AVG(CALC(a.longitude)) AS c_long
  FROM station a 
  WHERE a.district_id='110'
  AND a.name like 'VANCOUVER%'
) AS ilv,
station b
WHERE b.district_id='110'
AND b.name LIKE 'VANCOUVER%'
AND POW(ilv.c_lat-CALC(b.lattitude),2)
     + POW(olv.c_long-CALC(b.longitude),2)<=25;

CALC函数将存储的值转换为以分钟为单位的经度/格值,即

CALC(x)=(FLOOR(x/100)*60+MOD(x,100))

下进行。