2011-01-19 7 views
3

私は、指定された場所からある範囲(マイル)内のすべての場所を見つけることを可能にするクエリを書く必要があります。2つの座標の間の距離ですが、これを単純化したり、別の手法を使用するにはどうしたらいいですか?

id | name | lat | lng 

だから私は研究をやって発見されています:this my sql presentation

私は約100行でテーブルの上にそれをテストしているし、もっとたくさんを持っています

表には、このようなものです! - スケーラブルでなければならない。これはかなり良いです、その周り50msので行を返さ

//just some test data this would be required by user input  
set @orig_lat=55.857807; set @orig_lng=-4.242511; set @dist=10; 

SELECT *, 3956 * 2 * ASIN(
      SQRT(POWER(SIN((orig.lat - abs(dest.lat)) * pi()/180/2), 2) 
       + COS(orig.lat * pi()/180) * COS(abs(dest.lat) * pi()/180) 
       * POWER(SIN((orig.lng - dest.lng) * pi()/180/2), 2))) 
      AS distance 
    FROM locations dest, locations orig 
WHERE orig.id = '1' 
HAVING distance < 1 
ORDER BY distance; 

は、私はこの最初のような、よりシンプルなものを試してみました! しかし、これは行が増えるにつれて劇的に減速します。

EXPLAINは、明白なPRIMARYキーを使用していることを示しています。


次に、記事linked aboveを読んだ後。このクエリの時間が、これはあまりにも悪いの周り240msではありませんが、最後のよりも遅いです

// defining variables - this when made into a stored procedure will call 
// the values with a SELECT query. 
set @mylon = -4.242511; 
set @mylat = 55.857807; 
set @dist = 0.5; 

-- calculate lon and lat for the rectangle: 
set @lon1 = @[email protected]/abs(cos(radians(@mylat))*69); 
set @lon2 = @[email protected]/abs(cos(radians(@mylat))*69); 
set @lat1 = @mylat-(@dist/69); 
set @lat2 = @mylat+(@dist/69); 

-- run the query: 

SELECT *, 3956 * 2 * ASIN(
      SQRT(POWER(SIN((@mylat - abs(dest.lat)) * pi()/180/2) ,2) 
       + COS(@mylat * pi()/180) * COS(abs(dest.lat) * pi()/180) 
       * POWER(SIN((@mylon - dest.lng) * pi()/180/2), 2))) 
      AS distance 
    FROM locations dest 
WHERE dest.lng BETWEEN @lon1 AND @lon2 
    AND dest.lat BETWEEN @lat1 AND @lat2 
HAVING distance < @dist 
ORDER BY distance; 

:私はこのような何かを試してみました。しかし、これはもっと速く動くはずのはるかに多くの行で想像することができます。ただし、EXPLAINlat,lngまたはPRIMARYという可能なキーを表示し、PRIMARYを使用しています。

どうすればよいですか?

私はPOINT()として緯度経度を格納できると知っています。しかし、私はまた、これが高速か正確かを示すあまりにも多くのドキュメントを見つけられませんでしたか?

その他のアイデアは喜んで受け入れられるでしょう!

ありがとうございました!

-Stefan


UPDATE:ジョナサンレフラーが指摘したように

私は気づいていなかったいくつかのミス作った:

私は腹筋を入れていたが() lat値の1つに設定します。私は必要がなかったときにも、2番目のWHERE句でID検索を使用していました。最初のクエリでは、純粋に実験的なものでしたが、2番目のクエリは生産を打つ可能性が高いです。これらの変更EXPLAIN

キーは現在の周り180ms今改善している応答するlngカラムと平均時間を使用して示しています。

+0

私はこのような何かをしたいと思っています。最終的なストアドプロシージャを投稿できますか?私は前にストアドプロシージャを書いたことはありません。最初の質問には、静的なパラメータがあるようなコードがあります。myLat、myLon、および距離をストアドプロシージャに渡すにはどうすればいいですか?距離" – erik

答えて

2

その他のアイデアは喜んで受け入れられるでしょう!

スピード(単純さ)が必要な場合は、データベースからの適切な地理空間的サポートが必要です。これは、地理空間データの処理/構築/解析のための地理空間データ型、地理空間索引および(多くの)機能を導入する。

MySQL implements a part of the OpenGIS specifications(私がチェックしたのは前回)非常に非常に粗い/早すぎる(実際の作業には役に立たない)ですが、implements a part of the OpenGIS specificationsです。 PostgreSql

PostGisこれは自明簡単で読みやすくなります

(これは、ID 123を持つTABLEAのA点から千メートル、次に近いTableBのからすべてのポイントを見つける)

