Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Pesquisa espacial Oracle à distância


Você tem uma referência muito boa para pesquisa de distância do mySQL.

Esqueça as coisas do Oracle Spatial. Muito código, muita complexidade, pouco valor agregado.

Aqui está uma consulta que fará o truque. Isso usa distâncias em milhas terrestres. EDITAR Isso corrige o bug mencionado pelo mdarwin, ao custo da verificação de divisão se você tentar usá-lo para um local no pólo norte ou sul.
  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 você estiver trabalhando em quilômetros, altere mydst/69 para mydst/111.045 e altere 3959 para 6371,4. (1/69 converte milhas em graus; 3959 é um valor para o raio do planeta.)

Agora, você provavelmente ficará tentado a usar essa grande consulta como uma "caixa preta mágica". Não faça isso! Não é muito difícil de entender, e se você entender, poderá fazer um trabalho melhor. Aqui está o que está acontecendo.

Esta cláusula é o coração do que torna a consulta rápida. Ele pesquisa na sua tabela Cidades por cidades próximas ao ponto que você especificou.
     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))))

Para que funcione, você definitivamente precisa de um índice em sua coluna LATITUDE. Um índice em sua coluna LONGITUDE também ajudará um pouco. Ele faz uma busca aproximada, procurando por linhas que estejam dentro de uma mancha quase retangular na superfície da Terra perto do seu ponto. Ele seleciona muitas cidades, mas não muitas.

Esta cláusula aqui permite que você elimine as cidades extras do seu conjunto de resultados:
   WHERE distance <= mydst

Esta cláusula é a fórmula de Haversine que calcula a distância do grande círculo entre cada cidade e seu ponto.
           (3959 * ACOS(COS(RADIANS(LATITUDE)) 
                 * COS(RADIANS(mylat)) 
                 * COS(RADIANS(LONGITUDE) - RADIANS(mylng)) 
                 + SIN(RADIANS(LATITUDE)) 
                 * SIN(RADIANS(mylat)) 

Esta cláusula permite que você insira seu ponto e seu limite de raio apenas uma vez como variáveis ​​vinculadas à sua consulta. É útil porque as várias fórmulas usam essas variáveis ​​várias vezes.
        SELECT :LAT AS mylat,
               :LONG AS mylng,
               :RADIUS_LIMIT AS mydst
          FROM DUAL

O restante da consulta simplesmente organiza as coisas para que você selecione e ordene por distância.

Aqui está uma explicação mais completa:http://www.plumislandmedia.net /mysql/haversine-mysql-nearest-loc/