Oracle
 sql >> Base de données >  >> RDS >> Oracle

Recherche spatiale Oracle à distance

Vous avez là une assez bonne référence pour la recherche à distance mySQL.

Oubliez les trucs d'Oracle Spatial. Trop de code, trop de complexité, pas assez de valeur ajoutée.

Voici une requête qui fera l'affaire. Cela utilise des distances en milles terrestres. MODIFIER Cela corrige le bogue mentionné par mdarwin, au prix d'une vérification de division si vous essayez de l'utiliser pour un emplacement au pôle nord ou 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

Si vous travaillez en kilomètres, changez mydst/69 en mydst/111.045 et changez 3959 en 6371.4. (1/69 convertit les miles en degrés ; 3959 est une valeur pour le rayon de la planète.)

Maintenant, vous serez probablement tenté d'utiliser cette grande requête comme une "boîte noire magique". Ne le faites pas ! Ce n'est pas très difficile à comprendre, et si vous le comprenez, vous serez en mesure de faire un meilleur travail. Voici ce qui se passe.

Cette clause est au cœur de ce qui rend la requête rapide. Il recherche dans votre table Cities les villes voisines jusqu'au point que vous avez spécifié.

     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))))

Pour que cela fonctionne, vous avez certainement besoin d'un index sur votre colonne LATITUDE. Un index sur votre colonne LONGITUDE aidera également un peu. Il effectue une recherche approximative, recherchant les lignes qui se trouvent dans une zone quasi rectangulaire à la surface de la terre près de votre point. Il sélectionne trop de villes, mais pas beaucoup trop.

Cette clause vous permet d'éliminer les villes supplémentaires de votre jeu de résultats :

   WHERE distance <= mydst

Cette clause est la formule haversine qui calcule la distance orthodromique entre chaque ville et votre point.

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

Cette clause vous permet d'entrer votre point et votre limite de rayon une seule fois en tant que variables liées à votre requête. C'est utile car les différentes formules utilisent ces variables plusieurs fois.

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

Le reste de la requête organise simplement les choses afin que vous sélectionniez et triiez par distance.

Voici une explication plus complète :http://www.plumislandmedia.net /mysql/haversine-mysql-nearest-loc/