SQL Server 2008添加了一些很酷的新Spacial类型,并为SQL开发人员提供了更强大的操作空间数据的方法,但它仍然无法让我如何有效地返回,例如,只有位于##英里半径范围内的位置一长串拉链(20到15,000个不同的拉链码)。
有一种简单的方法吗?想到的唯一可能的解决方案似乎有些可怕,因为产生了cartiasian产品,因此计算得非常多......
我擅长创建CLR SP和函数,如果这有帮助(我认为它会......)。
我不太关心如何找到2点(或地理类型)之间的距离,而不是“是提供列表中任何一个zipcodes(地理点)的##里程内的给定位置?”这里的复杂部分是要搜索的拉链列表。
感谢。
答案 0 :(得分:4)
我必须实施地理位置搜索,经过大量研究后我决定使用sql2008地理位置。您需要一个填充了lat / long的邮政编码表。该表应如下所示:
CREATE TABLE [dbo].[PostalCodes](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[StateID] [bigint] NOT NULL,
[PostalCode] [varchar](10) NOT NULL,
[Latitude] [decimal](16, 12) NULL,
[Longitude] [decimal](16, 12) NULL,
[GeographyLocation] [geography] NULL,
[CreatedOn] [datetime] NOT NULL,
[LastUpdated] [datetime] NOT NULL,
[GeographyLocation_temp] [varchar](100) NULL,
CONSTRAINT [PK_PostalCode] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
我从GeoNames.org下载了一个国际邮政编码列表,并将其导入为tmp_GeoNames。然后我运行以下脚本将数据插入到我的zipcode表中并创建空间索引。 (我必须添加自己的StateID列并填充它,但您可以跳过该部分并将其从脚本中删除。)
INSERT INTO PostalCodes
(StateID, PostalCode, Latitude, Longitude)
SELECT DISTINCT StateID, PostalCode, Latitude, Longitude FROM temp_GeoNames where stateID is not null
UPDATE PostalCodes
SET GeographyLocation_temp= 'POINT(' + CONVERT(VARCHAR(100),longitude)
+' ' + CONVERT(VARCHAR(100),latitude) +')'
UPDATE PostalCodes
SET GeographyLocation = geography::STGeomFromText(GeographyLocation_temp,4326)
CREATE SPATIAL INDEX SIndx_SpatialTable_geography_col1
ON PostalCodes(GeographyLocation);
最后,我创建了一个接受lat / long的函数,并返回特定范围内的所有zipcodes。因为它使用空间索引,所以它非常快。
CREATE FUNCTION [dbo].[PostalCode_SelectNearest]
(
@Latitude [decimal](16, 12)
,@Longitude [decimal](16, 12)
,@RangeInMiles int
)
RETURNS @PostalCodes Table (PostalCode varchar(10) PRIMARY KEY NOT NULL, DistanceInMiles FLOAT NULL)
AS
BEGIN
--Create geography point based on Lat/Long passed ... careful, the values passed are reversed from normal thinking
DECLARE @g geography;
SET @g = geography::STGeomFromText('POINT(' +
CONVERT(varchar,@Longitude) + ' ' +
CONVERT(varchar,@Latitude) + ')', 4326);
--Select the nearest Postal Codes
INSERT INTO @PostalCodes (PostalCode, DistanceInMiles)
SELECT PostalCode, GeographyLocation.STDistance(@g)/1609.344 as DistanceInMiles
FROM PostalCodes
WHERE GeographyLocation.STDistance(@g)<=(@RangeInMiles * 1609.344)
RETURN;
END
我意识到这不是你想要的,但它可以转换成你的目的。我发现使用zipcodes比城市更有效和准确,因为城市可以跨越许多邮政编码,因此返回对最终用户来说错误的数据。
这一切都以美国为中心,但很容易转换为国际使用。我打算在将来的某个时候这样做,但还没有必要。
答案 1 :(得分:1)
还要考虑这需要多么精确......对于小半径,(不需要大圆数学),只需将一个方形内的所有位置放在一边多英里即可。如果你有每个邮政编码的纬度和经度,只需一个过滤器即可完成,无需任何计算。并且返回的行数将被关闭(它将太多)仅为因子1 - pi / 4,仅为约21%
foreach给定位置(Tgt lat / long)
- 假设半径为海里(6080英尺),
纬度和经度以总分钟为单位测量
(即30度,10分钟= 1810分钟)
然后:
Select * From theTable
Where Latitude Between TgtLat - radius
And TgtLat + radius
And Longitude Between TgtLong - radius/Cos(TgtLat)
And TgtLong + radius/Cos(TgtLat)
答案 2 :(得分:0)
我确实有一个sql函数可以为你执行那些实际上并不那么慢的可怕计算。但是这里有一个链接,以及如何使用sql 2008中的新功能执行查询:http://msdn.microsoft.com/en-us/magazine/dd434647.aspx
编辑:更多链接:
http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/sql-server-2008-proximity-search-with-th
答案 3 :(得分:0)
查看GeoNames Webservice。我用过这个并且效果很好。
答案 4 :(得分:0)
我已经完成了与Oracle Spatial非常相似的事情,所以我不熟悉MSSQL的空间特性,因此我的回答很简单:
我将假设您拥有表示包含每个邮政编码的多边形的数据,您所要做的就是获取邮政编码列表,合并他们的多边形,然后询问所有记录组合多边形或其边缘的 x 英里。某些空间包具有“多种”类型,允许您在非连续区域(在您的情况下为非相邻邮政编码)中进行组合和操作。
如果你拥有的只是邮政编码的中心点,你可以做同样的事情:组合点并在 x 里面寻找任何东西。这里的缺陷是一些邮政编码可能非常大,你会丢失一些符合你标准但不在中心点的 x 英里范围内的记录。
听起来很毛茸茸,但用于空间数据的索引系统非常有效。
HTH。
答案 5 :(得分:0)
无论您决定使用哪种解决方案,您都需要一个邮政编码数据库。这是one,下载并将其导入表格。