Estou redesenhando um banco de dados de clientes e uma das novas informações que gostaria de armazenar junto com os campos de endereço padrão (rua, cidade etc.) é a localização geográfica do endereço. O único caso de uso que tenho em mente é permitir que os usuários mapeiem as coordenadas nos mapas do Google quando o endereço não puder ser encontrado de outra forma, o que geralmente acontece quando a área é desenvolvida recentemente ou está em uma localização remota / rural.
Minha primeira inclinação foi armazenar latitude e longitude como valores decimais, mas então me lembrei que o SQL Server 2008 R2 tem um geography
tipo de dados. Não tenho absolutamente nenhuma experiência com o uso geography
e, pela minha pesquisa inicial, parece um exagero para o meu cenário.
Por exemplo, para trabalhar com latitude e longitude armazenadas como decimal(7,4)
, posso fazer o seguinte:
insert into Geotest(Latitude, Longitude) values (47.6475, -122.1393)
select Latitude, Longitude from Geotest
mas com geography
, eu faria isso:
insert into Geotest(Geolocation) values (geography::Point(47.6475, -122.1393, 4326))
select Geolocation.Lat, Geolocation.Long from Geotest
Embora não seja que muito mais complicado, porque a complexidade add se eu não tem que?
Antes de abandonar a ideia de usar geography
, há algo que devo considerar? Seria mais rápido pesquisar um local usando um índice espacial em vez de indexar os campos Latitude e Longitude? Há vantagens em usar geography
que eu não conheço? Ou, por outro lado, há ressalvas que devo saber que me desencorajariam a usargeography
?
Atualizar
@Erik Philips trouxe a capacidade de fazer pesquisas de proximidade com geography
, o que é muito legal.
Por outro lado, um teste rápido está mostrando que uma forma simples select
de obter a latitude e a longitude é significativamente mais lenta ao usar geography
(detalhes abaixo). , e um comentário sobre a resposta aceita a outra pergunta do SO geography
me deixou desconfiado:
@SaphuA De nada. Como nota lateral, tenha MUITO cuidado ao usar um índice espacial em uma coluna de tipo de dados GEOGRAPHY anulável. Há alguns problemas sérios de desempenho, portanto, torne essa coluna GEOGRAPHY não anulável, mesmo se você precisar remodelar seu esquema. - Tomas 18 de junho às 11h18
Ao todo, pesando a probabilidade de fazer pesquisas de proximidade versus a compensação em desempenho e complexidade, decidi renunciar ao uso de geography
neste caso.
Detalhes do teste que fiz:
Criei duas tabelas, uma usando geography
e outra usando decimal(9,6)
para latitude e longitude:
CREATE TABLE [dbo].[GeographyTest]
(
[RowId] [int] IDENTITY(1,1) NOT NULL,
[Location] [geography] NOT NULL,
CONSTRAINT [PK_GeographyTest] PRIMARY KEY CLUSTERED ( [RowId] ASC )
)
CREATE TABLE [dbo].[LatLongTest]
(
[RowId] [int] IDENTITY(1,1) NOT NULL,
[Latitude] [decimal](9, 6) NULL,
[Longitude] [decimal](9, 6) NULL,
CONSTRAINT [PK_LatLongTest] PRIMARY KEY CLUSTERED ([RowId] ASC)
)
e inseriu uma única linha usando os mesmos valores de latitude e longitude em cada tabela:
insert into GeographyTest(Location) values (geography::Point(47.6475, -122.1393, 4326))
insert into LatLongTest(Latitude, Longitude) values (47.6475, -122.1393)
Finalmente, rodar o código a seguir mostra que, na minha máquina, selecionar a latitude e longitude é aproximadamente 5 vezes mais lento durante o uso geography
.
declare @lat float, @long float,
@d datetime2, @repCount int, @trialCount int,
@geographyDuration int, @latlongDuration int,
@trials int = 3, @reps int = 100000
create table #results
(
GeographyDuration int,
LatLongDuration int
)
set @trialCount = 0
while @trialCount < @trials
begin
set @repCount = 0
set @d = sysdatetime()
while @repCount < @reps
begin
select @lat = Location.Lat, @long = Location.Long from GeographyTest where RowId = 1
set @repCount = @repCount + 1
end
set @geographyDuration = datediff(ms, @d, sysdatetime())
set @repCount = 0
set @d = sysdatetime()
while @repCount < @reps
begin
select @lat = Latitude, @long = Longitude from LatLongTest where RowId = 1
set @repCount = @repCount + 1
end
set @latlongDuration = datediff(ms, @d, sysdatetime())
insert into #results values(@geographyDuration, @latlongDuration)
set @trialCount = @trialCount + 1
end
select *
from #results
select avg(GeographyDuration) as AvgGeographyDuration, avg(LatLongDuration) as AvgLatLongDuration
from #results
drop table #results
Resultados:
GeographyDuration LatLongDuration
----------------- ---------------
5146 1020
5143 1016
5169 1030
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
5152 1022
O que foi mais surpreendente é que mesmo quando nenhuma linha foi selecionada, por exemplo, selecionar onde RowId = 2
, que não existe, geography
ainda era mais lento:
GeographyDuration LatLongDuration
----------------- ---------------
1607 948
1610 946
1607 947
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
1608 947