2017-07-07 55 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,但可以有焦點的任意數量的 - 我已經在這個例子中使用了兩個剛顯示它是如何工作的)。這是大圈距離的Haversine公式。

  3. 然後,Case語句啓動,爲每個座標指定一個「區域」的成員,這些成員從最複雜到最不復雜的處理。區域代碼僅意味着「區域X,半徑Y」 - 因此「z0_1」表示「區域0,半徑1」。如果存在「x」,則表示它是多個區域的交集。這個「區碼」只是作爲一個字符串分配的。

  4. 最後,通過分配區域名稱和Sum(If())語句,區域代碼用於總結所有內容。 (不管出於什麼原因,如果()似乎比案件工作稍快()在這裏。)

其中輸出到我的腳本(PHP)區和款項的清單。現在顯然這整個過程是程序化生成的,因爲你必須提前計算所有可能實際上會「命中」的區域,並且這些都是作爲預處理完成的,以避免在SQL中執行。

有沒有更聰明的方法來做到這一點?我給他們分配一個字符串的位,然後將該字符串過濾到字段中......它看起來很黑,並不很優雅。但我無法找到一種更好的方法,將它們分類爲一個大的Case語句中的字段(它似乎比許多Case語句快得多)。

任何和所有這方面的反饋將不勝感激。 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