任务
查询选择以“温哥华”开头的所有点,并且距离以“温哥华”开头的所有地点的中心都在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总是很好。
谢谢!
答案 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))
下进行。