Atualmente, tenho pouco menos de um milhão de locais em um banco de dados mysql, todos com informações de longitude e latitude.
Estou tentando encontrar a distância entre um ponto e muitos outros pontos através de uma consulta. Não é tão rápido quanto eu quero, especialmente com mais de 100 hits por segundo.
Existe uma consulta mais rápida ou possivelmente um sistema mais rápido que não seja o mysql para isso? Estou usando esta consulta:
SELECT
name,
( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) )
* cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763))
* sin( radians(locations.lat)))) AS distance
FROM locations
WHERE active = 1
HAVING distance < 10
ORDER BY distance;
Nota: A distância fornecida é em milhas . Se você precisar de Quilômetros , use em 6371
vez de 3959
.
Respostas:
Crie seus pontos usando
Point
valores deGeometry
tipos de dados naMyISAM
tabela. A partir do Mysql 5.7.5, asInnoDB
tabelas agora também suportamSPATIAL
índices.Crie um
SPATIAL
índice nesses pontosUse
MBRContains()
para encontrar os valores:ou
MySQL 5.1
acima e abaixo:Isso selecionará todos os pontos aproximadamente dentro da caixa
(@lat +/- 10 km, @lon +/- 10km)
.Na verdade, isso não é uma caixa, mas um retângulo esférico: segmento da esfera de latitude e longitude. Isso pode diferir de um retângulo simples na Terra Franz Joseph , mas bastante próximo a ele na maioria dos lugares habitados.
Aplique filtragem adicional para selecionar tudo dentro do círculo (não o quadrado)
Possivelmente aplique filtragem fina adicional para levar em consideração a grande distância do círculo (para grandes distâncias)
fonte
@lon - 10 / ( 111.1 / cos(@lat))
(e seja a segunda no par quando tudo estiver correto).cos(lon)
é preciso apenas para distâncias pequenas. Veja janmatuschek.de/LatitudeLongitudeBoundingCoordinates111.(1)
km em um grau de latitude.mypoint
é o campo na tabela que armazena as coordenadas.Não é uma resposta específica do MySql, mas melhorará o desempenho da sua instrução sql.
O que você está efetivamente fazendo é calcular a distância de cada ponto da tabela, para ver se está dentro de 10 unidades de um determinado ponto.
O que você pode fazer antes de executar este sql é criar quatro pontos que desenham uma caixa de 20 unidades de um lado, com o seu ponto no centro, ou seja. (x1, y1). . . (x4, y4), onde (x1, y1) é (dado + 10 unidades, dado + Lat + 10 unidades). . . (determinadoLongo - 10 unidades, dadoLat -10 unidades). Na verdade, você só precisa de dois pontos, superior esquerdo e inferior direito, chame-os (X1, Y1) e (X2, Y2)
Agora, sua instrução SQL usa esses pontos para excluir linhas que definitivamente são mais de 10u do seu ponto especificado, ela pode usar índices nas latitudes e longitudes, portanto haverá ordens de magnitude mais rápidas do que as que você possui atualmente.
por exemplo
A abordagem da caixa pode retornar falsos positivos (você pode pegar pontos nos cantos da caixa que são> 10u a partir do ponto especificado), portanto, você ainda precisa calcular a distância de cada ponto. No entanto, isso novamente será muito mais rápido, porque você limitou drasticamente o número de pontos a serem testados nos pontos dentro da caixa.
Eu chamo essa técnica de "Pensar dentro da caixa" :)
Edição: isso pode ser colocado em uma instrução SQL?
Não tenho idéia do que o mySql ou Php é capaz, desculpe. Eu não sei onde o melhor lugar é construir os quatro pontos, ou como eles podem ser passados para uma consulta mySql em Php. No entanto, depois de ter os quatro pontos, não há nada que o impeça de combinar sua própria instrução SQL com a minha.
Eu sei que com o MS SQL eu posso criar uma instrução SQL que declara quatro carros alegóricos (X1, Y1, X2, Y2) e os calcula antes da instrução de seleção "principal", como eu disse, não tenho idéia se isso pode ser feito com MySql. No entanto, eu ainda estaria inclinado a criar os quatro pontos em C # e passá-los como parâmetros para a consulta SQL.
Desculpe, não posso ajudar mais, se alguém puder responder a partes específicas do MySQL e Php, sinta-se à vontade para editar essa resposta.
fonte
A seguinte função MySQL foi publicada nesta postagem do blog . Não testei muito, mas pelo que coletei na postagem, se seus campos de latitude e longitude estiverem indexados , isso poderá funcionar bem para você:
Uso da amostra:
Assumindo uma tabela chamada
places
com camposlatitude
&longitude
:fonte
SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS distance
Eu precisava resolver um problema semelhante (filtrando linhas pela distância do ponto único) e, combinando a pergunta original com respostas e comentários, criei uma solução que funciona perfeitamente para mim no MySQL 5.6 e 5.7.
coordinates
é campo com tipoPOINT
e temSPATIAL
índice6371
é para calcular distância em quilômetros56.946285
é latitude para ponto central24.105078
é longitude para ponto central15
é distância máxima em quilômetrosNos meus testes, o MySQL usa o índice SPATIAL no
coordinates
campo para selecionar rapidamente todas as linhas que estão dentro do retângulo e depois calcula a distância real de todos os locais filtrados para excluir locais dos cantos dos retângulos e deixar apenas locais dentro do círculo.Esta é a visualização do meu resultado:
Estrelas cinzas visualizam todos os pontos no mapa, estrelas amarelas são aquelas retornadas pela consulta do MySQL. Estrelas cinzas dentro dos cantos do retângulo (mas fora do círculo) foram selecionadas
MBRContains()
e desmarcadas pelaHAVING
cláusula.fonte
se você estiver usando o MySQL 5.7. *, poderá usar st_distance_sphere (POINT, POINT) .
fonte
Esta é a consulta de cálculo de distância entre os pontos no MySQL, eu a usei em um banco de dados longo, funcionando perfeitamente! Nota: faça as alterações (nome do banco de dados, nome da tabela, coluna etc.) conforme seus requisitos.
fonte
fonte
fonte
fonte
Uma função MySQL que retorna o número de metros entre as duas coordenadas:
Para retornar o valor em um formato diferente, substitua o
6371000
na função pelo raio da Terra em sua unidade escolhida. Por exemplo, quilômetros seriam6371
e milhas seriam3959
.Para usar a função, basta chamá-la como faria com qualquer outra função no MySQL. Por exemplo, se você tivesse uma mesa
city
, poderia encontrar a distância entre todas as cidades e todas as outras cidades:fonte
O código completo com detalhes sobre como instalar como plugin do MySQL está aqui: https://github.com/lucasepe/lib_mysqludf_haversine
Eu publiquei este ano passado como comentário. Desde que gentilmente @TylerCollier me sugeriu postar como resposta, aqui está.
Outra maneira é escrever uma função UDF personalizada que retorne a distância do haversine de dois pontos. Esta função pode receber entrada:
Então, podemos escrever algo como isto:
buscar todos os registros a uma distância menor que 40 quilômetros. Ou:
buscar todos os registros com uma distância inferior a 25 pés.
A função principal é:
fonte
Uma aproximação rápida, simples e precisa (para distâncias menores) pode ser feita com uma projeção esférica . Pelo menos no meu algoritmo de roteamento, recebo um aumento de 20% em comparação com o cálculo correto. No código Java, ele se parece com:
Não tenho certeza sobre o MySQL (desculpe!).
Certifique-se de conhecer a limitação (o terceiro parâmetro de assertEquals significa a precisão em quilômetros):
fonte
Aqui está uma descrição muito detalhada da Geo Distance Search com MySQL, uma solução baseada na implementação da Haversine Formula no mysql. A descrição completa da solução com teoria, implementação e otimização de desempenho adicional. Embora a parte de otimização espacial não funcionou corretamente no meu caso. http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
fonte
Leia a Pesquisa por distância geográfica com o MySQL , uma solução baseada na implementação da Haversine Formula no MySQL. Esta é uma descrição completa da solução com teoria, implementação e otimização de desempenho adicional. Embora a parte de otimização espacial não funcione corretamente no meu caso.
Notei dois erros nisso:
o uso de
abs
na instrução select na p8. Eu apenas omitiabs
e funcionou.a função de distância de busca espacial na p27 não converte em radianos ou multiplica a longitude por
cos(latitude)
, a menos que seus dados espaciais sejam carregados com isso em consideração (não é possível distinguir do contexto do artigo), mas seu exemplo na p26 indica que seus dados espaciaisPOINT
não são carregados com radianos ou graus.fonte
fonte
Usando o mysql
Veja: https://andrew.hedges.name/experiments/haversine/
Consulte: https://stackoverflow.com/a/24372831/5155484
Veja: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
NOTA:
LEAST
é usado para evitar valores nulos como um comentário sugerido em https://stackoverflow.com/a/24372831/5155484fonte