2017-07-07 5 views
0

私は、緯度、経度、および値からなるMySQLデータセットを持っています。私は、緯度と経度の座標が他の緯度と経度座標の指定された半径の範囲内にある値を合計しようとしています(それらを「焦点」と呼ぶ)。最も難しいのは、重なりのゾーンから別の座標を切り離そうとしていることです。たとえば、半径1が半径2と重なっている場合などです。SQL、地理座標、および半径を使用して重複のゾーンを照会

半径がある焦点のそれぞれには複数の「ゾーン」の半径があります任意の与えられた緯度/経度座標セットについて、要約するとかなり多くのことがあります。それは少し遅いですが、私は、一緒に主に働くクエリを置くために管理している:

Select 
      Sum(If(`zone`='z0_0x1_0',`value`,0)) as `z0_0x1_0`, 
      Sum(If(`zone`='z0_0x1_1',`value`,0)) as `z0_0x1_1`, 
      Sum(If(`zone`='z0_0x1_2',`value`,0)) as `z0_0x1_2`, 
      Sum(If(`zone`='z0_0x1_3',`value`,0)) as `z0_0x1_3`, 
      Sum(If(`zone`='z0_1x1_0',`value`,0)) as `z0_1x1_0`, 
      Sum(If(`zone`='z0_1x1_1',`value`,0)) as `z0_1x1_1`, 
      Sum(If(`zone`='z0_1x1_2',`value`,0)) as `z0_1x1_2`, 
      Sum(If(`zone`='z0_2x1_0',`value`,0)) as `z0_2x1_0`, 
      Sum(If(`zone`='z0_2x1_1',`value`,0)) as `z0_2x1_1`, 
      Sum(If(`zone`='z0_3x1_0',`value`,0)) as `z0_3x1_0`, 
      Sum(If(`zone`='z0_3x1_1',`value`,0)) as `z0_3x1_1`, 
      Sum(If(`zone`='z0_0',`value`,0)) as `z0_0`, 
      Sum(If(`zone`='z0_1',`value`,0)) as `z0_1`, 
      Sum(If(`zone`='z0_2',`value`,0)) as `z0_2`, 
      Sum(If(`zone`='z0_3',`value`,0)) as `z0_3`, 
      Sum(If(`zone`='z1_0',`value`,0)) as `z1_0`, 
      Sum(If(`zone`='z1_1',`value`,0)) as `z1_1`, 
      Sum(If(`zone`='z1_2',`value`,0)) as `z1_2`, 
      Sum(If(`zone`='z1_3',`value`,0)) as `z1_3` 
    From 
     (Select `lat`, `lng`, `value`, 
       Case 
          When ((`dist_0` Between 2.8723597844095 And 4.3343662110324) And (`dist_1` Between 3.6260179152491 And 5.4681062617155)) Then 'z0_0x1_0' 
          When ((`dist_0` Between 2.8723597844095 And 4.3343662110324) And (`dist_1` Between 2.1278369006061 And 3.6260179152491)) Then 'z0_0x1_1' 
          When ((`dist_0` Between 2.8723597844095 And 4.3343662110324) And (`dist_1` Between 1.3333495959677 And 2.1278369006061)) Then 'z0_0x1_2' 
          When ((`dist_0` Between 2.8723597844095 And 4.3343662110324) And (`dist_1` Between 0 And 1.3333495959677)) Then 'z0_0x1_3' 
          When ((`dist_0` Between 1.68658498678 And 2.8723597844095) And (`dist_1` Between 3.6260179152491 And 5.4681062617155)) Then 'z0_1x1_0' 
          When ((`dist_0` Between 1.68658498678 And 2.8723597844095) And (`dist_1` Between 2.1278369006061 And 3.6260179152491)) Then 'z0_1x1_1' 
          When ((`dist_0` Between 1.68658498678 And 2.8723597844095) And (`dist_1` Between 1.3333495959677 And 2.1278369006061)) Then 'z0_1x1_2' 
          When ((`dist_0` Between 1.0573158612197 And 1.68658498678) And (`dist_1` Between 3.6260179152491 And 5.4681062617155)) Then 'z0_2x1_0' 
          When ((`dist_0` Between 1.0573158612197 And 1.68658498678) And (`dist_1` Between 2.1278369006061 And 3.6260179152491)) Then 'z0_2x1_1' 
          When ((`dist_0` Between 0 And 1.0573158612197) And (`dist_1` Between 3.6260179152491 And 5.4681062617155)) Then 'z0_3x1_0' 
          When ((`dist_0` Between 0 And 1.0573158612197) And (`dist_1` Between 2.1278369006061 And 3.6260179152491)) Then 'z0_3x1_1' 
          When ((`dist_0` Between 2.8723597844095 And 4.3343662110324)) Then 'z0_0' 
          When ((`dist_0` Between 1.68658498678 And 2.8723597844095)) Then 'z0_1' 
          When ((`dist_0` Between 1.0573158612197 And 1.68658498678)) Then 'z0_2' 
          When ((`dist_0` Between 0 And 1.0573158612197)) Then 'z0_3' 
          When ((`dist_1` Between 3.6260179152491 And 5.4681062617155)) Then 'z1_0' 
          When ((`dist_1` Between 2.1278369006061 And 3.6260179152491)) Then 'z1_1' 
          When ((`dist_1` Between 1.3333495959677 And 2.1278369006061)) Then 'z1_2' 
          When ((`dist_1` Between 0 And 1.3333495959677)) Then 'z1_3' 
       End As `zone` 

       From 
        (Select `lat`, `lng`, `value`, 
         (acos(0.65292272498833*sin(radians(`lat`)) + 0.75742452772129*cos(radians(`lat`))*cos(radians(`lng`)-(-1.2910922519714))) * 6371) as `dist_0`, 
         (acos(0.65251345816785*sin(radians(`lat`)) + 0.75777713538338*cos(radians(`lat`))*cos(radians(`lng`)-(-1.2916315412569))) * 6371) as `dist_1` 
        From `pop` 
        Where 
         ((`lat` Between 40.714353892125 And 40.810300107875) And (`lng` Between -74.037474145971 And -73.910799854029)) Or 
         ((`lat` Between 40.673205922895 And 40.789544077105) And (`lng` Between -74.081798776797 And -73.928273223203)) 
        ) 
       As FirstCut 
     ) 
     As Zonecut 

