【问题标题】:Get the nearest longitude and latitude from MSSQL database table?从 MSSQL 数据库表中获取最近的经度和纬度?
【发布时间】:2013-07-07 05:35:03
【问题描述】:

好的,我已经搜索了 SO 和 Google,但还没有真正找到明确的答案,所以把它扔给 SO 社区。​​p>

基本上我有一个特定兴趣点的经度和纬度表,sql查询将知道您当前所在的位置(这将是表中的位置之一,作为参数传入),因此它然后需要计算并返回最接近传入的经纬度的那一行。

我要求这一切都在 MSSQL(2012 / 存储过程)中完成,而不是在调用应用程序(即 .NET)中完成,因为我听说 SQL 在处理此类查询时通常比 .NET 快得多?

编辑:

我找到了 STDistance 函数,它给出了位置之间的英里数,例如:

DECLARE @NWI geography, @EDI geography
SET @NWI = geography::Point(52.675833,1.282778,4326)
SET @EDI = geography::Point(55.95,-3.3725,4326)
SELECT @NWI.STDistance(@EDI) / 1000

但是我不想遍历表中的所有纬度/经度,因为这肯定会影响性能?

我还尝试转换以下评论链接之一中指出的示例之一(这是 MYSQL 而不是 MSSQL)但是我收到错误,代码如下:

DECLARE @orig_lat decimal(6,2), @orig_long decimal(6,2), @bounding_distance int
set @orig_lat=52.056736; 
set @orig_long=1.14822; 
set @bounding_distance=1;