select 
    myvalue 
from 
    tablea, tableb 
where 
    st_dwithin(tablea.the_geom, tableb.the_geom, 1000) 
and 
    tablea.id = 123 
0

いくつかの考えパフォーマンスを向上させる物事を保守性の観点から単純化するものではありません(物事をより複雑にする)が、スケーラビリティに役立つかもしれない。あなたは、半径を知っているので

  1. 、あなたはデシベルは三角関数CALCSを行うことなく、いくつかの行を排除するために、クエリを最適化することを可能にするバウンディングボックスのための条件を追加することができます。

  2. 保存された場所の緯度/経度の三角値の一部を事前に計算し、テーブルに格納することができます。これは、レコードを挿入するときにパフォーマンスコストの一部をシフトさせますが、クエリが挿入数を上回ると、これは良い結果になります。このアプローチの考え方のためにこの回答を参照してください。

    Query to get records based on Radius in SQLite?

  3. あなたはgeohashingのようなもので見ることができます。

データベースで使用される場合、geohashedデータの構造は、2つの利点を有します。 ,,,第2に、この索引構造は、すばやく汚れた近接検索に使用できます。最も近い点は、最も近いジオハッシュの中にあることがよくあります。

あなたはどのように実装する上でいくつかのアイデアのためのSO検索することができ:最初のクエリはあなたが設定したパラメータを無視し https://stackoverflow.com/search?q=geohash

2

から1を使用しての代わりに、距離のため@dist、代わりにテーブルの別名origを使用してパラメータ@orig_latおよび@orig_lonのうちの1つである。

次に、テーブルとそれ自身との間にデカルト積を行うクエリがありますが、回避することはできません。フィルタ条件がorig.id = 1であるため、origの行がdestdest.id = 1の点を含めて; AND orig.id != dest.id)の行に結合されていることを意味します。 HAVING節もありますが、問題を示すGROUP BY節はありません。 HAVING句は集約を関連付けませんが、HAVING句は集約値を比較するためのものです(主に)。

私の記憶が私を失敗している場合を除き、COS(ABS(X))=== COS(x)は、あなたがABSをドロップすることで、物事を単純化することができるかもしれませんので、()。なぜなら、緯度はABSを必要とし、もう一方は球面三角法の問題では重要ではないことは明らかではありません。

あなたはマジックナンバーの用量を有する - 値69(赤道に、経度)程度でおそらくマイル数であり、3956は地球の半径です。

指定された位置がポールに近い場合、私は計算ボックスの不審なよ。極端な場合、経度をまったく許可する必要があるかもしれません。 2番目のクエリで

条件dest.id = 1は奇数です。私はそれを省略しなければならないと信じていますが、その条件に一致する行は1行だけなので、その存在は速くなるはずです。余分な時間は困惑しています。しかし、主キー索引を使用することは、書かれているように適切です。

あなたは、WHERE句の中にHAVING句で条件を移動する必要があります。

しかし、私は、これは本当に助けているかわからない...

+0

よく目撃され、abs()とWHERE節の間違いを2番目のものに気付かなかった!ありがとう、私はそれに応じて投稿を更新しました。 –

1

NGSオンライン逆測地電卓は伝統的な参照が地球楕円体上の任意の2つの位置の間の距離を計算することを意味している。

http://www.ngs.noaa.gov/cgi-bin/Inv_Fwd/inverse2.prl

しかし、計算上の

はまだ問題があります。特に2つのほぼ対蹠的な位置の間で、計算された距離は数十キロメートルの誤差を示すことができる!!!数値トラブルの起源はタデウス・ビンセンティー(92ページ)で、長い時間前に同定された。いずれの場合で

http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf

、チャールズKarneyで信頼性が高く、非常に正確なオンライン計算機を使用するpreferrableです:

http://geographiclib.sourceforge.net/cgi-bin/Geod

0

ごくわずかな距離にのみ興味がある場合は、地理的なグリッドを長方形のグリッドで近似できます。

SELECT *, SQRT(POWER(RADIANS(@mylat - dest.lat), 2) + 
       POWER(RADIANS(@mylon - dst.lng)*COS(RADIANS(@mylat)), 2) 
      )*@radiusOfEarth AS approximateDistance 
… 

あなたは、データベース内のラジアンの代わりに(またはそれに加えて)度を格納することで、これは、より効率的に作ることができます。あなたのクエリが180度の子午線を横切る場合、そこでは特別な注意が必要ですが、多くのアプリケーションはそれらの場所を扱う必要はありません。あなたはまた、より高速に計算する恐れがありますからPOWER(X) X*Xを変更しようとすることができます。

関連する問題