【发布时间】:2011-11-16 13:36:08
【问题描述】:
我正在重新设计一个客户数据库,我想与标准地址字段(街道、城市等)一起存储的新信息之一是地址的地理位置。我想到的唯一用例是允许用户在无法找到地址时在谷歌地图上绘制坐标,这通常发生在该地区是新开发的地区或位于偏远/农村地区时。
我的第一个想法是将纬度和经度存储为十进制值,但后来我记得 SQL Server 2008 R2 有一个 geography 数据类型。我完全没有使用 geography 的经验,而且从我最初的研究来看,这对于我的场景来说似乎是矫枉过正。
例如,要使用存储为decimal(7,4) 的纬度和经度,我可以这样做:
insert into Geotest(Latitude, Longitude) values (47.6475, -122.1393)
select Latitude, Longitude from Geotest
但是对于geography,我会这样做:
insert into Geotest(Geolocation) values (geography::Point(47.6475, -122.1393, 4326))
select Geolocation.Lat, Geolocation.Long from Geotest
虽然它并没有那么复杂得多,但如果我不需要,为什么还要增加复杂性呢?
在我放弃使用geography 的想法之前,有什么我应该考虑的吗?使用空间索引搜索位置是否比索引纬度和经度字段更快?使用geography 是否有我不知道的优势?或者,另一方面,我应该知道哪些警告会阻止我使用geography?
更新
@Erik Philips 提出了使用geography 进行邻近搜索的功能,这非常酷。
另一方面,一项快速测试表明,使用geography 时,简单的select 获取纬度和经度会明显变慢(详情如下)。 ,以及对accepted answer 对geography 上的另一个SO 问题的评论让我持怀疑态度:
@SaphuA 不客气。作为旁注要非常小心使用 可为空的 GEOGRAPHY 数据类型列上的空间索引。有一些 严重的性能问题,因此使 GEOGRAPHY 列不可为空 即使您必须改造您的架构。 – 托马斯 6 月 18 日 11:18
总而言之,在权衡进行邻近搜索的可能性与性能和复杂性的权衡后,我决定在这种情况下放弃使用 geography。
我运行的测试的详细信息:
我创建了两张表,一张使用geography,另一张使用decimal(9,6) 表示经度和纬度:
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)
)
并在每个表中插入使用相同纬度和经度值的单行:
insert into GeographyTest(Location) values (geography::Point(47.6475, -122.1393, 4326))
insert into LatLongTest(Latitude, Longitude) values (47.6475, -122.1393)
最后,运行以下代码显示,在我的机器上,使用geography时选择经纬度大约慢了5倍。
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
结果:
GeographyDuration LatLongDuration
----------------- ---------------
5146 1020
5143 1016
5169 1030
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
5152 1022
更令人惊讶的是,即使没有选择行,例如选择不存在的RowId = 2 的位置,geography 仍然较慢:
GeographyDuration LatLongDuration
----------------- ---------------
1607 948
1610 946
1607 947
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
1608 947
【问题讨论】:
-
我正在考虑两者都做,将 Lat 和 Lon 保存在它们自己的列中,并为 Geography 对象设置另一列,所以如果我只需要 Lat/Lon 我从列中获取它们,如果我需要邻近搜索,我将使用 Geography。这是明智的吗?有什么缺点吗(除了需要更多空间……)?
-
@YuvalA。这听起来当然是合理的,并且可能是一个很好的折衷方案。我唯一担心的是表中的 Geography 列是否会对针对表的查询产生任何影响 - 我没有这方面的经验,因此您需要进行测试以验证。
-
您为什么一直用新问题更新您的问题,而不是提出新问题?
-
@Chad 不确定您的意思。我更新了一次问题的正文,不是为了问更多问题。
-
现在,对于那些发现这个问题的人来说,值得注意的是,SQL Server 2012 包括空间索引的显着性能提升。另外值得注意的是,只要您存储位置信息,您就可以稍后使用查找服务添加空间信息,以对您已经存储的地址进行地理编码。
标签: sql-server-2008 geolocation geocoding