1

クエスト

このクエリは、「Vancouver」で始まるすべてのポイントを選択し、「Vancouver」で始まるすべての場所の中心から 5 分のエリア内にあります。たとえば、Vancouver South Fraser、Vancouver Fairview、Vancouver Ballantree Place W の緯度と経度は、それぞれの平均緯度と経度から 5 分以内です。緯度と経度は (4915, 12311) の整数のペア (北緯 49.15 度と西経 123.11 度) として格納されます。

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() を探しているかもしれません。

第 2 に、互いに 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 分の領域については、完全に正確に言うと、10 x 10 分の領域であると言えます。これが、クエリが返すものです。

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

古き良きピタゴラスに何が起こったとしても(確かにそれは曲面には当てはまらないことはわかっていますが、十分に近似しているはずです)。座標ペアのセットの中心(実際には、幾何学者ではなく物理学者によって適用された解釈に基づく重心)を探している場合は、 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 に答える
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 に答える