Tenho uma tabela que contém 10.301.390 registros de GPS, cidades, países e blocos de endereços IP. Eu tenho a localização atual do usuário com latitude e longitude. Eu criei esta consulta:
SELECT
*, point(45.1013021, 46.3021011) <@> point(latitude, longitude) :: point AS distance
FROM
locs
WHERE
(
point(45.1013021, 46.3021011) <@> point(latitude, longitude)
) < 10 -- radius
ORDER BY
distance LIMIT 1;
Esta consulta me deu com sucesso o que eu quero, mas é lento. Demorou 2 a 3 segundos para obter um registro por determinada latitude e longitude.
Eu tentei um índice B-Tree nas colunas latitude
e , também tentei, mas ainda assim a consulta é lenta.longitude
GIST( point(latitude, longitude));
Como posso acelerar esta consulta?
Atualizar:
Parece que a lentidão é causada pelo ORDER BY
mas eu quero obter a distância mais curta, então a questão permanece.
Você pode considerar o uso de um índice GIST com base no uso da função
ll_to_earth
. Este índice permitirá pesquisas "próximas" rápidas.Depois de ter esse índice, sua consulta deve ser feita de uma maneira diferente.
Seus pares (lat, lng) precisam ser convertidos para o
earth
tipo e comparados com os valores indexados (que são do mesmo tipo). Sua consulta precisará ter duas condições, uma para o resultado "aproximado" e outra para o "preciso". O primeiro poderá usar o índice anterior:Para usar este código, você precisa de duas extensões (incluídas na maioria das distribuições do PostgreSQL):
Esta é a documentação para eles:
earth_box
eearth_distance
. Este módulo assume que a Terra é esférica, o que é uma aproximação boa o suficiente para a maioria das aplicações.Um teste com uma tabela composta por 2,2 milhões de linhas retiradas do Free World Cities Database me dá a seguinte resposta para a consulta anterior (que não é exatamente a mesma que a sua):
Para ter uma ideia de "ordem de grandeza" sobre temporizações: pgAdmin III está me dizendo que o tempo para obter esta resposta é 22 ms. (PostgreSQL 9.6.1 com parâmetros "out-of-the-box", em um Mac com Mac OS 10.12, Core i7, SSD)
Resposta alternativa com PostGIS
Se você estiver usando 10 milhões de linhas. Você provavelmente precisará intensificar e atualizar para o PostGIS.
geometery(point)::geography
, ou se você armazenar em lat/long você pode usarST_MakePoint
ST_DWithin
. Esta função irá um índice (se você criar um).ST_Distance
pontos na caixa delimitadoraAqui está o sinal para ST_DWithin,
Ele pode medir sua distância ao longo do esferóide ou esfera.