Qual é a melhor maneira de armazenar coordenadas (longitude / latitude, do Google Maps) no SQL Server?

103

Estou projetando uma tabela no SQL Server 2008 que armazenará uma lista de usuários e uma coordenada do Google Maps (longitude e latitude).

Vou precisar de dois campos ou posso fazer com 1?

Qual é o melhor (ou mais comum) tipo de dados a ser usado para armazenar esse tipo de dados?

Jonathan
fonte

Respostas:

63

Aviso justo! Antes de seguir o conselho para usar o tipo GEOGRAPHY, certifique-se de que não está planejando usar o Linq ou Entity Framework para acessar os dados porque não é compatível (desde novembro de 2010) e você ficará triste!

Atualização em julho de 2017

Para aqueles que estão lendo esta resposta agora, ela é obsoleta no que se refere à pilha de tecnologia retroativa. Veja comentários para mais detalhes.

dan90266
fonte
1
Como a resposta original foi postada, encontrei este artigo jasonfollas.com/blog/archive/2010/02/14/… discutindo uma possível solução alternativa.
Norman H
56
Aviso não é mais válido. EF agora oferece suporte a tipos de geografia.
Marcelo Mason
1
Nerveless EF oferece suporte a tipos espaciais, ele usa tipos diferentes definidos para serviços de dados WCF, portanto, não são compatíveis
abatishchev
1
o hibernate 5 espacial ainda não, por exemplo :(
Eugene
1
Fair Warning ainda se aplica ao Entity Framework Core 2.0 github.com/aspnet/EntityFrameworkCore/issues/1100
ono2012
29

Não sei a resposta para o SQL Server, mas ...

No MySQL, salve-o comoFLOAT( 10, 6 )

Esta é a recomendação oficial da documentação do desenvolvedor do Google .

CREATE TABLE `coords` (
  `lat` FLOAT( 10, 6 ) NOT NULL ,
  `lng` FLOAT( 10, 6 ) NOT NULL ,
) ENGINE = MYISAM ;
powtac
fonte
19
A pergunta afirma claramente o SQL Server, não o MySQL. E você certamente não iria querer uma mesa apenas com latitude e longitude sozinhas.
araqnid
2
Concordo - má resposta. Use o novo tipo espacial GEOGRAPHY.
Pure.Krome de
2
O link também mudou para code.google.com/apis/maps/articles/phpsqlajax.html
Ralph Lavelle
14
Eu não usaria float por causa de problemas de precisão. Use decimal (9,6).
kaptan
Há casos reais em que onde late lngsuperam georgraphy, mesmo com índices de alta densidade no SQL 2014. Por exemplo: localizar todos os pontos está dentro de um retângulo. Só não tenho certeza, vejo que o Google Maps agora usa 7 em vez de 6 dígitos.
Nenad
22

A maneira como eu faço isso: eu armazeno a latitude e a longitude e, em seguida, tenho uma terceira coluna que é um tipo de geografia derivado automaticamente das primeiras duas colunas. A tabela é parecida com esta:

CREATE TABLE [dbo].[Geopoint]
(
    [GeopointId] BIGINT NOT NULL PRIMARY KEY IDENTITY, 
    [Latitude] float NOT NULL, 
    [Longitude] float NOT NULL, 
    [ts] ROWVERSION NOT NULL, 
    [GeographyPoint]  AS ([geography]::STGeomFromText(((('POINT('+CONVERT([varchar](20),[Longitude]))+' ')+CONVERT([varchar](20),[Latitude]))+')',(4326))) 
)

Isso dá a você a flexibilidade de consultas espaciais na coluna geoPoint e você também pode recuperar os valores de latitude e longitude conforme necessário para exibição ou extração para fins de csv.

Jaxxbo
fonte
ótimo para o que eu estava procurando, você tem algo para faixas / linhas
aggie
Outra abordagem que também pode funcionar, dependendo do seu cenário, é armazenar o longo e o lat e, em seguida, apenas criar dinamicamente o objeto geográfico em tempo de execução.
Zapnologica
1
Ótima ideia, mas esteja ciente de que você não pode criar índices espaciais em colunas computadas se essa for a intenção de alguém.
hvaughan3 01 de
1
@ hvaughan3 Eu acho que você pode se você torná-la uma coluna computada persistente .
NickG
2
Obrigado e +1, sua resposta me ajudou. Mas acho que seria melhor usar em Pointvez de STGeomFromText. Por exemplo: [geography]::Point([Latitude], [Longitude], 4326).
default.kramer
21

Eu odeio ser contrária àqueles que dizem "aqui está um novo tipo, vamos usar". Os novos tipos espaciais do SQL Server 2008 têm alguns prós - a saber, eficiência, mas você não pode dizer cegamente sempre use esse tipo. Realmente depende de alguns problemas gerais.

Como exemplo, integração. Este tipo tem um tipo equivalente em .Net - mas e a interoperabilidade? Que tal oferecer suporte ou extensão de versões anteriores do .Net? Que tal expor esse tipo em toda a camada de serviço para outras plataformas? E quanto à normalização de dados - talvez você esteja interessado em informações de longa ou longa duração. Talvez você já tenha escrito uma lógica de negócios complexa para lidar com long / lat.

Não estou dizendo que você não deve usar o tipo espacial - em muitos casos, deve. Só estou dizendo que você deve fazer algumas perguntas mais críticas antes de seguir esse caminho. Para que eu responda à sua pergunta com mais precisão, preciso saber mais sobre sua situação específica.

Armazenar long / lat separadamente ou em um tipo espacial são soluções viáveis, e uma pode ser preferível à outra dependendo de suas próprias circunstâncias.

R. Lawson
fonte
O GIS e o processamento de dados espaciais têm uma longa história e representações textuais binárias padrão, pelo menos desde os anos 2000. Você acabará com todos os problemas que mencionou se não usar os tipos espaciais e as representações padrão
Panagiotis Kanavos
15

O que você deseja fazer é armazenar a Latitude e Longitude como o novo tipo Espacial SQL2008 -> GEOGRAFIA.

Aqui está uma captura de tela de uma mesa, que eu tenho.

texto alternativo http://img20.imageshack.us/img20/6839/zipcodetable.png

Nesta tabela, temos dois campos que armazenam dados geográficos.

  • Limite: este é o polígono que é o limite do CEP
  • CentrePoint: este é o ponto de Latitude / Longitude que representa o ponto médio visual deste polígono.

O principal motivo pelo qual você deseja salvá-lo no banco de dados como um tipo GEOGRAFIA é para que você possa aproveitar todos os métodos ESPACIAIS dele -> por exemplo. Ponto em Poly, Distância entre dois pontos, etc.

A propósito, também usamos a API do Google Maps para recuperar dados lat / long e armazená-los em nosso banco de dados Sql 2008 - portanto, esse método funciona.

Pure.Krome
fonte
1
E se você ainda não estiver em 2008 ou usar o SQLCE? Este último não suporta o tipo GEOGRAFIA ...
fretje
3
Se SqlCE ou <2008 oferecer suporte a binários, é possível armazenar os resultados em varbinary e, em seguida, usar a DLL da biblioteca de ferramentas espaciais para fazer cálculos espaciais em relação a esta representação de dados binários em seu código .NET. Não é a melhor solução, mas ainda é uma solução possível para alguns problemas. (nuget para sql espacial .. para pegar essa dll).
Pure.Krome
2
O link da imagem está quebrado
Bryan Denny
urgh :( obrigado por nada imageshack. Há anos não uso o IS :( ​​imgur.com até o fim!
Pure.Krome de
1
-1, esta resposta está incompleta sem a imagem. Considere substituí-la por uma nova imagem ou uma descrição textual de tabela ou excluir esta resposta.
Ilmari Karonen
11

O SQL Server oferece suporte para informações relacionadas ao espaço. Você pode ver mais em http://www.microsoft.com/sqlserver/2008/en/us/spatial-data.aspx .

Como alternativa, você pode armazenar as informações como dois campos básicos, geralmente um float é o tipo de dados padrão relatado pela maioria dos dispositivos e é preciso o suficiente para uma polegada ou duas - mais do que adequado para o Google Maps.

Rosstificado
fonte
2

OBSERVAÇÃO : Esta é uma resposta recente com base no servidor SQL recente, atualizações de pilha do .NET

latitude e longitude do Google Maps devem ser armazenadas como dados de ponto (observe P maiúsculo) no servidor SQL sob o tipo de dados geográficos.

Assumindo que seus dados atuais estão armazenados em uma tabela Samplecomo varchar em colunas late lon, a consulta abaixo irá ajudá-lo a converter para geografia

alter table Sample add latlong geography
go
update Sample set latlong= geography::Point(lat,lon,4326)
go

PS: Da próxima vez que você fizer uma seleção nesta tabela com dados geográficos, além da guia Resultados e Mensagens, você também obterá a guia Resultados espaciais como abaixo para visualização

Guia de resultados geográficos de SSMS

DhruvJoshi
fonte
0

Se você estiver usando o Entity Framework 5 <, você pode usar DbGeography. Exemplo do MSDN:

public class University  
{ 
    public int UniversityID { get; set; } 
    public string Name { get; set; } 
    public DbGeography Location { get; set; } 
}

public partial class UniversityContext : DbContext 
{ 
    public DbSet<University> Universities { get; set; } 
}

using (var context = new UniversityContext ()) 
{ 
    context.Universities.Add(new University() 
        { 
            Name = "Graphic Design Institute", 
            Location = DbGeography.FromText("POINT(-122.336106 47.605049)"), 
        }); 

    context. Universities.Add(new University() 
        { 
            Name = "School of Fine Art", 
            Location = DbGeography.FromText("POINT(-122.335197 47.646711)"), 
        }); 

    context.SaveChanges(); 

    var myLocation = DbGeography.FromText("POINT(-122.296623 47.640405)"); 

    var university = (from u in context.Universities 
                        orderby u.Location.Distance(myLocation) 
                        select u).FirstOrDefault(); 

    Console.WriteLine( 
        "The closest University to you is: {0}.", 
        university.Name); 
}

https://msdn.microsoft.com/en-us/library/hh859721(v=vs.113).aspx

Algo contra o qual eu lutava quando comecei a usar DbGeographyera o coordinateSystemId. Veja a resposta abaixo para uma excelente explicação e fonte para o código abaixo.

public class GeoHelper
{
    public const int SridGoogleMaps = 4326;
    public const int SridCustomMap = 3857;

    public static DbGeography FromLatLng(double lat, double lng)
    {
        return DbGeography.PointFromText(
            "POINT("
            + lng.ToString() + " "
            + lat.ToString() + ")",
            SridGoogleMaps);
    }
}

https://stackoverflow.com/a/25563269/3850405

Ogglas
fonte
-4

Se você for apenas substituí-lo por um URL, suponho que um campo serviria - para que você possa formar um URL como

http://maps.google.co.uk/maps?q=12.345678,12.345678&z=6

mas como são dois dados, eu os armazenaria em campos separados

Kristen
fonte
Este é o meu caso. Preciso armazenar as coordenadas em apenas um campo e separadas por uma vírgula. Acho que se poderia usar um TEXTO como um tipo de campo. O que você acha?
Amr
-10

Armazene ambos como flutuante e use palavras-chave exclusivas neles.i.em

create table coordinates(
coord_uid counter primary key,
latitude float,
longitude float,
constraint la_long unique(latitude, longitude)
);
Graviton
fonte
Para ter certeza de que há apenas 1 conjunto único de par de latitude e longitude. Você não quer armazenar coordenadas {0,0} duas vezes na sua mesa, não é?
Graviton
1
Você provavelmente não quer ter uma tabela de coordenadas separada como esta, especialmente com a restrição de exclusividade, é um pesadelo de manutenção lidar com o caso de dois locais se referirem ao mesmo ponto, sem mencionar a limpeza de linhas não referenciadas.
araqnid de
2
> Você provavelmente não quer ter uma tabela de coordenadas separada como esta - De jeito nenhum? Nunca? Como você armazenaria isso em 1 campo? E quanto aos milhões de pessoas que NÃO usam o tipo espacial do SQL 2008?
Sally de
2
Ter tal restrição é uma má decisão. Suponha que Bob more House Ae se mudará para House Ba casa onde Alice morava. Em breve Bob não poderá mais salvar seu endereço (local), porque Alice ainda não atualizou o dela - ou nunca o fará.
jweyrich
@Sally - não foi isso que ele disse. Leia seu comentário. Ele disse que não deveria haver razão para armazenar um par de valores em uma tabela separada . Apenas coloque a latitude / longitude na tabela original e salve a sobrecarga de uma segunda tabela e todos os JOINS.
NickG