1

寻求

查询选择所有以“Vancouver”开头且距离所有以“Vancouver”开头的位置中心 5 分钟范围内的点。例如,Vancouver South Fraser、Vancouver Fairview 和 Vancouver Ballantree Place 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 总是很好。

谢谢!

4

5 回答 5

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
于 2010-05-06T06:45:18.013 回答
2

首先请注意,您的“彼此相距 5 分钟内”的定义并未定义单一解决方案,并且您的 (MIN()+MAX())/2 不是平均值,而只是最小值和最大值的中间值。您可能正在子查询中寻找 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

即使它不是必需的(如在上面写的查询中,两个变量只出现一次)。

编辑:哎呀,误读了这个问题。它是中心的半径 - 因此将 25 替换为 100(同时决定是否要使用更少或等于)。此外,如果中心是边界框的中心,那么您的 (min()+max())/2 是正确的公式,而不是我的建议。仍然“所有位置的中心”有点模糊,所以我留下我的答案(很容易改变它)。

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

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

至于 5 分钟区域,更准确地说是 10x10 分钟区域,这就是查询返回的内容。

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

于 2010-05-06T06:47:11.420 回答
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

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

于 2010-05-06T06:39:28.740 回答
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
于 2010-05-06T06:40:41.737 回答
0

无论老毕达哥拉斯发生了什么(好吧,我知道它并不真正适用于曲面 - 但应该是一个足够好的近似值)。如果您正在寻找坐标对集合的中心(实际上,质心基于物理学家而不是几何学家应用的解释),那么您不应该使用 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))

C。

于 2010-05-06T12:09:04.393 回答