Oracle
 sql >> Database >  >> RDS >> Oracle

Ricerca spaziale Oracle a distanza

Hai un buon riferimento per la ricerca a distanza di MySQL.

Dimentica le cose di Oracle Spatial. Troppo codice, troppa complessità, non abbastanza valore aggiunto.

Ecco una query che farà il trucco. Questo utilizza le distanze in miglia statutarie. MODIFICA Questo risolve il bug menzionato da mdarwin, a costo del controllo della divisione se provi a usarlo per una posizione al polo nord o sud.

  SELECT id, city, LATITUDE, LONGITUDE, distance
    FROM
  (
    SELECT id, 
           city, 
           LATITUDE, LONGITUDE,
           (3959 * ACOS(COS(RADIANS(LATITUDE)) 
                 * COS(RADIANS(mylat)) 
                 * COS(RADIANS(LONGITUDE) - RADIANS(mylng)) 
                 + SIN(RADIANS(LATITUDE)) 
                 * SIN(RADIANS(mylat)) 
               ))
           AS distance,
           b.mydst
      FROM Cities
      JOIN (
        SELECT :LAT AS mylat,
               :LONG AS mylng,
               :RADIUS_LIMIT AS mydst
          FROM DUAL
      )b ON (1 = 1)
     WHERE LATITUDE >=  mylat -(mydst/69)
       AND LATITUDE <=  mylat +(mydst/69)
       AND LONGITUDE >= mylng -(mydst/(69 * COS(RADIANS(mylat))))
       AND LONGITUDE <= mylng +(mydst/(69 * COS(RADIANS(mylat))))
  )a
   WHERE distance <= mydst
   ORDER BY distance

Se stai lavorando in chilometri, cambia mydst/69 in mydst/111.045 e cambia 3959 in 6371.4. (1/69 converte le miglia in gradi; 3959 è un valore per il raggio del pianeta.)

Ora, probabilmente sarai tentato di utilizzare questa query di grandi dimensioni come una "scatola nera magica". Non farlo! Non è molto difficile da capire, e se lo capisci sarai in grado di fare un lavoro migliore. Ecco cosa sta succedendo.

Questa clausola è il cuore di ciò che rende la query veloce. Cerca nella tua tabella Città le città vicine fino al punto specificato.

     WHERE LATITUDE >=  mylat -(mydst/69)
       AND LATITUDE <=  mylat +(mydst/69)
       AND LONGITUDE >= mylng -(mydst/(69 * COS(RADIANS(mylat))))
       AND LONGITUDE <= mylng +(mydst/(69 * COS(RADIANS(mylat))))

Affinché funzioni, hai sicuramente bisogno di un indice nella colonna LATITUDE. Anche un indice sulla colonna LONGITUDE aiuterà un po'. Esegue una ricerca approssimativa, cercando righe che si trovano all'interno di una macchia quasi rettangolare sulla superficie della terra vicino al tuo punto. Seleziona troppe città, ma non troppe.

Questa clausola qui ti consente di eliminare le città extra dal tuo set di risultati:

   WHERE distance <= mydst

Questa clausola è la formula haversine che calcola la distanza del grande cerchio tra ogni città e il tuo punto.

           (3959 * ACOS(COS(RADIANS(LATITUDE)) 
                 * COS(RADIANS(mylat)) 
                 * COS(RADIANS(LONGITUDE) - RADIANS(mylng)) 
                 + SIN(RADIANS(LATITUDE)) 
                 * SIN(RADIANS(mylat)) 

Questa clausola ti consente di inserire il tuo punto e il tuo limite di raggio solo una volta come variabili associate alla tua query. È utile perché le varie formule utilizzano queste variabili più volte.

        SELECT :LAT AS mylat,
               :LONG AS mylng,
               :RADIUS_LIMIT AS mydst
          FROM DUAL

Il resto della query organizza semplicemente le cose in modo da selezionare e ordinare in base alla distanza.

Ecco una spiegazione più completa:http://www.plumislandmedia.net /mysql/haversine-mysql-loc-più vicino/