Tendo em mente que executarei cálculos em pares lat / long, que tipo de dados é mais adequado para uso com um banco de dados MySQL?
Tendo em mente que executarei cálculos em pares lat / long, que tipo de dados é mais adequado para uso com um banco de dados MySQL?
Respostas:
Use as extensões espaciais do MySQL com GIS.
O Google fornece uma solução PHP / MySQL do início ao fim para um exemplo de aplicativo "Localizador de lojas" no Google Maps. Neste exemplo, eles armazenam os valores de lat / lng como "Float" com um comprimento de "10,6"
FLOAT(10,6)
deixa 4 dígitos para a parte inteira da coordenada. E não, o sinal não conta - isso vem do atributo (des) assinado.
Double
de dados para o Laravel
Basicamente, depende da precisão que você precisa para seus locais. Usando DOUBLE, você terá uma precisão de 3,5 nm. DECIMAL (8,6) / (9,6) desce para 16 cm. FLOAT é de 1,7 m ...
Esta tabela muito interessante possui uma lista mais completa: http://mysql.rjweb.org/doc.php/latlng :
Datatype Bytes Resolution
Deg*100 (SMALLINT) 4 1570 m 1.0 mi Cities
DECIMAL(4,2)/(5,2) 5 1570 m 1.0 mi Cities
SMALLINT scaled 4 682 m 0.4 mi Cities
Deg*10000 (MEDIUMINT) 6 16 m 52 ft Houses/Businesses
DECIMAL(6,4)/(7,4) 7 16 m 52 ft Houses/Businesses
MEDIUMINT scaled 6 2.7 m 8.8 ft
FLOAT 8 1.7 m 5.6 ft
DECIMAL(8,6)/(9,6) 9 16cm 1/2 ft Friends in a mall
Deg*10000000 (INT) 8 16mm 5/8 in Marbles
DOUBLE 16 3.5nm ... Fleas on a dog
Espero que isto ajude.
As extensões espaciais do MySQL são a melhor opção porque você tem a lista completa de operadores e índices espaciais à sua disposição. Um índice espacial permitirá realizar cálculos baseados em distância muito rapidamente. Lembre-se de que, a partir da versão 6.0, a extensão espacial ainda está incompleta. Não estou descartando o MySQL Spatial, apenas informando sobre as armadilhas antes que você se demore demais nisso.
Se você estiver lidando estritamente com pontos e apenas com a função DISTANCE, tudo bem. Se você precisar fazer cálculos com polígonos, linhas ou pontos de buffer, os operadores espaciais não fornecerão resultados exatos, a menos que você use o operador "relacionar". Veja o aviso na parte superior de 21.5.6 . Relacionamentos como contém, dentro ou interseções estão usando o MBR, não a forma exata da geometria (ou seja, um Ellipse é tratado como um retângulo).
Além disso, as distâncias no MySQL Spatial estão nas mesmas unidades que sua primeira geometria. Isso significa que, se você estiver usando graus decimais, suas medidas de distância serão em graus decimais. Isso tornará muito difícil obter resultados exatos à medida que você se aproxima do equador.
Quando fiz isso para um banco de dados de navegação construído a partir do ARINC424, fiz uma boa quantidade de testes e, olhando para o código, usei um DECIMAL (18,12) (atualmente um NUMERIC (18,12) porque era o firebird).
Flutuadores e duplos não são tão precisos e podem resultar em erros de arredondamento, o que pode ser uma coisa muito ruim. Não me lembro se encontrei dados reais com problemas - mas tenho certeza de que a incapacidade de armazenar com precisão em um float ou double pode causar problemas
O ponto é que, ao usar graus ou radianos, sabemos a faixa dos valores - e a parte fracionária precisa de mais dígitos.
As extensões espaciais do MySQL são uma boa alternativa, porque seguem o OpenGIS Geometry Model . Não os usei porque precisava manter meu banco de dados portátil.
a*b
não era igual b*a
(para alguns valores). Houve muitos exemplos um pouco como: 2+2 = 3.9999
. O padrão limpava muita bagunça e era "rapidamente" adotado por praticamente todas as peças de hardware e software. Portanto, essa discussão é válida, não apenas desde 2008, mas por um terço de século.
Depende da precisão que você precisa.
Datatype Bytes resolution
------------------ ----- --------------------------------
Deg*100 (SMALLINT) 4 1570 m 1.0 mi Cities
DECIMAL(4,2)/(5,2) 5 1570 m 1.0 mi Cities
SMALLINT scaled 4 682 m 0.4 mi Cities
Deg*10000 (MEDIUMINT) 6 16 m 52 ft Houses/Businesses
DECIMAL(6,4)/(7,4) 7 16 m 52 ft Houses/Businesses
MEDIUMINT scaled 6 2.7 m 8.8 ft
FLOAT 8 1.7 m 5.6 ft
DECIMAL(8,6)/(9,6) 9 16cm 1/2 ft Friends in a mall
Deg*10000000 (INT) 8 16mm 5/8 in Marbles
DOUBLE 16 3.5nm ... Fleas on a dog
From: http://mysql.rjweb.org/doc.php/latlng
Para resumir:
DOUBLE
.DECIMAL(8,6)/(9,6)
.No MySQL 5.7 , considere o uso de Tipos de Dados Espaciais (SDT), especificamente POINT
para armazenar uma única coordenada. Antes do 5.7, o SDT não suporta índices (com exceção de 5.6 quando o tipo de tabela é MyISAM).
Nota:
POINT
classe, a ordem dos argumentos para armazenar coordenadas deve ser POINT(latitude, longitude)
.ST_Distance
) e determinando se um ponto está contido em outra área ( ST_Contains
).CREATE TABLE geom (g GEOMETRY NOT NULL, SPATIAL INDEX(g)) ENGINE=MyISAM;
o aviso sobre as limitações do SDT, como James mencionou , talvez sua resposta seja mais concisa e precisa para ajudar outras pessoas também. ..
Com base neste artigo da wiki http://en.wikipedia.org/wiki/Decimal_degrees#Accuracy, o tipo de dados apropriado no MySQL é Decimal (9,6) para armazenar a longitude e latitude em campos separados.
Use DECIMAL(8,6)
para latitude (90 a -90 graus) e DECIMAL(9,6)
longitude (180 a -180 graus). 6 casas decimais é bom para a maioria dos aplicativos. Ambos devem ser "assinados" para permitir valores negativos.
DECIMAL
O tipo é destinado a cálculos financeiros em que nenhum floor/ceil
é aceito. Plain FLOAT
supera significativamente DECIMAL
.
Não é preciso ir muito longe, de acordo com o Google Maps, o melhor é o FLOAT (10,6) para lat e lng.
lat FLOAT( 10, 6 ) NOT NULL,
lng FLOAT( 10, 6 ) NOT NULL
FLOAT
sintaxe está obsoleta a partir de mysql 8.0.17
. O Mysql agora recomenda o uso FLOAT
sem parâmetros de precisão dev.mysql.com/doc/refman/8.0/en/numeric-type-overview.html e dev.mysql.com/doc/refman/5.5/en/floating-point- types.html
Armazenamos latitude / longitude X 1.000.000 em nosso banco de dados oracle como NUMBERS para evitar erros de arredondamento com dobras.
Dado que a latitude / longitude até a sexta casa decimal era de 10 cm de precisão, tudo o que precisávamos. Muitos outros bancos de dados também armazenam lat / long até a sexta casa decimal.
Em uma perspectiva completamente diferente e mais simples:
VARCHAR
), por exemplo: " -0000.0000001, -0000.000000000000001 " (comprimento 35 e se um número tiver mais de 7 dígitos decimais, ele será arredondado);google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
Dessa forma, você não precisa se preocupar com a indexação de números e com todos os outros problemas associados aos tipos de dados que podem estragar suas coordenadas.
dependendo da sua aplicação, sugiro usar o FLOAT (9,6)
as teclas espaciais fornecerão mais recursos, mas nos benchmarks de produção os carros alegóricos são muito mais rápidos que as chaves espaciais. (0,01 VS 0,001 no AVG)
O MySQL usa o dobro para todos os carros alegóricos ... Então use o tipo double. Usar float levará a valores arredondados imprevisíveis na maioria das situações
DOUBLE
. O MySQL permite que você armazene dados como 4 FLOAT
ou 8 bytes DOUBLE
. Portanto, é provável que haja uma perda de precisão ao armazenar uma expressão em uma FLOAT
coluna.
Embora não seja ideal para todas as operações, se você estiver criando blocos de mapas ou trabalhando com um grande número de marcadores (pontos) com apenas uma projeção (por exemplo, Mercator, como o Google Maps e muitas outras estruturas de mapas esperadas), eu descobri o que Eu chamo de "vasto sistema de coordenadas" para ser muito, muito útil. Basicamente, você armazena as coordenadas x e y de pixel com algum zoom - eu uso o nível de zoom 23. Isso tem vários benefícios:
Falei sobre tudo isso em uma postagem recente do blog: http://blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/
Estou muito surpreso com algumas respostas / comentários.
Por que diabos alguém estaria disposto a voluntariamente "pré-diminuir" a precisão e depois realizar cálculos com números piores? Parece estúpido.
Se a fonte tiver precisão de 64 bits, certamente seria estúpido fixar voluntariamente a escala para, por exemplo. 6 decimais e limite a precisão a um máximo de 9 dígitos significativos (o que acontece com o formato 9.6 decimal proposto com frequência).
Naturalmente, os dados são armazenados com a precisão que o material de origem possui. O único motivo para diminuir a precisão seria o espaço de armazenamento limitado.
O formato decimal 9.6 causa um fenômeno de encaixe na grade. Esse deve ser o último passo, se é que deve acontecer.
Eu não convidaria erros acumulados para o meu ninho.
TL; DR
Use FLOAT (8,5) se você não estiver trabalhando na NASA / militar e não estiver fabricando sistemas navi de aeronaves.
Para responder sua pergunta completamente, você precisa considerar várias coisas:
Formato
Portanto, a primeira parte da resposta seria: você pode armazenar as coordenadas no formato que seu aplicativo usa para evitar conversões constantes e fazer consultas SQL mais simples.
Provavelmente você usa o Google Maps ou OSM para exibir seus dados, e o GMaps está usando o formato "graus decimais 2". Portanto, será mais fácil armazenar coordenadas no mesmo formato.
Precisão
Em seguida, você deseja definir a precisão necessária. Claro que você pode armazenar coordenadas como "-32.608697550570334,21.278081997935146", mas você já se importou com milímetros enquanto navegava até o ponto? Se você não trabalha na NASA e não faz trajetórias de satélites, foguetes ou aviões, deve ficar bem com vários metros de precisão.
O formato comumente usado é de 5 dígitos após pontos, o que fornece 50 cm de precisão.
Exemplo : existe uma distância de 1 cm entre X, 21.278081 8 e X, 21.278081 9 . Portanto, 7 dígitos após o ponto fornecem precisão de 1/2 cm e 5 dígitos após o ponto fornecem precisão de 1/2 metro (porque a distância mínima entre pontos distintos é de 1m, o erro de arredondamento não pode ser mais da metade). Para a maioria dos propósitos civis, deve ser suficiente.
O formato de graus decimais em minutos (40 ° 26.767 ′ N 79 ° 58.933 ′ W) fornece exatamente a mesma precisão que 5 dígitos após o ponto
Armazenamento com espaço eficiente
Se você selecionou o formato decimal, sua coordenada é um par (-32.60875, 21.27812). Obviamente, 2 x (1 bit para sinal, 2 dígitos para graus e 5 dígitos para expoente) será suficiente.
Então, aqui gostaria de apoiar Alix Axel a partir de comentários dizendo que a sugestão do Google para armazená-lo no FLOAT (10,6) é realmente extra, porque você não precisa de 4 dígitos para a parte principal (já que o sinal é separado e a latitude é limitada) a 90 e a longitude é limitada a 180). Você pode facilmente usar FLOAT (8,5) para precisão de 1 / 2m ou FLOAT (9,6) para precisão de 50 / 2cm. Ou você pode até armazenar lat e long em tipos separados, porque FLOAT (7,5) é suficiente para lat. Consulte a referência de tipos de float do MySQL. . Qualquer um deles será como FLOAT normal e igual a 4 bytes de qualquer maneira.
Normalmente, o espaço não é um problema hoje em dia, mas se você realmente deseja otimizar o armazenamento por algum motivo (Isenção de responsabilidade: não faça pré-otimização), pode compactar lat (não mais que 91.000 valores + sinal) + longo (não mais de 181 000 valores + sinal) a 21 bits, significativamente menor que 2xFLOAT (8 bytes == 64 bits)
As latitudes variam de -90 a +90 (graus), portanto DECIMAL (10, 8) é aceitável para isso.
as longitudes variam de -180 a +180 (graus), então você precisa de DECIMAL (11, 8).
Nota: O primeiro número é o número total de dígitos armazenados e o segundo é o número após o ponto decimal.
Em resumo: lat DECIMAL(10, 8) NOT NULL, lng DECIMAL(11, 8) NOT NULL
Eu sugiro que você use o tipo de dados Float para SQL Server.
Os cálculos de Lat Long requerem precisão, portanto, use algum tipo de tipo decimal e faça com que a precisão seja pelo menos 2 maior que o número que você armazenará para realizar cálculos matemáticos. Eu não sei sobre os meus tipos de dados sql, mas no SQL Server as pessoas costumam usar float ou real em vez de decimal e se metem em problemas porque esses são números estimados e não reais. Portanto, verifique se o tipo de dados que você usa é um tipo decimal verdadeiro e não um tipo decimal flutuante e você deve estar bem.
A FLOAT
deve fornecer toda a precisão necessária e ser melhor para as funções de comparação do que armazenar cada coordenada como uma string ou algo parecido.
Se sua versão do MySQL for anterior à 5.0.3, talvez você precise prestar atenção em certos erros de comparação de ponto flutuante .
Antes do MySQL 5.0.3, as colunas DECIMAL armazenam valores com precisão exata, porque são representados como strings, mas os cálculos nos valores DECIMAL são feitos usando operações de ponto flutuante. A partir da versão 5.0.3, o MySQL executa operações DECIMAL com uma precisão de 64 dígitos decimais, o que deve resolver os problemas de imprecisão mais comuns quando se trata de colunas DECIMAL
DECIMAL
havia (antes da versão 5.0.3) certos erros devido ao uso da implementação flutuante.