c# - 如何返回邮政编码列表 ## 英里半径内的所有实例

标签 c# sql sql-server database tsql

SQL Server 2008 添加了一些很酷的新空间类型,并为 SQL 开发人员提供了很多更强大的空间数据处理方法,但我仍然不知道如何有效地返回,例如,仅返回 ## 英里半径内的位置一长串邮政编码(20 到 15000 个不同的邮政编码)。

有没有简单的方法来做到这一点?想到的唯一可能的解决方案似乎有点可怕,因为创建了 Cartiasian 乘积,因此计算量大得离谱......

如果有帮助,我擅长创建 CLR SP 和函数(我认为它会......)。

我不太关心如何找到 2 点(或地理类型)之间的距离,而是“给定位置是否在所提供列表中任何邮政编码(地理点)的 ## 英里范围内?”这里的复杂部分是要搜索的 zip 列表。

谢谢。

最佳答案

我必须实现地理定位搜索,经过大量研究后我决定使用 sql2008 地理。您需要一个填有经/纬度的邮政编码表。该表应如下所示:

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。然后我运行以下脚本将数据插入我的邮政编码表并创建空间索引。 (我必须添加自己的 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);

最后,我创建了一个接受纬度/经度并返回特定范围内的所有邮政编码的函数。因为它使用空间索引,所以速度非常快。

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

我知道这不是您要找的东西,但可以将其转换为您的目的。我发现使用邮政编码比城市更有效和准确,因为城市可以跨越许多邮政编码,因此返回给最终用户的数据是错误的。

这一切都非常以美国为中心,但可以很容易地转换为国际使用。我计划在未来的某个时候这样做,但还没有这个需要。

关于c# - 如何返回邮政编码列表 ## 英里半径内的所有实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3919878/

相关文章:

c# - 使用事件目录 API DirectoryEntry 进行身份验证

c# - 具有 .NET core 和 json 验证的 WebApi

sql - 如何查找一年中所有月份的第一个日期

SQL 子查询替代 INTERSECT

sql - 插入参数值和 JSON 字符串值

sql-server - 源代码控制分支策略

c# - 覆盖 C# 的虚方法 - 为什么这不会导致无限递归?

php - 更新mysql查询报错

SQL 比较集,第二部分 : How to join sets of sets

c# - 如何访问用户控件中父页面的隐藏字段