Estoy rediseñando una base de datos de clientes y una de las nuevas piezas de información que me gustaría almacenar junto con los campos de dirección estándar (calle, ciudad, etc.) es la ubicación geográfica de la dirección. El único caso de uso que tengo en mente es permitir que los usuarios mapeen las coordenadas en los mapas de Google cuando la dirección no se puede encontrar de otra manera, lo que a menudo ocurre cuando el área se desarrolla recientemente o se encuentra en una ubicación remota / rural.
Mi primera inclinación fue almacenar la latitud y la longitud como valores decimales, pero luego recordé que SQL Server 2008 R2 tiene un geography
tipo de datos. No tengo absolutamente ninguna experiencia en el uso geography
, y desde mi investigación inicial, parece exagerado para mi escenario.
Por ejemplo, para trabajar con la latitud y la longitud almacenadas como decimal(7,4)
, puedo hacer esto:
insert into Geotest(Latitude, Longitude) values (47.6475, -122.1393)
select Latitude, Longitude from Geotest
pero con geography
, haría esto:
insert into Geotest(Geolocation) values (geography::Point(47.6475, -122.1393, 4326))
select Geolocation.Lat, Geolocation.Long from Geotest
Aunque no es que mucho más complicado, ¿por qué añadir complejidad si yo no tengo que?
Antes de abandonar la idea de usar geography
, ¿hay algo que deba considerar? ¿Sería más rápido buscar una ubicación utilizando un índice espacial en lugar de indexar los campos de latitud y longitud? ¿Hay ventajas de usar de las geography
que no tenga conocimiento? O, por otro lado, ¿hay advertencias que debería conocer y que me desanimarían a consumir geography
?
Actualizar
@Erik Philips mencionó la capacidad de realizar búsquedas de proximidad con geography
, lo cual es muy bueno.
Por otro lado, una prueba rápida muestra que un simple select
obtener la latitud y la longitud es significativamente más lento cuando se usa geography
(detalles a continuación). , y un comentario sobre la respuesta aceptada a otra pregunta SO geography
me tiene receloso:
@SaphuA De nada. Como nota al margen, tenga MUCHO cuidado al usar un índice espacial en una columna de tipo de datos GEOGRAPHY anulable. Hay algunos problemas graves de rendimiento, por lo que debe hacer que la columna GEOGRAPHY no acepte nulos incluso si tiene que remodelar su esquema. - Tomas 18 de junio a las 11:18
Con todo, al sopesar la probabilidad de realizar búsquedas de proximidad frente a la compensación en rendimiento y complejidad, he decidido renunciar al uso de geography
en este caso.
Detalles de la prueba que realicé:
Creé dos tablas, una usando geography
y otra usando decimal(9,6)
para latitud y longitud:
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 insertó una sola fila usando los mismos valores de latitud y longitud en cada tabla:
insert into GeographyTest(Location) values (geography::Point(47.6475, -122.1393, 4326))
insert into LatLongTest(Latitude, Longitude) values (47.6475, -122.1393)
Finalmente, ejecutar el siguiente código muestra que, en mi máquina, seleccionar la latitud y la longitud es aproximadamente 5 veces más lento cuando se usa 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
Lo que fue más sorprendente es que incluso cuando no se seleccionan filas, por ejemplo, seleccionar dónde RowId = 2
, que no existe, geography
fue aún más lento:
GeographyDuration LatLongDuration
----------------- ---------------
1607 948
1610 946
1607 947
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
1608 947