はここのもののロジックです:

  1. まず、それが最大半径の周りにバウンディングボックスをつかみます各焦点ごとにこれはFirstCutクエリーです。これにより、見ているデータポイントの数が数桁減少します。

  2. そしてそのデータの全てを処理し、この場合には(焦点から各データポイントの距離を取得し、dist_0dist_1が、焦点の任意の数が存在することができる - 私はこの例では2を使用していますどのように動作するかを示す)。これは大きな円距離のHaversine式です。

  3. 次に、各座標に「ゾーン」のメンバーを割り当てて、最も複雑なものから最も複雑なものまで処理します。ゾーンコードは「ゾーンX、半径Y」を意味するので、「z0_1」は「ゾーン0、半径1」を意味します。 「x」がある場合、それは複数のゾーンの交差点であることを意味します。この「ゾーンコード」は文字列として割り当てられます。

  4. 最後に、ゾーンコードを使用して、ゾーン名を割り当て、次にそれらのSum(If())ステートメントを割り当てます。 (何らかの理由で、もし()。ここで)(ケースよりもわずかに速く動作しているようです)私のスクリプト(PHP)ゾーンと和のリストに出力し

。実際には、実際に「ヒット」を持つ可能性のあるゾーンをすべて計算しなければならないため、この処理全体が手続き的に生成されることは明らかです。これはSQLでの処理を避けるために前処理として行われます。

もっと巧妙な方法がありますか?私はそれらに文字列を割り当て、フィールドにその文字列をフィルタリングするビット...それは非常にエレガントではない、ハッキーに見えます。しかし、私は1つの大文字小文字のステートメントにフィールドを分類するためのより良い方法を見つけることができませんでした(これは多くのケースステートメントよりもはるかに高速です)。

これに関するすべてのフィードバックは高く評価されます。 MySQLテーブルは大量(数百万行)で、すべての聖なる地獄にインデックスされています。上記のクエリーを実行するには約0.6秒かかりますが、それほど悪くはありませんが、より多くのフォーカルポイントが追加されるにつれて、クエリーがより長くかかり始めます。この段階でSQLロジックを考えようとしています。ありがとうございました。

答えて

1

私は徹底的にそれをチェックしませんでしたが、これはその大きなCASEいくつかの短縮ができようにそれはそう:

CONCAT(
    (CASE 
     WHEN (dist_0 ...) THEN 'z0_0' 
     WHEN (dist_0 ...) THEN 'z0_1' 
     ... 
     ELSE ''), 
    (CASE 
     WHEN (dist_1 ...) THEN 'z1_0' 
     WHEN (dist_1 ...) THEN 'z1_1' 
     ... 
     ELSE '')) AS zone 
+0

ああ - それは非常に賢いです。それが実現できるかどうかがわかります。私はそれが働くことができると思います。 – nucleon

関連する問題