SELECT *,((ACOS(SIN(@orig_lat * PI() / 180) * SIN('lat' * PI() / 180) + COS(@orig_lat * PI() / 180) * COS('lat' * PI() / 180) * COS((@orig_long - 'lon') * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS 'distance' 
FROM [DB1].[dbo].[LatLons]
WHERE
(
  'lat' BETWEEN (@orig_lat - @bounding_distance) AND (@orig_lat + @bounding_distance)
  AND 'lon' BETWEEN (@orig_long - @bounding_distance) AND (@orig_long + @bounding_distance)
)
ORDER BY 'distance' ASC

收到的错误是:

消息 8114,级别 16,状态 5,行 6 转换数据类型 varchar 时出错 转为数字。

任何人能够解决上述代码或提出更好的解决方案?

【问题讨论】:

  • 这可能就是你要找的stackoverflow.com/questions/592209/…
  • 看来你的答案在这里stackoverflow.com/questions/5826430/…
  • 如果建议的主题对您没有帮助,请发布您的示例数据。
  • 大家好,感谢您的建议,但是这两个链接似乎都没有明确给出我正在寻找的答案,如果它有帮助的话,我已经稍微扩展了我的问题?
  • 在你的例子中,距离是英里还是公里?

标签: sql sql-server database tsql sql-server-2012


【解决方案1】:

几年前我写了一篇博客,解释了如何在不使用空间数据类型的情况下做到这一点。由于您似乎有一张经度/纬度值表,因此此博客可能会有所帮助。

SQL Server Zipcode Latitude Longitude Proximity Search

Same page saved from Archive.org

【讨论】:

  • @G Mastros 你的博文简直是天才!通过一个非常小的调整,我能够让它与我的数据一起工作,我必须承认它的性能是传奇的,我衷心感谢帮助解决我的问题:-)
【解决方案2】:

让我们看一个在 SQL Server 2008(及更高版本)中使用 STDistance 函数的简单示例。

我要告诉 SQL Server 我在伦敦,我想看看我的每个办公室离我有多远。这是我希望 SQL Server 给我的结果:

首先,我们需要一些示例数据。我们将创建一个包含一些 Microsoft 办事处位置的表,并将它们的经度和纬度值存储在 geography 字段中。

CREATE TABLE [Offices] (
    [Office_Id] [int] IDENTITY(1, 1) NOT NULL,
    [Office_Name] [nvarchar](200) NOT NULL,
    [Office_Location] [geography] NOT NULL,
    [Update_By] nvarchar(30) NULL,
    [Update_Time] [datetime]
) ON [PRIMARY]

GO

INSERT INTO [dbo].[Offices] VALUES ('Microsoft Zurich', 'POINT(8.590847 47.408860 )', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft San Francisco', 'POINT(-122.403697 37.792062 )', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Paris', 'POINT(2.265509 48.833946)', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Sydney', 'POINT(151.138378 -33.796572)', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Dubai', 'POINT(55.286282 25.228850)', 'mike', GetDate())

现在,假设我们在伦敦。以下是如何根据伦敦的经度和纬度值生成 geography 值:

DECLARE 
    @latitude numeric(12, 7),
    @longitude numeric(12, 7)

SET @latitude = 51.507351
SET @longitude = -0.127758

DECLARE @g geography = 'POINT(' + cast(@longitude as nvarchar) + ' ' + cast(@latitude as nvarchar) + ')';

最后,让我们看看我们每个办公室的距离。

SELECT [Office_Name], 
       cast([Office_Location].STDistance(@g) / 1609.344 as numeric(10, 1)) as 'Distance (in miles)' 
FROM [Offices]
ORDER BY 2 ASC

这给了我们希望的结果。

显然,如果您只是想看看最近的办公室,您可以输入TOP(1)

酷,嘿?

只有一个障碍。当您有很多 geography 点可供比较时,即使您在该数据库字段上添加 SPATIAL INDEX,性能也不会出色。

我在一个包含 330,000 个geography 点的表格中测试了一个点。使用此处显示的代码,它在大约 8 秒 内找到了最近的点。

当我修改我的表来存储经度和纬度值,并使用[dbo].[fnCalcDistanceMiles] 函数from this StackOverflow article 时,它在大约3 秒 内找到了最近的点。

不过……

我在互联网上找到的所有“两点之间的距离”示例要么使用 SQL Server STDistance 函数,要么使用涉及(CPU 密集型)cos、sin 和 tan 函数的数学公式。

一个更快的解决方案是回到高中,记住毕达哥拉斯是如何计算两点之间的距离的。

假设我们想知道伦敦和巴黎之间的距离。

这是我的 SQL Server 函数:

CREATE FUNCTION [dbo].[uf_CalculateDistance] (@Lat1 decimal(8,4), @Long1 decimal(8,4), @Lat2 decimal(8,4), @Long2 decimal(8,4))
RETURNS decimal (8,4) AS
BEGIN
    DECLARE @d decimal(28,10)

    SET @d = sqrt(square(@Lat1-@Lat2) + square(@Long1-@Long2))

    RETURN @d
END

现在,请记住,此函数不会返回以英里、公里等为单位的值...它只是比较经度和纬度值。并且毕达哥拉斯是用于 2D 的,而不是比较圆形星球上的点!

但是,在我的测试中,它在 1 秒 内找到了最近的点,并产生了与使用 SQL Server 的 STDistance 函数相同的结果。

因此,请随意使用此函数来比较相对距离,但如果您需要实际距离本身,请不要使用此函数。

希望这一切对您有所帮助。

【讨论】:

  • 在你的“毕达哥拉斯”函数中,使用 abs 是多余的......例如square(-3) 产生 9
  • 如果你只想找到最近的,你甚至不需要sqrt
【解决方案3】:

我是如何使用上面 Mike 的函数的:

    SELECT TOP 1 [State], [City], [AddressID], sqrt(square(abs(la.Latitude-@lat)) + square(abs(la.Longitude-@lng))) as Distance
    FROM [LocationAddresses] la     
    ORDER BY Distance

【讨论】:

    【解决方案4】:

    在我的表格中,我有 5 个字段:[名称]、[时间]、[描述]、[纬度]、[经度]。在这里,用户可以传递他/她想要结果的当前位置和距离。

    declare @latitude float, @longitude float, @distance float 
    select @latitude = 28.6493, @longitude = 77.2217 , @distance = 5
    ;with RestroomLocationsWithDistance as
        (
            select
            [Name]   ,[Timings]     ,[Description],   
            ( 3959 * acos( cos( radians(@latitude) ) * cos( radians( [Lattitude] ) ) * cos( radians( [Longitude] )
           - radians(@longitude) ) + sin( radians(@latitude) ) * sin( radians( [Lattitude] ) ) ) )  As Distance            
            FROM [dbo].[Locations]
        )
        select
        [Name]   ,[Timings]     ,[Description],    Distance        
        from RestroomLocationsWithDistance
        where Distance <= @distance
    

    【讨论】:

    • 距离是英里还是公里?
    【解决方案5】:

    由于这是 SQL Server,只需在表的 Lat/Lon 列上放置一个空间索引,然后在 WHERE 子句中使用 .STDistance() 函数对其进行查询。

    Microsoft 文档中的这篇文章解释了如何执行此操作。:http://msdn.microsoft.com/en-us/library/ff929109.aspx。第二部分“Nearest Neighbor Query..”似乎特别相关。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-04
      • 1970-01-01
      • 2016-11-22
      • 1970-01-01
      • 2012-12-06
      • 1970-01-01
      相关资源
      最近更新 更多